From b30edf562c19bc76d83fd5c305389ae841482e85 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 2 Feb 2026 15:48:01 +0000 Subject: [PATCH] deploy: a34c88ca756d6cf03a4b900859f60c62338f12d0 --- pr/395/docs/.lock | 0 pr/395/docs/bench/all.html | 1 + pr/395/docs/bench/enum.CipherSuite.html | 35 + .../fn.configure_tracing_subscriber.html | 1 + pr/395/docs/bench/fn.connect_client.html | 6 + pr/395/docs/bench/fn.drain_stream.html | 4 + pr/395/docs/bench/fn.rt.html | 1 + pr/395/docs/bench/fn.send_data_on_stream.html | 4 + pr/395/docs/bench/fn.server_endpoint.html | 7 + pr/395/docs/bench/fn.transport_config.html | 1 + pr/395/docs/bench/index.html | 1 + pr/395/docs/bench/sidebar-items.js | 1 + .../docs/bench/stats/fn.throughput_bps.html | 1 + pr/395/docs/bench/stats/index.html | 1 + pr/395/docs/bench/stats/sidebar-items.js | 1 + pr/395/docs/bench/stats/struct.Stats.html | 29 + .../docs/bench/stats/struct.StreamStats.html | 27 + .../bench/stats/struct.TransferResult.html | 28 + pr/395/docs/bench/struct.Opt.html | 68 + pr/395/docs/bulk/all.html | 1 + pr/395/docs/bulk/fn.client.html | 5 + pr/395/docs/bulk/fn.handle_client_stream.html | 5 + pr/395/docs/bulk/fn.main.html | 1 + pr/395/docs/bulk/fn.server.html | 1 + pr/395/docs/bulk/index.html | 1 + pr/395/docs/bulk/sidebar-items.js | 1 + pr/395/docs/bulk/struct.ClientStats.html | 27 + pr/395/docs/certificate/all.html | 1 + .../docs/certificate/fn.configure_client.html | 1 + .../fn.generate_self_signed_cert.html | 1 + pr/395/docs/certificate/fn.main.html | 1 + .../certificate/fn.read_certs_from_file.html | 1 + pr/395/docs/certificate/index.html | 1 + pr/395/docs/certificate/sidebar-items.js | 1 + .../struct.SkipServerVerification.html | 44 + pr/395/docs/crates.js | 2 + pr/395/docs/data_transfer/all.html | 1 + pr/395/docs/data_transfer/fn.main.html | 1 + .../fn.open_bidirectional_stream.html | 3 + .../fn.open_unidirectional_stream.html | 3 + .../fn.receive_bidirectional_stream.html | 3 + .../data_transfer/fn.receive_datagram.html | 1 + .../fn.receive_unidirectional_stream.html | 3 + .../data_transfer/fn.send_unreliable.html | 1 + pr/395/docs/data_transfer/index.html | 1 + pr/395/docs/data_transfer/sidebar-items.js | 1 + pr/395/docs/help.html | 1 + pr/395/docs/iroh_quinn/all.html | 1 + pr/395/docs/iroh_quinn/congestion/index.html | 2 + .../iroh_quinn/congestion/sidebar-items.js | 1 + .../iroh_quinn/congestion/struct.Bbr.html | 52 + .../congestion/struct.BbrConfig.html | 33 + .../congestion/struct.ControllerMetrics.html | 32 + .../iroh_quinn/congestion/struct.Cubic.html | 48 + .../congestion/struct.CubicConfig.html | 33 + .../iroh_quinn/congestion/struct.NewReno.html | 52 + .../congestion/struct.NewRenoConfig.html | 34 + .../congestion/trait.Controller.html | 79 + .../congestion/trait.ControllerFactory.html | 10 + .../connection/enum.SendDatagramError.html | 11 + .../connection/struct.AcceptBi.html | 11 + .../connection/struct.AcceptUni.html | 11 + .../connection/struct.Connecting.html | 11 + .../connection/struct.Connection.html | 11 + .../connection/struct.OnClosed.html | 11 + .../iroh_quinn/connection/struct.OpenBi.html | 11 + .../iroh_quinn/connection/struct.OpenUni.html | 11 + .../connection/struct.ReadDatagram.html | 11 + .../connection/struct.SendDatagram.html | 11 + .../struct.WeakConnectionHandle.html | 11 + .../connection/struct.ZeroRttAccepted.html | 11 + pr/395/docs/iroh_quinn/crypto/index.html | 8 + .../iroh_quinn/crypto/rustls/enum.Error.html | 101 + .../docs/iroh_quinn/crypto/rustls/index.html | 2 + .../iroh_quinn/crypto/rustls/sidebar-items.js | 1 + .../crypto/rustls/struct.HandshakeData.html | 34 + .../rustls/struct.NoInitialCipherSuite.html | 31 + .../rustls/struct.QuicClientConfig.html | 52 + .../rustls/struct.QuicServerConfig.html | 56 + .../crypto/rustls/struct.TlsSession.html | 38 + .../docs/iroh_quinn/crypto/sidebar-items.js | 1 + .../iroh_quinn/crypto/struct.CryptoError.html | 25 + .../struct.ExportKeyingMaterialError.html | 31 + .../iroh_quinn/crypto/struct.KeyPair.html | 36 + .../docs/iroh_quinn/crypto/struct.Keys.html | 30 + .../crypto/struct.UnsupportedVersion.html | 25 + .../docs/iroh_quinn/crypto/trait.AeadKey.html | 32 + .../iroh_quinn/crypto/trait.ClientConfig.html | 16 + .../crypto/trait.HandshakeTokenKey.html | 6 + .../iroh_quinn/crypto/trait.HeaderKey.html | 10 + .../docs/iroh_quinn/crypto/trait.HmacKey.html | 10 + .../iroh_quinn/crypto/trait.PacketKey.html | 51 + .../iroh_quinn/crypto/trait.ServerConfig.html | 38 + .../docs/iroh_quinn/crypto/trait.Session.html | 68 + .../iroh_quinn/endpoint/struct.Accept.html | 11 + .../iroh_quinn/endpoint/struct.Endpoint.html | 11 + .../endpoint/struct.EndpointStats.html | 11 + pr/395/docs/iroh_quinn/enum.ConfigError.html | 36 + pr/395/docs/iroh_quinn/enum.ConnectError.html | 51 + .../docs/iroh_quinn/enum.ConnectionError.html | 54 + pr/395/docs/iroh_quinn/enum.Dir.html | 54 + pr/395/docs/iroh_quinn/enum.EcnCodepoint.html | 41 + pr/395/docs/iroh_quinn/enum.FrameType.html | 79 + pr/395/docs/iroh_quinn/enum.ReadError.html | 45 + .../docs/iroh_quinn/enum.ReadExactError.html | 38 + .../docs/iroh_quinn/enum.ReadToEndError.html | 38 + pr/395/docs/iroh_quinn/enum.ResetError.html | 40 + .../iroh_quinn/enum.SendDatagramError.html | 44 + pr/395/docs/iroh_quinn/enum.Side.html | 55 + pr/395/docs/iroh_quinn/enum.StoppedError.html | 40 + pr/395/docs/iroh_quinn/enum.WriteError.html | 45 + .../docs/iroh_quinn/fn.default_runtime.html | 5 + .../iroh_quinn/incoming/struct.Incoming.html | 11 + .../incoming/struct.IncomingFuture.html | 11 + .../incoming/struct.RetryError.html | 11 + pr/395/docs/iroh_quinn/index.html | 34 + .../path/struct.AddressDiscovery.html | 11 + .../iroh_quinn/path/struct.ClosePath.html | 11 + .../docs/iroh_quinn/path/struct.OpenPath.html | 11 + pr/395/docs/iroh_quinn/path/struct.Path.html | 11 + .../recv_stream/enum.ReadError.html | 11 + .../recv_stream/enum.ReadExactError.html | 11 + .../recv_stream/enum.ReadToEndError.html | 11 + .../recv_stream/enum.ResetError.html | 11 + .../recv_stream/struct.RecvStream.html | 11 + .../struct.UnorderedRecvStream.html | 11 + .../runtime/fn.default_runtime.html | 11 + .../runtime/smol/struct.SmolRuntime.html | 11 + .../runtime/tokio/struct.TokioRuntime.html | 11 + .../iroh_quinn/runtime/trait.AsyncTimer.html | 11 + .../runtime/trait.AsyncUdpSocket.html | 11 + .../iroh_quinn/runtime/trait.Runtime.html | 11 + .../iroh_quinn/runtime/trait.UdpSender.html | 11 + .../send_stream/enum.StoppedError.html | 11 + .../send_stream/enum.WriteError.html | 11 + .../send_stream/struct.SendStream.html | 11 + pr/395/docs/iroh_quinn/sidebar-items.js | 1 + pr/395/docs/iroh_quinn/struct.Accept.html | 50 + pr/395/docs/iroh_quinn/struct.AcceptBi.html | 54 + pr/395/docs/iroh_quinn/struct.AcceptUni.html | 54 + .../iroh_quinn/struct.AckFrequencyConfig.html | 65 + .../iroh_quinn/struct.AddressDiscovery.html | 173 + .../iroh_quinn/struct.ApplicationClose.html | 38 + .../docs/iroh_quinn/struct.BloomTokenLog.html | 48 + pr/395/docs/iroh_quinn/struct.Chunk.html | 35 + .../docs/iroh_quinn/struct.ClientConfig.html | 49 + pr/395/docs/iroh_quinn/struct.ClosePath.html | 53 + .../docs/iroh_quinn/struct.ClosedStream.html | 33 + pr/395/docs/iroh_quinn/struct.Connecting.html | 101 + pr/395/docs/iroh_quinn/struct.Connection.html | 216 + .../iroh_quinn/struct.ConnectionClose.html | 40 + .../docs/iroh_quinn/struct.ConnectionId.html | 2469 ++++++ .../iroh_quinn/struct.ConnectionStats.html | 36 + pr/395/docs/iroh_quinn/struct.Endpoint.html | 103 + .../iroh_quinn/struct.EndpointConfig.html | 67 + .../docs/iroh_quinn/struct.EndpointStats.html | 36 + pr/395/docs/iroh_quinn/struct.FrameStats.html | 65 + .../docs/iroh_quinn/struct.IdleTimeout.html | 52 + pr/395/docs/iroh_quinn/struct.Incoming.html | 46 + .../iroh_quinn/struct.IncomingFuture.html | 53 + pr/395/docs/iroh_quinn/struct.InvalidCid.html | 27 + .../iroh_quinn/struct.MtuDiscoveryConfig.html | 88 + .../docs/iroh_quinn/struct.NoneTokenLog.html | 31 + .../iroh_quinn/struct.NoneTokenStore.html | 25 + pr/395/docs/iroh_quinn/struct.OnClosed.html | 50 + pr/395/docs/iroh_quinn/struct.OpenBi.html | 54 + pr/395/docs/iroh_quinn/struct.OpenPath.html | 57 + pr/395/docs/iroh_quinn/struct.OpenUni.html | 54 + pr/395/docs/iroh_quinn/struct.Path.html | 48 + pr/395/docs/iroh_quinn/struct.PathId.html | 53 + pr/395/docs/iroh_quinn/struct.PathStats.html | 56 + pr/395/docs/iroh_quinn/struct.QlogConfig.html | 34 + .../iroh_quinn/struct.QlogFileFactory.html | 39 + .../docs/iroh_quinn/struct.ReadDatagram.html | 54 + pr/395/docs/iroh_quinn/struct.RecvStream.html | 210 + pr/395/docs/iroh_quinn/struct.RetryError.html | 27 + .../docs/iroh_quinn/struct.SendDatagram.html | 54 + pr/395/docs/iroh_quinn/struct.SendStream.html | 222 + .../docs/iroh_quinn/struct.ServerConfig.html | 114 + .../docs/iroh_quinn/struct.SmolRuntime.html | 25 + .../docs/iroh_quinn/struct.StdSystemTime.html | 26 + pr/395/docs/iroh_quinn/struct.StreamId.html | 55 + .../iroh_quinn/struct.TokenMemoryCache.html | 31 + .../iroh_quinn/struct.TokenReuseError.html | 25 + .../docs/iroh_quinn/struct.TokioRuntime.html | 25 + pr/395/docs/iroh_quinn/struct.Transmit.html | 37 + .../iroh_quinn/struct.TransportConfig.html | 252 + .../iroh_quinn/struct.TransportErrorCode.html | 57 + pr/395/docs/iroh_quinn/struct.UdpStats.html | 41 + .../struct.UnorderedRecvStream.html | 54 + .../struct.ValidationTokenConfig.html | 74 + pr/395/docs/iroh_quinn/struct.VarInt.html | 65 + .../struct.VarIntBoundsExceeded.html | 33 + .../struct.WeakConnectionHandle.html | 31 + pr/395/docs/iroh_quinn/struct.Written.html | 38 + .../iroh_quinn/struct.ZeroRttAccepted.html | 51 + pr/395/docs/iroh_quinn/trait.AsyncTimer.html | 11 + .../docs/iroh_quinn/trait.AsyncUdpSocket.html | 38 + .../trait.ConnectionIdGenerator.html | 21 + pr/395/docs/iroh_quinn/trait.QlogFactory.html | 23 + pr/395/docs/iroh_quinn/trait.Runtime.html | 19 + pr/395/docs/iroh_quinn/trait.TimeSource.html | 7 + pr/395/docs/iroh_quinn/trait.TokenLog.html | 45 + pr/395/docs/iroh_quinn/trait.TokenStore.html | 13 + pr/395/docs/iroh_quinn/trait.UdpSender.html | 30 + pr/395/docs/iroh_quinn_proto/all.html | 1 + .../iroh_quinn_proto/bench_exports/index.html | 2 + .../fn.get_into_many_segments.html | 2 + .../fn.get_loop_many_segments.html | 2 + .../send_buffer_benches/index.html | 4 + .../send_buffer_benches/sidebar-items.js | 1 + .../bench_exports/sidebar-items.js | 1 + .../bloom_token_log/struct.BloomTokenLog.html | 11 + .../struct.HashedConnectionIdGenerator.html | 11 + .../cid_generator/struct.InvalidCid.html | 11 + .../struct.RandomConnectionIdGenerator.html | 11 + .../trait.ConnectionIdGenerator.html | 11 + .../docs/iroh_quinn_proto/coding/index.html | 2 + .../iroh_quinn_proto/coding/sidebar-items.js | 1 + .../coding/struct.UnexpectedEnd.html | 33 + .../coding/trait.Decodable.html | 6 + .../coding/trait.Encodable.html | 6 + .../iroh_quinn_proto/coding/type.Result.html | 7 + .../config/enum.ConfigError.html | 11 + .../config/qlog/struct.QlogConfig.html | 11 + .../config/qlog/struct.QlogFileFactory.html | 11 + .../config/qlog/trait.QlogFactory.html | 11 + .../config/struct.ClientConfig.html | 11 + .../config/struct.EndpointConfig.html | 11 + .../config/struct.ServerConfig.html | 11 + .../config/struct.StdSystemTime.html | 11 + .../config/struct.ValidationTokenConfig.html | 11 + .../config/trait.TimeSource.html | 11 + ...NCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html | 11 + .../transport/struct.AckFrequencyConfig.html | 11 + .../config/transport/struct.IdleTimeout.html | 11 + .../transport/struct.MtuDiscoveryConfig.html | 11 + .../transport/struct.TransportConfig.html | 11 + .../congestion/bbr/struct.Bbr.html | 11 + .../congestion/bbr/struct.BbrConfig.html | 11 + .../congestion/cubic/struct.Cubic.html | 11 + .../congestion/cubic/struct.CubicConfig.html | 11 + .../iroh_quinn_proto/congestion/index.html | 2 + .../congestion/new_reno/struct.NewReno.html | 11 + .../new_reno/struct.NewRenoConfig.html | 11 + .../congestion/sidebar-items.js | 1 + .../congestion/struct.Bbr.html | 52 + .../congestion/struct.BbrConfig.html | 33 + .../congestion/struct.ControllerMetrics.html | 32 + .../congestion/struct.Cubic.html | 48 + .../congestion/struct.CubicConfig.html | 29 + .../congestion/struct.NewReno.html | 48 + .../congestion/struct.NewRenoConfig.html | 30 + .../congestion/trait.Controller.html | 79 + .../congestion/trait.ControllerFactory.html | 10 + .../connection/assembler/struct.Chunk.html | 11 + .../datagrams/enum.SendDatagramError.html | 11 + .../datagrams/struct.Datagrams.html | 11 + .../connection/enum.ClosePathError.html | 11 + .../connection/enum.ConnectionError.html | 11 + .../connection/enum.Event.html | 11 + .../connection/enum.PathError.html | 11 + .../connection/paths/enum.PathEvent.html | 11 + .../connection/paths/enum.PathStatus.html | 11 + .../paths/enum.SetPathStatusError.html | 11 + .../connection/paths/struct.ClosedPath.html | 11 + .../connection/paths/struct.PathId.html | 11 + .../connection/paths/struct.RttEstimator.html | 11 + .../fn.get_into_many_segments.html | 11 + .../fn.get_loop_many_segments.html | 11 + .../send_buffer_benches/index.html | 11 + .../stats/struct.ConnectionStats.html | 11 + .../connection/stats/struct.FrameStats.html | 11 + .../connection/stats/struct.PathStats.html | 11 + .../connection/stats/struct.UdpStats.html | 11 + .../connection/streams/enum.StreamEvent.html | 11 + .../streams/recv/enum.ReadError.html | 11 + .../streams/recv/enum.ReadableError.html | 11 + .../streams/recv/struct.Chunks.html | 11 + .../streams/send/enum.FinishError.html | 11 + .../streams/send/enum.WriteError.html | 11 + .../streams/send/struct.Written.html | 11 + .../streams/struct.ClosedStream.html | 11 + .../connection/streams/struct.RecvStream.html | 11 + .../connection/streams/struct.SendStream.html | 11 + .../streams/struct.ShouldTransmit.html | 11 + .../connection/streams/struct.Streams.html | 11 + .../connection/struct.Connection.html | 11 + .../struct.MultipathNotNegotiated.html | 11 + ...NCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html | 3 + .../constant.DEFAULT_SUPPORTED_VERSIONS.html | 2 + .../docs/iroh_quinn_proto/crypto/index.html | 8 + .../crypto/rustls/enum.Error.html | 101 + .../iroh_quinn_proto/crypto/rustls/index.html | 2 + .../crypto/rustls/sidebar-items.js | 1 + .../crypto/rustls/struct.HandshakeData.html | 34 + .../rustls/struct.NoInitialCipherSuite.html | 31 + .../rustls/struct.QuicClientConfig.html | 48 + .../rustls/struct.QuicServerConfig.html | 52 + .../crypto/rustls/struct.TlsSession.html | 40 + .../iroh_quinn_proto/crypto/sidebar-items.js | 1 + .../crypto/struct.CryptoError.html | 25 + .../struct.ExportKeyingMaterialError.html | 31 + .../crypto/struct.KeyPair.html | 36 + .../iroh_quinn_proto/crypto/struct.Keys.html | 30 + .../crypto/struct.UnsupportedVersion.html | 25 + .../crypto/trait.AeadKey.html | 32 + .../crypto/trait.ClientConfig.html | 16 + .../crypto/trait.HandshakeTokenKey.html | 6 + .../crypto/trait.HeaderKey.html | 10 + .../crypto/trait.HmacKey.html | 10 + .../crypto/trait.PacketKey.html | 51 + .../crypto/trait.ServerConfig.html | 38 + .../crypto/trait.Session.html | 72 + .../endpoint/enum.ConnectError.html | 11 + .../endpoint/enum.DatagramEvent.html | 11 + .../endpoint/struct.AcceptError.html | 11 + .../endpoint/struct.ConnectionHandle.html | 11 + .../endpoint/struct.Endpoint.html | 11 + .../endpoint/struct.Incoming.html | 11 + .../endpoint/struct.RetryError.html | 11 + .../iroh_quinn_proto/enum.ClosePathError.html | 38 + .../iroh_quinn_proto/enum.ConfigError.html | 36 + .../iroh_quinn_proto/enum.ConnectError.html | 51 + .../enum.ConnectionError.html | 54 + .../iroh_quinn_proto/enum.DatagramEvent.html | 32 + pr/395/docs/iroh_quinn_proto/enum.Dir.html | 53 + .../iroh_quinn_proto/enum.EcnCodepoint.html | 41 + pr/395/docs/iroh_quinn_proto/enum.Event.html | 48 + .../iroh_quinn_proto/enum.FinishError.html | 40 + .../docs/iroh_quinn_proto/enum.FrameType.html | 77 + .../docs/iroh_quinn_proto/enum.LongType.html | 37 + .../iroh_quinn_proto/enum.MaybeFrame.html | 40 + .../enum.PacketDecodeError.html | 55 + .../docs/iroh_quinn_proto/enum.PathError.html | 46 + .../docs/iroh_quinn_proto/enum.PathEvent.html | 80 + .../iroh_quinn_proto/enum.PathStatus.html | 43 + .../enum.ProtectedHeader.html | 75 + .../docs/iroh_quinn_proto/enum.ReadError.html | 51 + .../iroh_quinn_proto/enum.ReadableError.html | 50 + .../enum.SendDatagramError.html | 54 + .../enum.SetPathStatusError.html | 38 + pr/395/docs/iroh_quinn_proto/enum.Side.html | 54 + .../iroh_quinn_proto/enum.StreamEvent.html | 64 + .../iroh_quinn_proto/enum.WriteError.html | 54 + .../frame/enum.FrameType.html | 11 + .../frame/enum.MaybeFrame.html | 11 + .../frame/struct.ApplicationClose.html | 11 + .../frame/struct.ConnectionClose.html | 11 + .../frame/struct.Datagram.html | 11 + .../frame/struct.DatagramInfo.html | 11 + .../frame/struct.InvalidFrameId.html | 11 + .../frame/struct.StreamInfo.html | 11 + pr/395/docs/iroh_quinn_proto/index.html | 14 + .../iroh_quinn_proto/iroh_hp/enum.Error.html | 39 + .../iroh_quinn_proto/iroh_hp/enum.Event.html | 32 + .../docs/iroh_quinn_proto/iroh_hp/index.html | 2 + .../iroh_quinn_proto/iroh_hp/sidebar-items.js | 1 + .../packet/enum.LongType.html | 11 + .../packet/enum.PacketDecodeError.html | 11 + .../packet/enum.ProtectedHeader.html | 11 + .../struct.FixedLengthConnectionIdParser.html | 11 + .../packet/struct.PartialDecode.html | 11 + .../packet/struct.ProtectedInitialHeader.html | 11 + .../packet/trait.ConnectionIdParser.html | 11 + .../shared/enum.EcnCodepoint.html | 11 + .../shared/struct.ConnectionEvent.html | 11 + .../shared/struct.ConnectionId.html | 11 + .../shared/struct.EndpointEvent.html | 11 + pr/395/docs/iroh_quinn_proto/sidebar-items.js | 1 + .../iroh_quinn_proto/struct.AcceptError.html | 30 + .../struct.AckFrequencyConfig.html | 59 + .../struct.ApplicationClose.html | 38 + .../struct.BloomTokenLog.html | 48 + .../docs/iroh_quinn_proto/struct.Chunk.html | 35 + .../docs/iroh_quinn_proto/struct.Chunks.html | 41 + .../iroh_quinn_proto/struct.ClientConfig.html | 46 + .../iroh_quinn_proto/struct.ClosedPath.html | 33 + .../iroh_quinn_proto/struct.ClosedStream.html | 33 + .../iroh_quinn_proto/struct.Connection.html | 250 + .../struct.ConnectionClose.html | 40 + .../struct.ConnectionEvent.html | 25 + .../struct.ConnectionHandle.html | 42 + .../iroh_quinn_proto/struct.ConnectionId.html | 2468 ++++++ .../struct.ConnectionStats.html | 36 + .../iroh_quinn_proto/struct.Datagram.html | 31 + .../iroh_quinn_proto/struct.DatagramInfo.html | 32 + .../iroh_quinn_proto/struct.Datagrams.html | 44 + .../iroh_quinn_proto/struct.Endpoint.html | 78 + .../struct.EndpointConfig.html | 66 + .../struct.EndpointEvent.html | 30 + .../struct.FixedLengthConnectionIdParser.html | 26 + .../iroh_quinn_proto/struct.FourTuple.html | 58 + .../iroh_quinn_proto/struct.FrameStats.html | 65 + .../struct.HashedConnectionIdGenerator.html | 33 + .../iroh_quinn_proto/struct.IdleTimeout.html | 49 + .../iroh_quinn_proto/struct.Incoming.html | 36 + .../iroh_quinn_proto/struct.InvalidCid.html | 27 + .../struct.InvalidFrameId.html | 31 + .../struct.MtuDiscoveryConfig.html | 85 + .../struct.MultipathNotNegotiated.html | 28 + .../iroh_quinn_proto/struct.NoneTokenLog.html | 31 + .../struct.NoneTokenStore.html | 25 + .../struct.PartialDecode.html | 42 + .../docs/iroh_quinn_proto/struct.PathId.html | 49 + .../iroh_quinn_proto/struct.PathStats.html | 56 + .../struct.ProtectedInitialHeader.html | 38 + .../iroh_quinn_proto/struct.QlogConfig.html | 34 + .../struct.QlogFileFactory.html | 39 + .../struct.RandomConnectionIdGenerator.html | 33 + .../iroh_quinn_proto/struct.RecvStream.html | 44 + .../iroh_quinn_proto/struct.RetryError.html | 27 + .../iroh_quinn_proto/struct.RttEstimator.html | 32 + .../iroh_quinn_proto/struct.SendStream.html | 53 + .../iroh_quinn_proto/struct.ServerConfig.html | 102 + .../struct.ShouldTransmit.html | 35 + .../struct.StdSystemTime.html | 26 + .../iroh_quinn_proto/struct.StreamId.html | 52 + .../iroh_quinn_proto/struct.StreamInfo.html | 32 + .../docs/iroh_quinn_proto/struct.Streams.html | 35 + .../struct.TokenMemoryCache.html | 28 + .../struct.TokenReuseError.html | 25 + .../iroh_quinn_proto/struct.Transmit.html | 37 + .../struct.TransportConfig.html | 222 + .../struct.TransportError.html | 45 + .../struct.TransportErrorCode.html | 55 + .../iroh_quinn_proto/struct.UdpStats.html | 41 + .../struct.ValidationTokenConfig.html | 74 + .../docs/iroh_quinn_proto/struct.VarInt.html | 62 + .../struct.VarIntBoundsExceeded.html | 33 + .../docs/iroh_quinn_proto/struct.Written.html | 38 + .../token/struct.NoneTokenLog.html | 11 + .../token/struct.NoneTokenStore.html | 11 + .../token/struct.TokenReuseError.html | 11 + .../token/trait.TokenLog.html | 11 + .../token/trait.TokenStore.html | 11 + .../struct.TokenMemoryCache.html | 11 + .../trait.ConnectionIdGenerator.html | 21 + .../trait.ConnectionIdParser.html | 9 + .../iroh_quinn_proto/trait.QlogFactory.html | 23 + .../iroh_quinn_proto/trait.TimeSource.html | 7 + .../docs/iroh_quinn_proto/trait.TokenLog.html | 45 + .../iroh_quinn_proto/trait.TokenStore.html | 13 + .../transport_error/struct.Code.html | 11 + .../transport_error/struct.Error.html | 11 + .../transport_parameters/enum.Error.html | 38 + .../transport_parameters/index.html | 7 + .../transport_parameters/sidebar-items.js | 1 + .../struct.TransportParameters.html | 38 + .../varint/struct.VarInt.html | 11 + .../varint/struct.VarIntBoundsExceeded.html | 11 + pr/395/docs/iroh_quinn_udp/all.html | 1 + .../iroh_quinn_udp/constant.BATCH_SIZE.html | 2 + .../iroh_quinn_udp/enum.EcnCodepoint.html | 28 + .../imp/struct.UdpSocketState.html | 11 + pr/395/docs/iroh_quinn_udp/index.html | 22 + pr/395/docs/iroh_quinn_udp/sidebar-items.js | 1 + .../docs/iroh_quinn_udp/struct.RecvMeta.html | 41 + .../docs/iroh_quinn_udp/struct.Transmit.html | 31 + .../iroh_quinn_udp/struct.UdpSockRef.html | 19 + .../iroh_quinn_udp/struct.UdpSocketState.html | 62 + pr/395/docs/perf/all.html | 1 + pr/395/docs/perf/client/fn.run.html | 1 + pr/395/docs/perf/client/index.html | 1 + pr/395/docs/perf/client/sidebar-items.js | 1 + pr/395/docs/perf/client/struct.Opt.html | 43 + .../docs/perf/enum.CongestionAlgorithm.html | 30 + pr/395/docs/perf/fn.parse_byte_size.html | 1 + pr/395/docs/perf/index.html | 1 + pr/395/docs/perf/noprotection/index.html | 1 + .../docs/perf/noprotection/sidebar-items.js | 1 + .../struct.NoProtectionClientConfig.html | 29 + .../struct.NoProtectionServerConfig.html | 37 + pr/395/docs/perf/server/fn.run.html | 1 + pr/395/docs/perf/server/index.html | 1 + pr/395/docs/perf/server/sidebar-items.js | 1 + pr/395/docs/perf/server/struct.Opt.html | 42 + pr/395/docs/perf/sidebar-items.js | 1 + .../docs/perf/static.PERF_CIPHER_SUITES.html | 1 + pr/395/docs/perf/stats/index.html | 1 + pr/395/docs/perf/stats/sidebar-items.js | 1 + .../perf/stats/struct.OpenStreamStats.html | 35 + pr/395/docs/perf/stats/struct.Stats.html | 24 + .../docs/perf/stats/struct.StreamStats.html | 24 + pr/395/docs/perf/struct.CommonOpt.html | 84 + pr/395/docs/quinn_perf/all.html | 1 + pr/395/docs/quinn_perf/enum.Commands.html | 40 + pr/395/docs/quinn_perf/fn.main.html | 1 + pr/395/docs/quinn_perf/index.html | 1 + pr/395/docs/quinn_perf/sidebar-items.js | 1 + pr/395/docs/quinn_perf/struct.Cli.html | 45 + pr/395/docs/search.index/0022edf8729b.js | 1 + pr/395/docs/search.index/0051e5fe15da.js | 1 + pr/395/docs/search.index/009757435c6f.js | 1 + pr/395/docs/search.index/00aa835204e9.js | 1 + pr/395/docs/search.index/01c8653e6e9f.js | 1 + pr/395/docs/search.index/02bba2f3bb15.js | 1 + pr/395/docs/search.index/0515792bb474.js | 1 + pr/395/docs/search.index/056473b4cdef.js | 1 + pr/395/docs/search.index/06205a12fd8a.js | 1 + pr/395/docs/search.index/062983155a37.js | 1 + pr/395/docs/search.index/06edb61cc89f.js | 1 + pr/395/docs/search.index/070e1accd154.js | 1 + pr/395/docs/search.index/07329e64a5f0.js | 1 + pr/395/docs/search.index/077a08ac2280.js | 1 + pr/395/docs/search.index/085d7a8021d6.js | 1 + pr/395/docs/search.index/097a8c9da4ad.js | 1 + pr/395/docs/search.index/09c34cd06079.js | 1 + pr/395/docs/search.index/0a0d55e0322f.js | 1 + pr/395/docs/search.index/0ab68eae012d.js | 1 + pr/395/docs/search.index/0c67f8980f27.js | 1 + pr/395/docs/search.index/0c6d6b68fc28.js | 1 + pr/395/docs/search.index/0ca033ef2465.js | 1 + pr/395/docs/search.index/0d27f25fe856.js | 1 + pr/395/docs/search.index/0d76b2553bfd.js | 1 + pr/395/docs/search.index/0d7e3bfebb98.js | 1 + pr/395/docs/search.index/0dec9e618431.js | 1 + pr/395/docs/search.index/0f04eaaf1f31.js | 1 + pr/395/docs/search.index/0f80e54f3f36.js | 1 + pr/395/docs/search.index/10696d58fab9.js | 1 + pr/395/docs/search.index/106bd6af91ad.js | 1 + pr/395/docs/search.index/10dca90977e7.js | 1 + pr/395/docs/search.index/10e28db8e153.js | 1 + pr/395/docs/search.index/10fa6d29f4f2.js | 1 + pr/395/docs/search.index/115053be1aee.js | 1 + pr/395/docs/search.index/11ecde640804.js | 1 + pr/395/docs/search.index/122837952335.js | 1 + pr/395/docs/search.index/1257c4864088.js | 1 + pr/395/docs/search.index/1298ab4d6c58.js | 1 + pr/395/docs/search.index/12e5c196480d.js | 1 + pr/395/docs/search.index/13c59bf23082.js | 1 + pr/395/docs/search.index/14148cabdb79.js | 1 + pr/395/docs/search.index/14351f4f78de.js | 1 + pr/395/docs/search.index/1490375c9199.js | 1 + pr/395/docs/search.index/14bb3c65ae19.js | 1 + pr/395/docs/search.index/173afb185531.js | 1 + pr/395/docs/search.index/1754ac96d54a.js | 1 + pr/395/docs/search.index/175b4e0f835c.js | 1 + pr/395/docs/search.index/176725a3f611.js | 1 + pr/395/docs/search.index/1779ad771958.js | 1 + pr/395/docs/search.index/17830149a792.js | 1 + pr/395/docs/search.index/182b6e2dbd4a.js | 1 + pr/395/docs/search.index/1889fd721cb5.js | 1 + pr/395/docs/search.index/18ccfd1e3472.js | 1 + pr/395/docs/search.index/196b6ab52bc1.js | 1 + pr/395/docs/search.index/1a14b9a7d2f6.js | 1 + pr/395/docs/search.index/1b113bad2321.js | 1 + pr/395/docs/search.index/1b5c0368d221.js | 1 + pr/395/docs/search.index/1c404dc1a453.js | 1 + pr/395/docs/search.index/1d8c01ecbe8e.js | 1 + pr/395/docs/search.index/1e253efce535.js | 1 + pr/395/docs/search.index/1e45b5c47ee4.js | 1 + pr/395/docs/search.index/1e521ee4a588.js | 1 + pr/395/docs/search.index/1ebda14005f5.js | 1 + pr/395/docs/search.index/1f4506df5023.js | 1 + pr/395/docs/search.index/220c15e98816.js | 1 + pr/395/docs/search.index/225e079aa968.js | 1 + pr/395/docs/search.index/25add3d45510.js | 1 + pr/395/docs/search.index/25c8d0469b0f.js | 1 + pr/395/docs/search.index/263fb2099b3d.js | 1 + pr/395/docs/search.index/269534fbdbd0.js | 1 + pr/395/docs/search.index/283059613ad2.js | 1 + pr/395/docs/search.index/287195228062.js | 1 + pr/395/docs/search.index/288ad6e859a7.js | 1 + pr/395/docs/search.index/2937f2ef82d6.js | 1 + pr/395/docs/search.index/29d3c2222f61.js | 1 + pr/395/docs/search.index/2a562ad3f29f.js | 1 + pr/395/docs/search.index/2aa2debf8ed5.js | 1 + pr/395/docs/search.index/2aaca7f8de58.js | 1 + pr/395/docs/search.index/2b2167e7f8b2.js | 1 + pr/395/docs/search.index/2b7cfd46cb26.js | 1 + pr/395/docs/search.index/2c24473cb3e9.js | 1 + pr/395/docs/search.index/2c46939b6ca2.js | 1 + pr/395/docs/search.index/2dd8fd656c06.js | 1 + pr/395/docs/search.index/2eb373ad3c24.js | 1 + pr/395/docs/search.index/2f71766d07cc.js | 1 + pr/395/docs/search.index/301f4a70d960.js | 1 + pr/395/docs/search.index/3071a6cda431.js | 1 + pr/395/docs/search.index/30f9df4e9837.js | 1 + pr/395/docs/search.index/30ffbfdd11c6.js | 1 + pr/395/docs/search.index/31340ca2241e.js | 1 + pr/395/docs/search.index/3210ce3940fa.js | 1 + pr/395/docs/search.index/3240004448f4.js | 1 + pr/395/docs/search.index/32b9b5daf9e8.js | 1 + pr/395/docs/search.index/32ee32d485a7.js | 1 + pr/395/docs/search.index/3362c9bd9554.js | 1 + pr/395/docs/search.index/336f27a85aec.js | 1 + pr/395/docs/search.index/343b00f6b1cb.js | 1 + pr/395/docs/search.index/34a03cbb2682.js | 1 + pr/395/docs/search.index/34b69ebbda67.js | 1 + pr/395/docs/search.index/34cae06d030c.js | 1 + pr/395/docs/search.index/352927a38cef.js | 1 + pr/395/docs/search.index/36d2aad2bbc2.js | 1 + pr/395/docs/search.index/37448ddeb53e.js | 1 + pr/395/docs/search.index/37e6d63af90e.js | 1 + pr/395/docs/search.index/37f48bae88e4.js | 1 + pr/395/docs/search.index/380e3640e532.js | 1 + pr/395/docs/search.index/3848f16ce3c2.js | 1 + pr/395/docs/search.index/386ab9ff3804.js | 1 + pr/395/docs/search.index/3882b0299479.js | 1 + pr/395/docs/search.index/38f2efb5b65e.js | 1 + pr/395/docs/search.index/39ac769ad27b.js | 1 + pr/395/docs/search.index/3aa6d9572c12.js | 1 + pr/395/docs/search.index/3be462b0d192.js | 1 + pr/395/docs/search.index/3c5511ca849c.js | 1 + pr/395/docs/search.index/3e13ecd45e17.js | 1 + pr/395/docs/search.index/3e1b601c727f.js | 1 + pr/395/docs/search.index/3ea2a9f02ef8.js | 1 + pr/395/docs/search.index/3ef16a8006fc.js | 1 + pr/395/docs/search.index/3f496fd7d7dd.js | 1 + pr/395/docs/search.index/3f4f416f647f.js | 1 + pr/395/docs/search.index/3fa2f95c1020.js | 1 + pr/395/docs/search.index/410cbfd39641.js | 1 + pr/395/docs/search.index/4248fe585b27.js | 1 + pr/395/docs/search.index/4375a073b2db.js | 1 + pr/395/docs/search.index/45f6ee715dad.js | 1 + pr/395/docs/search.index/474d1a590443.js | 1 + pr/395/docs/search.index/483e10d0fb1a.js | 1 + pr/395/docs/search.index/485aace276e9.js | 1 + pr/395/docs/search.index/49793047756c.js | 1 + pr/395/docs/search.index/4ae488f26dde.js | 1 + pr/395/docs/search.index/4b97e0c80ad3.js | 1 + pr/395/docs/search.index/4c180bbff966.js | 1 + pr/395/docs/search.index/4cafff0b91f7.js | 1 + pr/395/docs/search.index/4d6048f07ed5.js | 1 + pr/395/docs/search.index/4d8a1f849ace.js | 1 + pr/395/docs/search.index/4da0028e700f.js | 1 + pr/395/docs/search.index/4f541acf0097.js | 1 + pr/395/docs/search.index/4fa3f1ec9b83.js | 1 + pr/395/docs/search.index/4fa8c75c3f45.js | 1 + pr/395/docs/search.index/503d89ddb639.js | 1 + pr/395/docs/search.index/50a025b610c7.js | 1 + pr/395/docs/search.index/510029c366bd.js | 1 + pr/395/docs/search.index/511f426cf452.js | 1 + pr/395/docs/search.index/51be0345651c.js | 1 + pr/395/docs/search.index/51fb0e67a5a5.js | 1 + pr/395/docs/search.index/534cb5ffe8b1.js | 1 + pr/395/docs/search.index/5489a4fd5a52.js | 1 + pr/395/docs/search.index/54a0f4bbac45.js | 1 + pr/395/docs/search.index/550f724dbd52.js | 1 + pr/395/docs/search.index/551174c27cfd.js | 1 + pr/395/docs/search.index/55d74f9a517a.js | 1 + pr/395/docs/search.index/55dc7f8c9f93.js | 1 + pr/395/docs/search.index/567c4dff1ab9.js | 1 + pr/395/docs/search.index/56fd0f0047d0.js | 1 + pr/395/docs/search.index/573cd35ae6b3.js | 1 + pr/395/docs/search.index/57fb74ce5042.js | 1 + pr/395/docs/search.index/583daef1b6ad.js | 1 + pr/395/docs/search.index/592f5e893bf5.js | 1 + pr/395/docs/search.index/597d40647479.js | 1 + pr/395/docs/search.index/59b1b3c79cd8.js | 1 + pr/395/docs/search.index/5a4d4c312dc2.js | 1 + pr/395/docs/search.index/5aa972e70a3a.js | 1 + pr/395/docs/search.index/5ab07c163f18.js | 1 + pr/395/docs/search.index/5add61f19273.js | 1 + pr/395/docs/search.index/5d2c023a10ee.js | 1 + pr/395/docs/search.index/5d68fc368cd0.js | 1 + pr/395/docs/search.index/5d85d427a4e9.js | 1 + pr/395/docs/search.index/5db8f7a37811.js | 1 + pr/395/docs/search.index/5e234a0755e9.js | 1 + pr/395/docs/search.index/5fc89dcacf77.js | 1 + pr/395/docs/search.index/6037a95cf9ab.js | 1 + pr/395/docs/search.index/610707ac8244.js | 1 + pr/395/docs/search.index/6207a70bbfba.js | 1 + pr/395/docs/search.index/62bb924ae01b.js | 1 + pr/395/docs/search.index/639a2b456721.js | 1 + pr/395/docs/search.index/64d55e5f1bdd.js | 1 + pr/395/docs/search.index/64e46650dfe4.js | 1 + pr/395/docs/search.index/657c7a54fc8d.js | 1 + pr/395/docs/search.index/665d99930d33.js | 1 + pr/395/docs/search.index/66af3e6bd759.js | 1 + pr/395/docs/search.index/677118915139.js | 1 + pr/395/docs/search.index/693c05a1eff1.js | 1 + pr/395/docs/search.index/6aea33209657.js | 1 + pr/395/docs/search.index/6af0c970065d.js | 1 + pr/395/docs/search.index/6b88e2cdd5d5.js | 1 + pr/395/docs/search.index/6bcf60824292.js | 1 + pr/395/docs/search.index/6bd9cf2870c6.js | 1 + pr/395/docs/search.index/6ccbc319ac73.js | 1 + pr/395/docs/search.index/6d3546009992.js | 1 + pr/395/docs/search.index/6ef23363b488.js | 1 + pr/395/docs/search.index/6fd721d22688.js | 1 + pr/395/docs/search.index/7017e57856f5.js | 1 + pr/395/docs/search.index/71b46e229639.js | 1 + pr/395/docs/search.index/71d860ce371b.js | 1 + pr/395/docs/search.index/724367660563.js | 1 + pr/395/docs/search.index/73a80984aee8.js | 1 + pr/395/docs/search.index/73e02919d8d7.js | 1 + pr/395/docs/search.index/7403472d979d.js | 1 + pr/395/docs/search.index/745e0125b99d.js | 1 + pr/395/docs/search.index/751ec5fe38a5.js | 1 + pr/395/docs/search.index/75634231c1f5.js | 1 + pr/395/docs/search.index/7743460538bb.js | 1 + pr/395/docs/search.index/77cd02480e18.js | 1 + pr/395/docs/search.index/782860c5c65d.js | 1 + pr/395/docs/search.index/792cc1abc097.js | 1 + pr/395/docs/search.index/7a1f32acb3d1.js | 1 + pr/395/docs/search.index/7b8a6a4a9fb4.js | 1 + pr/395/docs/search.index/7c16cb6d06bb.js | 1 + pr/395/docs/search.index/7c840b92ee85.js | 1 + pr/395/docs/search.index/7d4f6308935a.js | 1 + pr/395/docs/search.index/7f07551082cf.js | 1 + pr/395/docs/search.index/7f8c3fb9fdb6.js | 1 + pr/395/docs/search.index/7f983daa5a41.js | 1 + pr/395/docs/search.index/7fee0594063e.js | 1 + .../docs/search.index/alias/a762150c532c.js | 1 + .../search.index/crateNames/8a381fcf2bd4.js | 1 + pr/395/docs/search.index/desc/377b54f4891b.js | 1 + .../docs/search.index/entry/1282991302e8.js | 1 + .../search.index/function/2aa7c4c5510f.js | 1 + .../generic_inverted_index/5d8b801915db.js | 1 + pr/395/docs/search.index/name/7c550d2a41fa.js | 1 + .../normalizedName/7cb9cf25523a.js | 1 + pr/395/docs/search.index/path/69f26a7f5864.js | 1 + pr/395/docs/search.index/root.js | 1 + pr/395/docs/search.index/type/84f7d456d387.js | 1 + pr/395/docs/set_up_connection/all.html | 1 + .../constant.CLIENT_ADDR.html | 1 + .../constant.LOCALHOST_V4.html | 1 + .../constant.SERVER_ADDR.html | 1 + .../constant.SERVER_NAME.html | 1 + pr/395/docs/set_up_connection/fn.client.html | 1 + pr/395/docs/set_up_connection/fn.main.html | 1 + pr/395/docs/set_up_connection/fn.server.html | 1 + pr/395/docs/set_up_connection/index.html | 1 + .../docs/set_up_connection/sidebar-items.js | 1 + pr/395/docs/settings.html | 1 + pr/395/docs/src-files.js | 2 + pr/395/docs/src/bench/lib.rs.html | 256 + pr/395/docs/src/bench/stats.rs.html | 95 + pr/395/docs/src/bulk/bulk.rs.html | 219 + .../docs/src/certificate/certificate.rs.html | 106 + .../src/data_transfer/data-transfer.rs.html | 56 + pr/395/docs/src/iroh_quinn/connection.rs.html | 1768 +++++ pr/395/docs/src/iroh_quinn/endpoint.rs.html | 933 +++ pr/395/docs/src/iroh_quinn/incoming.rs.html | 152 + pr/395/docs/src/iroh_quinn/lib.rs.html | 138 + pr/395/docs/src/iroh_quinn/mutex.rs.html | 163 + pr/395/docs/src/iroh_quinn/path.rs.html | 303 + .../docs/src/iroh_quinn/recv_stream.rs.html | 759 ++ .../docs/src/iroh_quinn/runtime/mod.rs.html | 251 + .../docs/src/iroh_quinn/runtime/smol.rs.html | 102 + .../docs/src/iroh_quinn/runtime/tokio.rs.html | 106 + .../docs/src/iroh_quinn/send_stream.rs.html | 451 ++ .../docs/src/iroh_quinn/work_limiter.rs.html | 224 + .../address_discovery.rs.html | 126 + .../iroh_quinn_proto/bloom_token_log.rs.html | 368 + .../iroh_quinn_proto/cid_generator.rs.html | 180 + .../src/iroh_quinn_proto/cid_queue.rs.html | 484 ++ .../docs/src/iroh_quinn_proto/coding.rs.html | 152 + .../src/iroh_quinn_proto/config/mod.rs.html | 696 ++ .../src/iroh_quinn_proto/config/qlog.rs.html | 173 + .../iroh_quinn_proto/config/transport.rs.html | 912 +++ .../src/iroh_quinn_proto/congestion.rs.html | 112 + .../congestion/bbr/bw_estimation.rs.html | 101 + .../congestion/bbr/min_max.rs.html | 152 + .../congestion/bbr/mod.rs.html | 651 ++ .../iroh_quinn_proto/congestion/cubic.rs.html | 299 + .../congestion/new_reno.rs.html | 173 + .../connection/ack_frequency.rs.html | 159 + .../connection/assembler.rs.html | 845 ++ .../connection/cid_state.rs.html | 229 + .../connection/datagrams.rs.html | 222 + .../iroh_quinn_proto/connection/mod.rs.html | 7064 +++++++++++++++++ .../iroh_quinn_proto/connection/mtud.rs.html | 980 +++ .../connection/pacing.rs.html | 325 + .../connection/packet_builder.rs.html | 449 ++ .../connection/packet_crypto.rs.html | 198 + .../iroh_quinn_proto/connection/paths.rs.html | 993 +++ .../iroh_quinn_proto/connection/qlog.rs.html | 1269 +++ .../connection/send_buffer.rs.html | 762 ++ .../connection/spaces.rs.html | 1330 ++++ .../iroh_quinn_proto/connection/state.rs.html | 414 + .../iroh_quinn_proto/connection/stats.rs.html | 205 + .../connection/streams/mod.rs.html | 554 ++ .../connection/streams/recv.rs.html | 544 ++ .../connection/streams/send.rs.html | 414 + .../connection/streams/state.rs.html | 2050 +++++ .../iroh_quinn_proto/connection/timer.rs.html | 498 ++ .../connection/transmit_buf.rs.html | 236 + .../iroh_quinn_proto/constant_time.rs.html | 22 + .../docs/src/iroh_quinn_proto/crypto.rs.html | 221 + .../iroh_quinn_proto/crypto/ring_like.rs.html | 57 + .../iroh_quinn_proto/crypto/rustls.rs.html | 660 ++ .../src/iroh_quinn_proto/endpoint.rs.html | 1373 ++++ .../docs/src/iroh_quinn_proto/frame.rs.html | 2837 +++++++ .../docs/src/iroh_quinn_proto/iroh_hp.rs.html | 593 ++ pr/395/docs/src/iroh_quinn_proto/lib.rs.html | 430 + .../docs/src/iroh_quinn_proto/packet.rs.html | 1079 +++ .../range_set/array_range_set.rs.html | 253 + .../iroh_quinn_proto/range_set/mod.rs.html | 5 + .../docs/src/iroh_quinn_proto/shared.rs.html | 201 + .../docs/src/iroh_quinn_proto/token.rs.html | 506 ++ .../token_memory_cache.rs.html | 247 + .../iroh_quinn_proto/transport_error.rs.html | 166 + .../transport_parameters.rs.html | 979 +++ .../docs/src/iroh_quinn_proto/varint.rs.html | 233 + .../docs/src/iroh_quinn_udp/cmsg/mod.rs.html | 157 + .../docs/src/iroh_quinn_udp/cmsg/unix.rs.html | 81 + pr/395/docs/src/iroh_quinn_udp/lib.rs.html | 300 + pr/395/docs/src/iroh_quinn_udp/unix.rs.html | 1079 +++ pr/395/docs/src/perf/client.rs.html | 419 + pr/395/docs/src/perf/lib.rs.html | 219 + pr/395/docs/src/perf/noprotection.rs.html | 205 + pr/395/docs/src/perf/server.rs.html | 202 + pr/395/docs/src/perf/stats.rs.html | 413 + pr/395/docs/src/quinn_perf/perf.rs.html | 46 + .../set-up-connection.rs.html | 38 + .../docs/static.files/COPYRIGHT-7fb11f4e.txt | 71 + .../FiraMono-Medium-86f75c8c.woff2 | Bin 0 -> 64572 bytes .../FiraMono-Regular-87c26294.woff2 | Bin 0 -> 64868 bytes .../FiraSans-Italic-81dc35de.woff2 | Bin 0 -> 136300 bytes .../FiraSans-LICENSE-05ab6dbd.txt | 98 + .../FiraSans-Medium-e1aa3f0a.woff2 | Bin 0 -> 132780 bytes .../FiraSans-MediumItalic-ccf7e434.woff2 | Bin 0 -> 140588 bytes .../FiraSans-Regular-0fe48ade.woff2 | Bin 0 -> 129188 bytes .../static.files/LICENSE-APACHE-a60eea81.txt | 201 + .../static.files/LICENSE-MIT-23f18e03.txt | 23 + .../NanumBarunGothic-13b3dcba.ttf.woff2 | Bin 0 -> 399468 bytes .../NanumBarunGothic-LICENSE-a37d393b.txt | 103 + .../SourceCodePro-It-fc8b9304.ttf.woff2 | Bin 0 -> 44896 bytes .../SourceCodePro-LICENSE-67f54ca7.txt | 97 + .../SourceCodePro-Regular-8badfe75.ttf.woff2 | Bin 0 -> 52228 bytes .../SourceCodePro-Semibold-aa29a496.ttf.woff2 | Bin 0 -> 52348 bytes .../SourceSerif4-Bold-6d4fd4c0.ttf.woff2 | Bin 0 -> 81540 bytes .../SourceSerif4-It-ca3b17ed.ttf.woff2 | Bin 0 -> 59716 bytes .../SourceSerif4-LICENSE-a2cfd9d5.md | 98 + .../SourceSerif4-Regular-6b053e98.ttf.woff2 | Bin 0 -> 76260 bytes .../SourceSerif4-Semibold-457a13ac.ttf.woff2 | Bin 0 -> 80732 bytes pr/395/docs/static.files/favicon-044be391.svg | 24 + .../static.files/favicon-32x32-eab170b8.png | Bin 0 -> 690 bytes pr/395/docs/static.files/main-ce535bd0.js | 24 + .../docs/static.files/normalize-9960930a.css | 2 + .../docs/static.files/noscript-263c88ec.css | 1 + .../docs/static.files/rust-logo-9a9549ea.svg | 61 + pr/395/docs/static.files/rustdoc-ca0dd0c4.css | 86 + .../static.files/scrape-examples-2bbcccac.js | 1 + pr/395/docs/static.files/search-8d3311b9.js | 5 + pr/395/docs/static.files/settings-c38705f0.js | 17 + .../docs/static.files/src-script-813739b1.js | 1 + pr/395/docs/static.files/storage-e2aeef58.js | 27 + .../docs/static.files/stringdex-828709d0.js | 2 + .../trait.impl/arbitrary/trait.Arbitrary.js | 9 + .../clap_builder/derive/trait.Args.js | 9 + .../derive/trait.CommandFactory.js | 9 + .../derive/trait.FromArgMatches.js | 9 + .../clap_builder/derive/trait.Parser.js | 9 + .../clap_builder/derive/trait.Subcommand.js | 9 + .../clap_builder/derive/trait.ValueEnum.js | 9 + .../docs/trait.impl/core/clone/trait.Clone.js | 9 + pr/395/docs/trait.impl/core/cmp/trait.Eq.js | 9 + pr/395/docs/trait.impl/core/cmp/trait.Ord.js | 9 + .../trait.impl/core/cmp/trait.PartialEq.js | 9 + .../trait.impl/core/cmp/trait.PartialOrd.js | 9 + .../trait.impl/core/convert/trait.From.js | 9 + .../trait.impl/core/convert/trait.TryFrom.js | 9 + .../trait.impl/core/default/trait.Default.js | 9 + .../docs/trait.impl/core/error/trait.Error.js | 9 + .../docs/trait.impl/core/fmt/trait.Debug.js | 9 + .../docs/trait.impl/core/fmt/trait.Display.js | 9 + .../core/future/future/trait.Future.js | 9 + .../future/into_future/trait.IntoFuture.js | 9 + .../docs/trait.impl/core/hash/trait.Hash.js | 9 + .../docs/trait.impl/core/marker/trait.Copy.js | 9 + .../trait.impl/core/marker/trait.Freeze.js | 9 + .../docs/trait.impl/core/marker/trait.Send.js | 9 + .../core/marker/trait.StructuralPartialEq.js | 9 + .../docs/trait.impl/core/marker/trait.Sync.js | 9 + .../trait.impl/core/marker/trait.Unpin.js | 9 + .../core/marker/trait.UnsafeUnpin.js | 9 + .../docs/trait.impl/core/ops/bit/trait.Not.js | 9 + .../trait.impl/core/ops/deref/trait.Deref.js | 9 + .../core/ops/deref/trait.DerefMut.js | 9 + .../trait.impl/core/ops/drop/trait.Drop.js | 9 + .../trait.impl/core/ops/index/trait.Index.js | 9 + .../core/ops/index/trait.IndexMut.js | 9 + .../panic/unwind_safe/trait.RefUnwindSafe.js | 9 + .../panic/unwind_safe/trait.UnwindSafe.js | 9 + .../core/str/traits/trait.FromStr.js | 9 + .../futures_core/stream/trait.Stream.js | 9 + .../futures_io/if_std/trait.AsyncRead.js | 9 + .../futures_io/if_std/trait.AsyncWrite.js | 9 + .../identity_hash/trait.IdentityHashable.js | 9 + .../iroh_quinn/runtime/trait.AsyncTimer.js | 9 + .../iroh_quinn/runtime/trait.Runtime.js | 9 + .../trait.ConnectionIdGenerator.js | 9 + .../coding/trait.Decodable.js | 9 + .../coding/trait.Encodable.js | 9 + .../config/qlog/trait.QlogFactory.js | 9 + .../config/trait.TimeSource.js | 9 + .../congestion/trait.Controller.js | 9 + .../congestion/trait.ControllerFactory.js | 9 + .../iroh_quinn_proto/crypto/trait.AeadKey.js | 9 + .../crypto/trait.ClientConfig.js | 9 + .../crypto/trait.HandshakeTokenKey.js | 9 + .../crypto/trait.HeaderKey.js | 9 + .../iroh_quinn_proto/crypto/trait.HmacKey.js | 9 + .../crypto/trait.PacketKey.js | 9 + .../crypto/trait.ServerConfig.js | 9 + .../iroh_quinn_proto/crypto/trait.Session.js | 9 + .../packet/trait.ConnectionIdParser.js | 9 + .../iroh_quinn_proto/token/trait.TokenLog.js | 9 + .../token/trait.TokenStore.js | 9 + .../rustls/verify/trait.ServerCertVerifier.js | 9 + .../tokio/io/async_read/trait.AsyncRead.js | 9 + .../tokio/io/async_write/trait.AsyncWrite.js | 9 + .../docs/type.impl/core/result/enum.Result.js | 9 + 907 files changed, 63828 insertions(+) create mode 100644 pr/395/docs/.lock create mode 100644 pr/395/docs/bench/all.html create mode 100644 pr/395/docs/bench/enum.CipherSuite.html create mode 100644 pr/395/docs/bench/fn.configure_tracing_subscriber.html create mode 100644 pr/395/docs/bench/fn.connect_client.html create mode 100644 pr/395/docs/bench/fn.drain_stream.html create mode 100644 pr/395/docs/bench/fn.rt.html create mode 100644 pr/395/docs/bench/fn.send_data_on_stream.html create mode 100644 pr/395/docs/bench/fn.server_endpoint.html create mode 100644 pr/395/docs/bench/fn.transport_config.html create mode 100644 pr/395/docs/bench/index.html create mode 100644 pr/395/docs/bench/sidebar-items.js create mode 100644 pr/395/docs/bench/stats/fn.throughput_bps.html create mode 100644 pr/395/docs/bench/stats/index.html create mode 100644 pr/395/docs/bench/stats/sidebar-items.js create mode 100644 pr/395/docs/bench/stats/struct.Stats.html create mode 100644 pr/395/docs/bench/stats/struct.StreamStats.html create mode 100644 pr/395/docs/bench/stats/struct.TransferResult.html create mode 100644 pr/395/docs/bench/struct.Opt.html create mode 100644 pr/395/docs/bulk/all.html create mode 100644 pr/395/docs/bulk/fn.client.html create mode 100644 pr/395/docs/bulk/fn.handle_client_stream.html create mode 100644 pr/395/docs/bulk/fn.main.html create mode 100644 pr/395/docs/bulk/fn.server.html create mode 100644 pr/395/docs/bulk/index.html create mode 100644 pr/395/docs/bulk/sidebar-items.js create mode 100644 pr/395/docs/bulk/struct.ClientStats.html create mode 100644 pr/395/docs/certificate/all.html create mode 100644 pr/395/docs/certificate/fn.configure_client.html create mode 100644 pr/395/docs/certificate/fn.generate_self_signed_cert.html create mode 100644 pr/395/docs/certificate/fn.main.html create mode 100644 pr/395/docs/certificate/fn.read_certs_from_file.html create mode 100644 pr/395/docs/certificate/index.html create mode 100644 pr/395/docs/certificate/sidebar-items.js create mode 100644 pr/395/docs/certificate/struct.SkipServerVerification.html create mode 100644 pr/395/docs/crates.js create mode 100644 pr/395/docs/data_transfer/all.html create mode 100644 pr/395/docs/data_transfer/fn.main.html create mode 100644 pr/395/docs/data_transfer/fn.open_bidirectional_stream.html create mode 100644 pr/395/docs/data_transfer/fn.open_unidirectional_stream.html create mode 100644 pr/395/docs/data_transfer/fn.receive_bidirectional_stream.html create mode 100644 pr/395/docs/data_transfer/fn.receive_datagram.html create mode 100644 pr/395/docs/data_transfer/fn.receive_unidirectional_stream.html create mode 100644 pr/395/docs/data_transfer/fn.send_unreliable.html create mode 100644 pr/395/docs/data_transfer/index.html create mode 100644 pr/395/docs/data_transfer/sidebar-items.js create mode 100644 pr/395/docs/help.html create mode 100644 pr/395/docs/iroh_quinn/all.html create mode 100644 pr/395/docs/iroh_quinn/congestion/index.html create mode 100644 pr/395/docs/iroh_quinn/congestion/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn/congestion/struct.Bbr.html create mode 100644 pr/395/docs/iroh_quinn/congestion/struct.BbrConfig.html create mode 100644 pr/395/docs/iroh_quinn/congestion/struct.ControllerMetrics.html create mode 100644 pr/395/docs/iroh_quinn/congestion/struct.Cubic.html create mode 100644 pr/395/docs/iroh_quinn/congestion/struct.CubicConfig.html create mode 100644 pr/395/docs/iroh_quinn/congestion/struct.NewReno.html create mode 100644 pr/395/docs/iroh_quinn/congestion/struct.NewRenoConfig.html create mode 100644 pr/395/docs/iroh_quinn/congestion/trait.Controller.html create mode 100644 pr/395/docs/iroh_quinn/congestion/trait.ControllerFactory.html create mode 100644 pr/395/docs/iroh_quinn/connection/enum.SendDatagramError.html create mode 100644 pr/395/docs/iroh_quinn/connection/struct.AcceptBi.html create mode 100644 pr/395/docs/iroh_quinn/connection/struct.AcceptUni.html create mode 100644 pr/395/docs/iroh_quinn/connection/struct.Connecting.html create mode 100644 pr/395/docs/iroh_quinn/connection/struct.Connection.html create mode 100644 pr/395/docs/iroh_quinn/connection/struct.OnClosed.html create mode 100644 pr/395/docs/iroh_quinn/connection/struct.OpenBi.html create mode 100644 pr/395/docs/iroh_quinn/connection/struct.OpenUni.html create mode 100644 pr/395/docs/iroh_quinn/connection/struct.ReadDatagram.html create mode 100644 pr/395/docs/iroh_quinn/connection/struct.SendDatagram.html create mode 100644 pr/395/docs/iroh_quinn/connection/struct.WeakConnectionHandle.html create mode 100644 pr/395/docs/iroh_quinn/connection/struct.ZeroRttAccepted.html create mode 100644 pr/395/docs/iroh_quinn/crypto/index.html create mode 100644 pr/395/docs/iroh_quinn/crypto/rustls/enum.Error.html create mode 100644 pr/395/docs/iroh_quinn/crypto/rustls/index.html create mode 100644 pr/395/docs/iroh_quinn/crypto/rustls/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn/crypto/rustls/struct.HandshakeData.html create mode 100644 pr/395/docs/iroh_quinn/crypto/rustls/struct.NoInitialCipherSuite.html create mode 100644 pr/395/docs/iroh_quinn/crypto/rustls/struct.QuicClientConfig.html create mode 100644 pr/395/docs/iroh_quinn/crypto/rustls/struct.QuicServerConfig.html create mode 100644 pr/395/docs/iroh_quinn/crypto/rustls/struct.TlsSession.html create mode 100644 pr/395/docs/iroh_quinn/crypto/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn/crypto/struct.CryptoError.html create mode 100644 pr/395/docs/iroh_quinn/crypto/struct.ExportKeyingMaterialError.html create mode 100644 pr/395/docs/iroh_quinn/crypto/struct.KeyPair.html create mode 100644 pr/395/docs/iroh_quinn/crypto/struct.Keys.html create mode 100644 pr/395/docs/iroh_quinn/crypto/struct.UnsupportedVersion.html create mode 100644 pr/395/docs/iroh_quinn/crypto/trait.AeadKey.html create mode 100644 pr/395/docs/iroh_quinn/crypto/trait.ClientConfig.html create mode 100644 pr/395/docs/iroh_quinn/crypto/trait.HandshakeTokenKey.html create mode 100644 pr/395/docs/iroh_quinn/crypto/trait.HeaderKey.html create mode 100644 pr/395/docs/iroh_quinn/crypto/trait.HmacKey.html create mode 100644 pr/395/docs/iroh_quinn/crypto/trait.PacketKey.html create mode 100644 pr/395/docs/iroh_quinn/crypto/trait.ServerConfig.html create mode 100644 pr/395/docs/iroh_quinn/crypto/trait.Session.html create mode 100644 pr/395/docs/iroh_quinn/endpoint/struct.Accept.html create mode 100644 pr/395/docs/iroh_quinn/endpoint/struct.Endpoint.html create mode 100644 pr/395/docs/iroh_quinn/endpoint/struct.EndpointStats.html create mode 100644 pr/395/docs/iroh_quinn/enum.ConfigError.html create mode 100644 pr/395/docs/iroh_quinn/enum.ConnectError.html create mode 100644 pr/395/docs/iroh_quinn/enum.ConnectionError.html create mode 100644 pr/395/docs/iroh_quinn/enum.Dir.html create mode 100644 pr/395/docs/iroh_quinn/enum.EcnCodepoint.html create mode 100644 pr/395/docs/iroh_quinn/enum.FrameType.html create mode 100644 pr/395/docs/iroh_quinn/enum.ReadError.html create mode 100644 pr/395/docs/iroh_quinn/enum.ReadExactError.html create mode 100644 pr/395/docs/iroh_quinn/enum.ReadToEndError.html create mode 100644 pr/395/docs/iroh_quinn/enum.ResetError.html create mode 100644 pr/395/docs/iroh_quinn/enum.SendDatagramError.html create mode 100644 pr/395/docs/iroh_quinn/enum.Side.html create mode 100644 pr/395/docs/iroh_quinn/enum.StoppedError.html create mode 100644 pr/395/docs/iroh_quinn/enum.WriteError.html create mode 100644 pr/395/docs/iroh_quinn/fn.default_runtime.html create mode 100644 pr/395/docs/iroh_quinn/incoming/struct.Incoming.html create mode 100644 pr/395/docs/iroh_quinn/incoming/struct.IncomingFuture.html create mode 100644 pr/395/docs/iroh_quinn/incoming/struct.RetryError.html create mode 100644 pr/395/docs/iroh_quinn/index.html create mode 100644 pr/395/docs/iroh_quinn/path/struct.AddressDiscovery.html create mode 100644 pr/395/docs/iroh_quinn/path/struct.ClosePath.html create mode 100644 pr/395/docs/iroh_quinn/path/struct.OpenPath.html create mode 100644 pr/395/docs/iroh_quinn/path/struct.Path.html create mode 100644 pr/395/docs/iroh_quinn/recv_stream/enum.ReadError.html create mode 100644 pr/395/docs/iroh_quinn/recv_stream/enum.ReadExactError.html create mode 100644 pr/395/docs/iroh_quinn/recv_stream/enum.ReadToEndError.html create mode 100644 pr/395/docs/iroh_quinn/recv_stream/enum.ResetError.html create mode 100644 pr/395/docs/iroh_quinn/recv_stream/struct.RecvStream.html create mode 100644 pr/395/docs/iroh_quinn/recv_stream/struct.UnorderedRecvStream.html create mode 100644 pr/395/docs/iroh_quinn/runtime/fn.default_runtime.html create mode 100644 pr/395/docs/iroh_quinn/runtime/smol/struct.SmolRuntime.html create mode 100644 pr/395/docs/iroh_quinn/runtime/tokio/struct.TokioRuntime.html create mode 100644 pr/395/docs/iroh_quinn/runtime/trait.AsyncTimer.html create mode 100644 pr/395/docs/iroh_quinn/runtime/trait.AsyncUdpSocket.html create mode 100644 pr/395/docs/iroh_quinn/runtime/trait.Runtime.html create mode 100644 pr/395/docs/iroh_quinn/runtime/trait.UdpSender.html create mode 100644 pr/395/docs/iroh_quinn/send_stream/enum.StoppedError.html create mode 100644 pr/395/docs/iroh_quinn/send_stream/enum.WriteError.html create mode 100644 pr/395/docs/iroh_quinn/send_stream/struct.SendStream.html create mode 100644 pr/395/docs/iroh_quinn/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn/struct.Accept.html create mode 100644 pr/395/docs/iroh_quinn/struct.AcceptBi.html create mode 100644 pr/395/docs/iroh_quinn/struct.AcceptUni.html create mode 100644 pr/395/docs/iroh_quinn/struct.AckFrequencyConfig.html create mode 100644 pr/395/docs/iroh_quinn/struct.AddressDiscovery.html create mode 100644 pr/395/docs/iroh_quinn/struct.ApplicationClose.html create mode 100644 pr/395/docs/iroh_quinn/struct.BloomTokenLog.html create mode 100644 pr/395/docs/iroh_quinn/struct.Chunk.html create mode 100644 pr/395/docs/iroh_quinn/struct.ClientConfig.html create mode 100644 pr/395/docs/iroh_quinn/struct.ClosePath.html create mode 100644 pr/395/docs/iroh_quinn/struct.ClosedStream.html create mode 100644 pr/395/docs/iroh_quinn/struct.Connecting.html create mode 100644 pr/395/docs/iroh_quinn/struct.Connection.html create mode 100644 pr/395/docs/iroh_quinn/struct.ConnectionClose.html create mode 100644 pr/395/docs/iroh_quinn/struct.ConnectionId.html create mode 100644 pr/395/docs/iroh_quinn/struct.ConnectionStats.html create mode 100644 pr/395/docs/iroh_quinn/struct.Endpoint.html create mode 100644 pr/395/docs/iroh_quinn/struct.EndpointConfig.html create mode 100644 pr/395/docs/iroh_quinn/struct.EndpointStats.html create mode 100644 pr/395/docs/iroh_quinn/struct.FrameStats.html create mode 100644 pr/395/docs/iroh_quinn/struct.IdleTimeout.html create mode 100644 pr/395/docs/iroh_quinn/struct.Incoming.html create mode 100644 pr/395/docs/iroh_quinn/struct.IncomingFuture.html create mode 100644 pr/395/docs/iroh_quinn/struct.InvalidCid.html create mode 100644 pr/395/docs/iroh_quinn/struct.MtuDiscoveryConfig.html create mode 100644 pr/395/docs/iroh_quinn/struct.NoneTokenLog.html create mode 100644 pr/395/docs/iroh_quinn/struct.NoneTokenStore.html create mode 100644 pr/395/docs/iroh_quinn/struct.OnClosed.html create mode 100644 pr/395/docs/iroh_quinn/struct.OpenBi.html create mode 100644 pr/395/docs/iroh_quinn/struct.OpenPath.html create mode 100644 pr/395/docs/iroh_quinn/struct.OpenUni.html create mode 100644 pr/395/docs/iroh_quinn/struct.Path.html create mode 100644 pr/395/docs/iroh_quinn/struct.PathId.html create mode 100644 pr/395/docs/iroh_quinn/struct.PathStats.html create mode 100644 pr/395/docs/iroh_quinn/struct.QlogConfig.html create mode 100644 pr/395/docs/iroh_quinn/struct.QlogFileFactory.html create mode 100644 pr/395/docs/iroh_quinn/struct.ReadDatagram.html create mode 100644 pr/395/docs/iroh_quinn/struct.RecvStream.html create mode 100644 pr/395/docs/iroh_quinn/struct.RetryError.html create mode 100644 pr/395/docs/iroh_quinn/struct.SendDatagram.html create mode 100644 pr/395/docs/iroh_quinn/struct.SendStream.html create mode 100644 pr/395/docs/iroh_quinn/struct.ServerConfig.html create mode 100644 pr/395/docs/iroh_quinn/struct.SmolRuntime.html create mode 100644 pr/395/docs/iroh_quinn/struct.StdSystemTime.html create mode 100644 pr/395/docs/iroh_quinn/struct.StreamId.html create mode 100644 pr/395/docs/iroh_quinn/struct.TokenMemoryCache.html create mode 100644 pr/395/docs/iroh_quinn/struct.TokenReuseError.html create mode 100644 pr/395/docs/iroh_quinn/struct.TokioRuntime.html create mode 100644 pr/395/docs/iroh_quinn/struct.Transmit.html create mode 100644 pr/395/docs/iroh_quinn/struct.TransportConfig.html create mode 100644 pr/395/docs/iroh_quinn/struct.TransportErrorCode.html create mode 100644 pr/395/docs/iroh_quinn/struct.UdpStats.html create mode 100644 pr/395/docs/iroh_quinn/struct.UnorderedRecvStream.html create mode 100644 pr/395/docs/iroh_quinn/struct.ValidationTokenConfig.html create mode 100644 pr/395/docs/iroh_quinn/struct.VarInt.html create mode 100644 pr/395/docs/iroh_quinn/struct.VarIntBoundsExceeded.html create mode 100644 pr/395/docs/iroh_quinn/struct.WeakConnectionHandle.html create mode 100644 pr/395/docs/iroh_quinn/struct.Written.html create mode 100644 pr/395/docs/iroh_quinn/struct.ZeroRttAccepted.html create mode 100644 pr/395/docs/iroh_quinn/trait.AsyncTimer.html create mode 100644 pr/395/docs/iroh_quinn/trait.AsyncUdpSocket.html create mode 100644 pr/395/docs/iroh_quinn/trait.ConnectionIdGenerator.html create mode 100644 pr/395/docs/iroh_quinn/trait.QlogFactory.html create mode 100644 pr/395/docs/iroh_quinn/trait.Runtime.html create mode 100644 pr/395/docs/iroh_quinn/trait.TimeSource.html create mode 100644 pr/395/docs/iroh_quinn/trait.TokenLog.html create mode 100644 pr/395/docs/iroh_quinn/trait.TokenStore.html create mode 100644 pr/395/docs/iroh_quinn/trait.UdpSender.html create mode 100644 pr/395/docs/iroh_quinn_proto/all.html create mode 100644 pr/395/docs/iroh_quinn_proto/bench_exports/index.html create mode 100644 pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html create mode 100644 pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html create mode 100644 pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/index.html create mode 100644 pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn_proto/bench_exports/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn_proto/bloom_token_log/struct.BloomTokenLog.html create mode 100644 pr/395/docs/iroh_quinn_proto/cid_generator/struct.HashedConnectionIdGenerator.html create mode 100644 pr/395/docs/iroh_quinn_proto/cid_generator/struct.InvalidCid.html create mode 100644 pr/395/docs/iroh_quinn_proto/cid_generator/struct.RandomConnectionIdGenerator.html create mode 100644 pr/395/docs/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.html create mode 100644 pr/395/docs/iroh_quinn_proto/coding/index.html create mode 100644 pr/395/docs/iroh_quinn_proto/coding/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html create mode 100644 pr/395/docs/iroh_quinn_proto/coding/trait.Decodable.html create mode 100644 pr/395/docs/iroh_quinn_proto/coding/trait.Encodable.html create mode 100644 pr/395/docs/iroh_quinn_proto/coding/type.Result.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/enum.ConfigError.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/qlog/struct.QlogConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/qlog/struct.QlogFileFactory.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/qlog/trait.QlogFactory.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/struct.ClientConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/struct.EndpointConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/struct.ServerConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/struct.StdSystemTime.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/struct.ValidationTokenConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/trait.TimeSource.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/transport/struct.AckFrequencyConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/transport/struct.IdleTimeout.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/transport/struct.MtuDiscoveryConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/config/transport/struct.TransportConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/bbr/struct.Bbr.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/bbr/struct.BbrConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/cubic/struct.Cubic.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/cubic/struct.CubicConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/index.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/new_reno/struct.NewReno.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/new_reno/struct.NewRenoConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/struct.Bbr.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/struct.BbrConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/struct.ControllerMetrics.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/struct.Cubic.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/struct.CubicConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/struct.NewReno.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/struct.NewRenoConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/trait.Controller.html create mode 100644 pr/395/docs/iroh_quinn_proto/congestion/trait.ControllerFactory.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/assembler/struct.Chunk.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/datagrams/enum.SendDatagramError.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/datagrams/struct.Datagrams.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/enum.ClosePathError.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/enum.ConnectionError.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/enum.Event.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/enum.PathError.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/paths/enum.PathEvent.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/paths/enum.PathStatus.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/paths/enum.SetPathStatusError.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/paths/struct.ClosedPath.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/paths/struct.PathId.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/paths/struct.RttEstimator.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/fn.get_into_many_segments.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/fn.get_loop_many_segments.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/index.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/stats/struct.ConnectionStats.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/stats/struct.FrameStats.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/stats/struct.PathStats.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/stats/struct.UdpStats.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/streams/enum.StreamEvent.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadError.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadableError.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/streams/recv/struct.Chunks.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/streams/send/enum.FinishError.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/streams/send/enum.WriteError.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/streams/send/struct.Written.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/streams/struct.ClosedStream.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/streams/struct.RecvStream.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/streams/struct.SendStream.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/streams/struct.ShouldTransmit.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/streams/struct.Streams.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/struct.Connection.html create mode 100644 pr/395/docs/iroh_quinn_proto/connection/struct.MultipathNotNegotiated.html create mode 100644 pr/395/docs/iroh_quinn_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html create mode 100644 pr/395/docs/iroh_quinn_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/index.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/rustls/enum.Error.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/rustls/index.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/rustls/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.HandshakeData.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.NoInitialCipherSuite.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.QuicClientConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.QuicServerConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.TlsSession.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/struct.CryptoError.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/struct.ExportKeyingMaterialError.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/struct.KeyPair.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/struct.Keys.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/struct.UnsupportedVersion.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/trait.AeadKey.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/trait.ClientConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/trait.HeaderKey.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/trait.HmacKey.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/trait.PacketKey.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/trait.ServerConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/crypto/trait.Session.html create mode 100644 pr/395/docs/iroh_quinn_proto/endpoint/enum.ConnectError.html create mode 100644 pr/395/docs/iroh_quinn_proto/endpoint/enum.DatagramEvent.html create mode 100644 pr/395/docs/iroh_quinn_proto/endpoint/struct.AcceptError.html create mode 100644 pr/395/docs/iroh_quinn_proto/endpoint/struct.ConnectionHandle.html create mode 100644 pr/395/docs/iroh_quinn_proto/endpoint/struct.Endpoint.html create mode 100644 pr/395/docs/iroh_quinn_proto/endpoint/struct.Incoming.html create mode 100644 pr/395/docs/iroh_quinn_proto/endpoint/struct.RetryError.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.ClosePathError.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.ConfigError.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.ConnectError.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.ConnectionError.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.DatagramEvent.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.Dir.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.EcnCodepoint.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.Event.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.FinishError.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.FrameType.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.LongType.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.MaybeFrame.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.PacketDecodeError.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.PathError.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.PathEvent.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.PathStatus.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.ProtectedHeader.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.ReadError.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.ReadableError.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.SendDatagramError.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.SetPathStatusError.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.Side.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.StreamEvent.html create mode 100644 pr/395/docs/iroh_quinn_proto/enum.WriteError.html create mode 100644 pr/395/docs/iroh_quinn_proto/frame/enum.FrameType.html create mode 100644 pr/395/docs/iroh_quinn_proto/frame/enum.MaybeFrame.html create mode 100644 pr/395/docs/iroh_quinn_proto/frame/struct.ApplicationClose.html create mode 100644 pr/395/docs/iroh_quinn_proto/frame/struct.ConnectionClose.html create mode 100644 pr/395/docs/iroh_quinn_proto/frame/struct.Datagram.html create mode 100644 pr/395/docs/iroh_quinn_proto/frame/struct.DatagramInfo.html create mode 100644 pr/395/docs/iroh_quinn_proto/frame/struct.InvalidFrameId.html create mode 100644 pr/395/docs/iroh_quinn_proto/frame/struct.StreamInfo.html create mode 100644 pr/395/docs/iroh_quinn_proto/index.html create mode 100644 pr/395/docs/iroh_quinn_proto/iroh_hp/enum.Error.html create mode 100644 pr/395/docs/iroh_quinn_proto/iroh_hp/enum.Event.html create mode 100644 pr/395/docs/iroh_quinn_proto/iroh_hp/index.html create mode 100644 pr/395/docs/iroh_quinn_proto/iroh_hp/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn_proto/packet/enum.LongType.html create mode 100644 pr/395/docs/iroh_quinn_proto/packet/enum.PacketDecodeError.html create mode 100644 pr/395/docs/iroh_quinn_proto/packet/enum.ProtectedHeader.html create mode 100644 pr/395/docs/iroh_quinn_proto/packet/struct.FixedLengthConnectionIdParser.html create mode 100644 pr/395/docs/iroh_quinn_proto/packet/struct.PartialDecode.html create mode 100644 pr/395/docs/iroh_quinn_proto/packet/struct.ProtectedInitialHeader.html create mode 100644 pr/395/docs/iroh_quinn_proto/packet/trait.ConnectionIdParser.html create mode 100644 pr/395/docs/iroh_quinn_proto/shared/enum.EcnCodepoint.html create mode 100644 pr/395/docs/iroh_quinn_proto/shared/struct.ConnectionEvent.html create mode 100644 pr/395/docs/iroh_quinn_proto/shared/struct.ConnectionId.html create mode 100644 pr/395/docs/iroh_quinn_proto/shared/struct.EndpointEvent.html create mode 100644 pr/395/docs/iroh_quinn_proto/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn_proto/struct.AcceptError.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.AckFrequencyConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.ApplicationClose.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.BloomTokenLog.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.Chunk.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.Chunks.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.ClientConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.ClosedPath.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.ClosedStream.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.Connection.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.ConnectionClose.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.ConnectionEvent.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.ConnectionHandle.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.ConnectionId.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.ConnectionStats.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.Datagram.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.DatagramInfo.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.Datagrams.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.Endpoint.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.EndpointConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.EndpointEvent.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.FourTuple.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.FrameStats.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.HashedConnectionIdGenerator.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.IdleTimeout.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.Incoming.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.InvalidCid.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.InvalidFrameId.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.MtuDiscoveryConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.MultipathNotNegotiated.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.NoneTokenLog.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.NoneTokenStore.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.PartialDecode.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.PathId.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.PathStats.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.QlogConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.QlogFileFactory.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.RandomConnectionIdGenerator.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.RecvStream.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.RetryError.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.RttEstimator.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.SendStream.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.ServerConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.ShouldTransmit.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.StdSystemTime.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.StreamId.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.StreamInfo.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.Streams.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.TokenMemoryCache.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.TokenReuseError.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.Transmit.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.TransportConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.TransportError.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.TransportErrorCode.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.UdpStats.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.ValidationTokenConfig.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.VarInt.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.VarIntBoundsExceeded.html create mode 100644 pr/395/docs/iroh_quinn_proto/struct.Written.html create mode 100644 pr/395/docs/iroh_quinn_proto/token/struct.NoneTokenLog.html create mode 100644 pr/395/docs/iroh_quinn_proto/token/struct.NoneTokenStore.html create mode 100644 pr/395/docs/iroh_quinn_proto/token/struct.TokenReuseError.html create mode 100644 pr/395/docs/iroh_quinn_proto/token/trait.TokenLog.html create mode 100644 pr/395/docs/iroh_quinn_proto/token/trait.TokenStore.html create mode 100644 pr/395/docs/iroh_quinn_proto/token_memory_cache/struct.TokenMemoryCache.html create mode 100644 pr/395/docs/iroh_quinn_proto/trait.ConnectionIdGenerator.html create mode 100644 pr/395/docs/iroh_quinn_proto/trait.ConnectionIdParser.html create mode 100644 pr/395/docs/iroh_quinn_proto/trait.QlogFactory.html create mode 100644 pr/395/docs/iroh_quinn_proto/trait.TimeSource.html create mode 100644 pr/395/docs/iroh_quinn_proto/trait.TokenLog.html create mode 100644 pr/395/docs/iroh_quinn_proto/trait.TokenStore.html create mode 100644 pr/395/docs/iroh_quinn_proto/transport_error/struct.Code.html create mode 100644 pr/395/docs/iroh_quinn_proto/transport_error/struct.Error.html create mode 100644 pr/395/docs/iroh_quinn_proto/transport_parameters/enum.Error.html create mode 100644 pr/395/docs/iroh_quinn_proto/transport_parameters/index.html create mode 100644 pr/395/docs/iroh_quinn_proto/transport_parameters/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn_proto/transport_parameters/struct.TransportParameters.html create mode 100644 pr/395/docs/iroh_quinn_proto/varint/struct.VarInt.html create mode 100644 pr/395/docs/iroh_quinn_proto/varint/struct.VarIntBoundsExceeded.html create mode 100644 pr/395/docs/iroh_quinn_udp/all.html create mode 100644 pr/395/docs/iroh_quinn_udp/constant.BATCH_SIZE.html create mode 100644 pr/395/docs/iroh_quinn_udp/enum.EcnCodepoint.html create mode 100644 pr/395/docs/iroh_quinn_udp/imp/struct.UdpSocketState.html create mode 100644 pr/395/docs/iroh_quinn_udp/index.html create mode 100644 pr/395/docs/iroh_quinn_udp/sidebar-items.js create mode 100644 pr/395/docs/iroh_quinn_udp/struct.RecvMeta.html create mode 100644 pr/395/docs/iroh_quinn_udp/struct.Transmit.html create mode 100644 pr/395/docs/iroh_quinn_udp/struct.UdpSockRef.html create mode 100644 pr/395/docs/iroh_quinn_udp/struct.UdpSocketState.html create mode 100644 pr/395/docs/perf/all.html create mode 100644 pr/395/docs/perf/client/fn.run.html create mode 100644 pr/395/docs/perf/client/index.html create mode 100644 pr/395/docs/perf/client/sidebar-items.js create mode 100644 pr/395/docs/perf/client/struct.Opt.html create mode 100644 pr/395/docs/perf/enum.CongestionAlgorithm.html create mode 100644 pr/395/docs/perf/fn.parse_byte_size.html create mode 100644 pr/395/docs/perf/index.html create mode 100644 pr/395/docs/perf/noprotection/index.html create mode 100644 pr/395/docs/perf/noprotection/sidebar-items.js create mode 100644 pr/395/docs/perf/noprotection/struct.NoProtectionClientConfig.html create mode 100644 pr/395/docs/perf/noprotection/struct.NoProtectionServerConfig.html create mode 100644 pr/395/docs/perf/server/fn.run.html create mode 100644 pr/395/docs/perf/server/index.html create mode 100644 pr/395/docs/perf/server/sidebar-items.js create mode 100644 pr/395/docs/perf/server/struct.Opt.html create mode 100644 pr/395/docs/perf/sidebar-items.js create mode 100644 pr/395/docs/perf/static.PERF_CIPHER_SUITES.html create mode 100644 pr/395/docs/perf/stats/index.html create mode 100644 pr/395/docs/perf/stats/sidebar-items.js create mode 100644 pr/395/docs/perf/stats/struct.OpenStreamStats.html create mode 100644 pr/395/docs/perf/stats/struct.Stats.html create mode 100644 pr/395/docs/perf/stats/struct.StreamStats.html create mode 100644 pr/395/docs/perf/struct.CommonOpt.html create mode 100644 pr/395/docs/quinn_perf/all.html create mode 100644 pr/395/docs/quinn_perf/enum.Commands.html create mode 100644 pr/395/docs/quinn_perf/fn.main.html create mode 100644 pr/395/docs/quinn_perf/index.html create mode 100644 pr/395/docs/quinn_perf/sidebar-items.js create mode 100644 pr/395/docs/quinn_perf/struct.Cli.html create mode 100644 pr/395/docs/search.index/0022edf8729b.js create mode 100644 pr/395/docs/search.index/0051e5fe15da.js create mode 100644 pr/395/docs/search.index/009757435c6f.js create mode 100644 pr/395/docs/search.index/00aa835204e9.js create mode 100644 pr/395/docs/search.index/01c8653e6e9f.js create mode 100644 pr/395/docs/search.index/02bba2f3bb15.js create mode 100644 pr/395/docs/search.index/0515792bb474.js create mode 100644 pr/395/docs/search.index/056473b4cdef.js create mode 100644 pr/395/docs/search.index/06205a12fd8a.js create mode 100644 pr/395/docs/search.index/062983155a37.js create mode 100644 pr/395/docs/search.index/06edb61cc89f.js create mode 100644 pr/395/docs/search.index/070e1accd154.js create mode 100644 pr/395/docs/search.index/07329e64a5f0.js create mode 100644 pr/395/docs/search.index/077a08ac2280.js create mode 100644 pr/395/docs/search.index/085d7a8021d6.js create mode 100644 pr/395/docs/search.index/097a8c9da4ad.js create mode 100644 pr/395/docs/search.index/09c34cd06079.js create mode 100644 pr/395/docs/search.index/0a0d55e0322f.js create mode 100644 pr/395/docs/search.index/0ab68eae012d.js create mode 100644 pr/395/docs/search.index/0c67f8980f27.js create mode 100644 pr/395/docs/search.index/0c6d6b68fc28.js create mode 100644 pr/395/docs/search.index/0ca033ef2465.js create mode 100644 pr/395/docs/search.index/0d27f25fe856.js create mode 100644 pr/395/docs/search.index/0d76b2553bfd.js create mode 100644 pr/395/docs/search.index/0d7e3bfebb98.js create mode 100644 pr/395/docs/search.index/0dec9e618431.js create mode 100644 pr/395/docs/search.index/0f04eaaf1f31.js create mode 100644 pr/395/docs/search.index/0f80e54f3f36.js create mode 100644 pr/395/docs/search.index/10696d58fab9.js create mode 100644 pr/395/docs/search.index/106bd6af91ad.js create mode 100644 pr/395/docs/search.index/10dca90977e7.js create mode 100644 pr/395/docs/search.index/10e28db8e153.js create mode 100644 pr/395/docs/search.index/10fa6d29f4f2.js create mode 100644 pr/395/docs/search.index/115053be1aee.js create mode 100644 pr/395/docs/search.index/11ecde640804.js create mode 100644 pr/395/docs/search.index/122837952335.js create mode 100644 pr/395/docs/search.index/1257c4864088.js create mode 100644 pr/395/docs/search.index/1298ab4d6c58.js create mode 100644 pr/395/docs/search.index/12e5c196480d.js create mode 100644 pr/395/docs/search.index/13c59bf23082.js create mode 100644 pr/395/docs/search.index/14148cabdb79.js create mode 100644 pr/395/docs/search.index/14351f4f78de.js create mode 100644 pr/395/docs/search.index/1490375c9199.js create mode 100644 pr/395/docs/search.index/14bb3c65ae19.js create mode 100644 pr/395/docs/search.index/173afb185531.js create mode 100644 pr/395/docs/search.index/1754ac96d54a.js create mode 100644 pr/395/docs/search.index/175b4e0f835c.js create mode 100644 pr/395/docs/search.index/176725a3f611.js create mode 100644 pr/395/docs/search.index/1779ad771958.js create mode 100644 pr/395/docs/search.index/17830149a792.js create mode 100644 pr/395/docs/search.index/182b6e2dbd4a.js create mode 100644 pr/395/docs/search.index/1889fd721cb5.js create mode 100644 pr/395/docs/search.index/18ccfd1e3472.js create mode 100644 pr/395/docs/search.index/196b6ab52bc1.js create mode 100644 pr/395/docs/search.index/1a14b9a7d2f6.js create mode 100644 pr/395/docs/search.index/1b113bad2321.js create mode 100644 pr/395/docs/search.index/1b5c0368d221.js create mode 100644 pr/395/docs/search.index/1c404dc1a453.js create mode 100644 pr/395/docs/search.index/1d8c01ecbe8e.js create mode 100644 pr/395/docs/search.index/1e253efce535.js create mode 100644 pr/395/docs/search.index/1e45b5c47ee4.js create mode 100644 pr/395/docs/search.index/1e521ee4a588.js create mode 100644 pr/395/docs/search.index/1ebda14005f5.js create mode 100644 pr/395/docs/search.index/1f4506df5023.js create mode 100644 pr/395/docs/search.index/220c15e98816.js create mode 100644 pr/395/docs/search.index/225e079aa968.js create mode 100644 pr/395/docs/search.index/25add3d45510.js create mode 100644 pr/395/docs/search.index/25c8d0469b0f.js create mode 100644 pr/395/docs/search.index/263fb2099b3d.js create mode 100644 pr/395/docs/search.index/269534fbdbd0.js create mode 100644 pr/395/docs/search.index/283059613ad2.js create mode 100644 pr/395/docs/search.index/287195228062.js create mode 100644 pr/395/docs/search.index/288ad6e859a7.js create mode 100644 pr/395/docs/search.index/2937f2ef82d6.js create mode 100644 pr/395/docs/search.index/29d3c2222f61.js create mode 100644 pr/395/docs/search.index/2a562ad3f29f.js create mode 100644 pr/395/docs/search.index/2aa2debf8ed5.js create mode 100644 pr/395/docs/search.index/2aaca7f8de58.js create mode 100644 pr/395/docs/search.index/2b2167e7f8b2.js create mode 100644 pr/395/docs/search.index/2b7cfd46cb26.js create mode 100644 pr/395/docs/search.index/2c24473cb3e9.js create mode 100644 pr/395/docs/search.index/2c46939b6ca2.js create mode 100644 pr/395/docs/search.index/2dd8fd656c06.js create mode 100644 pr/395/docs/search.index/2eb373ad3c24.js create mode 100644 pr/395/docs/search.index/2f71766d07cc.js create mode 100644 pr/395/docs/search.index/301f4a70d960.js create mode 100644 pr/395/docs/search.index/3071a6cda431.js create mode 100644 pr/395/docs/search.index/30f9df4e9837.js create mode 100644 pr/395/docs/search.index/30ffbfdd11c6.js create mode 100644 pr/395/docs/search.index/31340ca2241e.js create mode 100644 pr/395/docs/search.index/3210ce3940fa.js create mode 100644 pr/395/docs/search.index/3240004448f4.js create mode 100644 pr/395/docs/search.index/32b9b5daf9e8.js create mode 100644 pr/395/docs/search.index/32ee32d485a7.js create mode 100644 pr/395/docs/search.index/3362c9bd9554.js create mode 100644 pr/395/docs/search.index/336f27a85aec.js create mode 100644 pr/395/docs/search.index/343b00f6b1cb.js create mode 100644 pr/395/docs/search.index/34a03cbb2682.js create mode 100644 pr/395/docs/search.index/34b69ebbda67.js create mode 100644 pr/395/docs/search.index/34cae06d030c.js create mode 100644 pr/395/docs/search.index/352927a38cef.js create mode 100644 pr/395/docs/search.index/36d2aad2bbc2.js create mode 100644 pr/395/docs/search.index/37448ddeb53e.js create mode 100644 pr/395/docs/search.index/37e6d63af90e.js create mode 100644 pr/395/docs/search.index/37f48bae88e4.js create mode 100644 pr/395/docs/search.index/380e3640e532.js create mode 100644 pr/395/docs/search.index/3848f16ce3c2.js create mode 100644 pr/395/docs/search.index/386ab9ff3804.js create mode 100644 pr/395/docs/search.index/3882b0299479.js create mode 100644 pr/395/docs/search.index/38f2efb5b65e.js create mode 100644 pr/395/docs/search.index/39ac769ad27b.js create mode 100644 pr/395/docs/search.index/3aa6d9572c12.js create mode 100644 pr/395/docs/search.index/3be462b0d192.js create mode 100644 pr/395/docs/search.index/3c5511ca849c.js create mode 100644 pr/395/docs/search.index/3e13ecd45e17.js create mode 100644 pr/395/docs/search.index/3e1b601c727f.js create mode 100644 pr/395/docs/search.index/3ea2a9f02ef8.js create mode 100644 pr/395/docs/search.index/3ef16a8006fc.js create mode 100644 pr/395/docs/search.index/3f496fd7d7dd.js create mode 100644 pr/395/docs/search.index/3f4f416f647f.js create mode 100644 pr/395/docs/search.index/3fa2f95c1020.js create mode 100644 pr/395/docs/search.index/410cbfd39641.js create mode 100644 pr/395/docs/search.index/4248fe585b27.js create mode 100644 pr/395/docs/search.index/4375a073b2db.js create mode 100644 pr/395/docs/search.index/45f6ee715dad.js create mode 100644 pr/395/docs/search.index/474d1a590443.js create mode 100644 pr/395/docs/search.index/483e10d0fb1a.js create mode 100644 pr/395/docs/search.index/485aace276e9.js create mode 100644 pr/395/docs/search.index/49793047756c.js create mode 100644 pr/395/docs/search.index/4ae488f26dde.js create mode 100644 pr/395/docs/search.index/4b97e0c80ad3.js create mode 100644 pr/395/docs/search.index/4c180bbff966.js create mode 100644 pr/395/docs/search.index/4cafff0b91f7.js create mode 100644 pr/395/docs/search.index/4d6048f07ed5.js create mode 100644 pr/395/docs/search.index/4d8a1f849ace.js create mode 100644 pr/395/docs/search.index/4da0028e700f.js create mode 100644 pr/395/docs/search.index/4f541acf0097.js create mode 100644 pr/395/docs/search.index/4fa3f1ec9b83.js create mode 100644 pr/395/docs/search.index/4fa8c75c3f45.js create mode 100644 pr/395/docs/search.index/503d89ddb639.js create mode 100644 pr/395/docs/search.index/50a025b610c7.js create mode 100644 pr/395/docs/search.index/510029c366bd.js create mode 100644 pr/395/docs/search.index/511f426cf452.js create mode 100644 pr/395/docs/search.index/51be0345651c.js create mode 100644 pr/395/docs/search.index/51fb0e67a5a5.js create mode 100644 pr/395/docs/search.index/534cb5ffe8b1.js create mode 100644 pr/395/docs/search.index/5489a4fd5a52.js create mode 100644 pr/395/docs/search.index/54a0f4bbac45.js create mode 100644 pr/395/docs/search.index/550f724dbd52.js create mode 100644 pr/395/docs/search.index/551174c27cfd.js create mode 100644 pr/395/docs/search.index/55d74f9a517a.js create mode 100644 pr/395/docs/search.index/55dc7f8c9f93.js create mode 100644 pr/395/docs/search.index/567c4dff1ab9.js create mode 100644 pr/395/docs/search.index/56fd0f0047d0.js create mode 100644 pr/395/docs/search.index/573cd35ae6b3.js create mode 100644 pr/395/docs/search.index/57fb74ce5042.js create mode 100644 pr/395/docs/search.index/583daef1b6ad.js create mode 100644 pr/395/docs/search.index/592f5e893bf5.js create mode 100644 pr/395/docs/search.index/597d40647479.js create mode 100644 pr/395/docs/search.index/59b1b3c79cd8.js create mode 100644 pr/395/docs/search.index/5a4d4c312dc2.js create mode 100644 pr/395/docs/search.index/5aa972e70a3a.js create mode 100644 pr/395/docs/search.index/5ab07c163f18.js create mode 100644 pr/395/docs/search.index/5add61f19273.js create mode 100644 pr/395/docs/search.index/5d2c023a10ee.js create mode 100644 pr/395/docs/search.index/5d68fc368cd0.js create mode 100644 pr/395/docs/search.index/5d85d427a4e9.js create mode 100644 pr/395/docs/search.index/5db8f7a37811.js create mode 100644 pr/395/docs/search.index/5e234a0755e9.js create mode 100644 pr/395/docs/search.index/5fc89dcacf77.js create mode 100644 pr/395/docs/search.index/6037a95cf9ab.js create mode 100644 pr/395/docs/search.index/610707ac8244.js create mode 100644 pr/395/docs/search.index/6207a70bbfba.js create mode 100644 pr/395/docs/search.index/62bb924ae01b.js create mode 100644 pr/395/docs/search.index/639a2b456721.js create mode 100644 pr/395/docs/search.index/64d55e5f1bdd.js create mode 100644 pr/395/docs/search.index/64e46650dfe4.js create mode 100644 pr/395/docs/search.index/657c7a54fc8d.js create mode 100644 pr/395/docs/search.index/665d99930d33.js create mode 100644 pr/395/docs/search.index/66af3e6bd759.js create mode 100644 pr/395/docs/search.index/677118915139.js create mode 100644 pr/395/docs/search.index/693c05a1eff1.js create mode 100644 pr/395/docs/search.index/6aea33209657.js create mode 100644 pr/395/docs/search.index/6af0c970065d.js create mode 100644 pr/395/docs/search.index/6b88e2cdd5d5.js create mode 100644 pr/395/docs/search.index/6bcf60824292.js create mode 100644 pr/395/docs/search.index/6bd9cf2870c6.js create mode 100644 pr/395/docs/search.index/6ccbc319ac73.js create mode 100644 pr/395/docs/search.index/6d3546009992.js create mode 100644 pr/395/docs/search.index/6ef23363b488.js create mode 100644 pr/395/docs/search.index/6fd721d22688.js create mode 100644 pr/395/docs/search.index/7017e57856f5.js create mode 100644 pr/395/docs/search.index/71b46e229639.js create mode 100644 pr/395/docs/search.index/71d860ce371b.js create mode 100644 pr/395/docs/search.index/724367660563.js create mode 100644 pr/395/docs/search.index/73a80984aee8.js create mode 100644 pr/395/docs/search.index/73e02919d8d7.js create mode 100644 pr/395/docs/search.index/7403472d979d.js create mode 100644 pr/395/docs/search.index/745e0125b99d.js create mode 100644 pr/395/docs/search.index/751ec5fe38a5.js create mode 100644 pr/395/docs/search.index/75634231c1f5.js create mode 100644 pr/395/docs/search.index/7743460538bb.js create mode 100644 pr/395/docs/search.index/77cd02480e18.js create mode 100644 pr/395/docs/search.index/782860c5c65d.js create mode 100644 pr/395/docs/search.index/792cc1abc097.js create mode 100644 pr/395/docs/search.index/7a1f32acb3d1.js create mode 100644 pr/395/docs/search.index/7b8a6a4a9fb4.js create mode 100644 pr/395/docs/search.index/7c16cb6d06bb.js create mode 100644 pr/395/docs/search.index/7c840b92ee85.js create mode 100644 pr/395/docs/search.index/7d4f6308935a.js create mode 100644 pr/395/docs/search.index/7f07551082cf.js create mode 100644 pr/395/docs/search.index/7f8c3fb9fdb6.js create mode 100644 pr/395/docs/search.index/7f983daa5a41.js create mode 100644 pr/395/docs/search.index/7fee0594063e.js create mode 100644 pr/395/docs/search.index/alias/a762150c532c.js create mode 100644 pr/395/docs/search.index/crateNames/8a381fcf2bd4.js create mode 100644 pr/395/docs/search.index/desc/377b54f4891b.js create mode 100644 pr/395/docs/search.index/entry/1282991302e8.js create mode 100644 pr/395/docs/search.index/function/2aa7c4c5510f.js create mode 100644 pr/395/docs/search.index/generic_inverted_index/5d8b801915db.js create mode 100644 pr/395/docs/search.index/name/7c550d2a41fa.js create mode 100644 pr/395/docs/search.index/normalizedName/7cb9cf25523a.js create mode 100644 pr/395/docs/search.index/path/69f26a7f5864.js create mode 100644 pr/395/docs/search.index/root.js create mode 100644 pr/395/docs/search.index/type/84f7d456d387.js create mode 100644 pr/395/docs/set_up_connection/all.html create mode 100644 pr/395/docs/set_up_connection/constant.CLIENT_ADDR.html create mode 100644 pr/395/docs/set_up_connection/constant.LOCALHOST_V4.html create mode 100644 pr/395/docs/set_up_connection/constant.SERVER_ADDR.html create mode 100644 pr/395/docs/set_up_connection/constant.SERVER_NAME.html create mode 100644 pr/395/docs/set_up_connection/fn.client.html create mode 100644 pr/395/docs/set_up_connection/fn.main.html create mode 100644 pr/395/docs/set_up_connection/fn.server.html create mode 100644 pr/395/docs/set_up_connection/index.html create mode 100644 pr/395/docs/set_up_connection/sidebar-items.js create mode 100644 pr/395/docs/settings.html create mode 100644 pr/395/docs/src-files.js create mode 100644 pr/395/docs/src/bench/lib.rs.html create mode 100644 pr/395/docs/src/bench/stats.rs.html create mode 100644 pr/395/docs/src/bulk/bulk.rs.html create mode 100644 pr/395/docs/src/certificate/certificate.rs.html create mode 100644 pr/395/docs/src/data_transfer/data-transfer.rs.html create mode 100644 pr/395/docs/src/iroh_quinn/connection.rs.html create mode 100644 pr/395/docs/src/iroh_quinn/endpoint.rs.html create mode 100644 pr/395/docs/src/iroh_quinn/incoming.rs.html create mode 100644 pr/395/docs/src/iroh_quinn/lib.rs.html create mode 100644 pr/395/docs/src/iroh_quinn/mutex.rs.html create mode 100644 pr/395/docs/src/iroh_quinn/path.rs.html create mode 100644 pr/395/docs/src/iroh_quinn/recv_stream.rs.html create mode 100644 pr/395/docs/src/iroh_quinn/runtime/mod.rs.html create mode 100644 pr/395/docs/src/iroh_quinn/runtime/smol.rs.html create mode 100644 pr/395/docs/src/iroh_quinn/runtime/tokio.rs.html create mode 100644 pr/395/docs/src/iroh_quinn/send_stream.rs.html create mode 100644 pr/395/docs/src/iroh_quinn/work_limiter.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/address_discovery.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/bloom_token_log.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/cid_generator.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/cid_queue.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/coding.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/config/mod.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/config/qlog.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/config/transport.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/congestion.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/congestion/bbr/bw_estimation.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/congestion/bbr/min_max.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/congestion/bbr/mod.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/congestion/cubic.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/congestion/new_reno.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/ack_frequency.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/assembler.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/cid_state.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/datagrams.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/mod.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/mtud.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/pacing.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/packet_crypto.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/paths.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/qlog.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/send_buffer.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/spaces.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/state.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/stats.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/streams/recv.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/streams/send.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/streams/state.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/timer.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/connection/transmit_buf.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/constant_time.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/crypto.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/crypto/ring_like.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/crypto/rustls.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/endpoint.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/frame.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/iroh_hp.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/lib.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/packet.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/range_set/array_range_set.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/range_set/mod.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/shared.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/token.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/token_memory_cache.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/transport_error.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/transport_parameters.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_proto/varint.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_udp/cmsg/mod.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_udp/cmsg/unix.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_udp/lib.rs.html create mode 100644 pr/395/docs/src/iroh_quinn_udp/unix.rs.html create mode 100644 pr/395/docs/src/perf/client.rs.html create mode 100644 pr/395/docs/src/perf/lib.rs.html create mode 100644 pr/395/docs/src/perf/noprotection.rs.html create mode 100644 pr/395/docs/src/perf/server.rs.html create mode 100644 pr/395/docs/src/perf/stats.rs.html create mode 100644 pr/395/docs/src/quinn_perf/perf.rs.html create mode 100644 pr/395/docs/src/set_up_connection/set-up-connection.rs.html create mode 100644 pr/395/docs/static.files/COPYRIGHT-7fb11f4e.txt create mode 100644 pr/395/docs/static.files/FiraMono-Medium-86f75c8c.woff2 create mode 100644 pr/395/docs/static.files/FiraMono-Regular-87c26294.woff2 create mode 100644 pr/395/docs/static.files/FiraSans-Italic-81dc35de.woff2 create mode 100644 pr/395/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt create mode 100644 pr/395/docs/static.files/FiraSans-Medium-e1aa3f0a.woff2 create mode 100644 pr/395/docs/static.files/FiraSans-MediumItalic-ccf7e434.woff2 create mode 100644 pr/395/docs/static.files/FiraSans-Regular-0fe48ade.woff2 create mode 100644 pr/395/docs/static.files/LICENSE-APACHE-a60eea81.txt create mode 100644 pr/395/docs/static.files/LICENSE-MIT-23f18e03.txt create mode 100644 pr/395/docs/static.files/NanumBarunGothic-13b3dcba.ttf.woff2 create mode 100644 pr/395/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt create mode 100644 pr/395/docs/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 create mode 100644 pr/395/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt create mode 100644 pr/395/docs/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 create mode 100644 pr/395/docs/static.files/SourceCodePro-Semibold-aa29a496.ttf.woff2 create mode 100644 pr/395/docs/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 create mode 100644 pr/395/docs/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 create mode 100644 pr/395/docs/static.files/SourceSerif4-LICENSE-a2cfd9d5.md create mode 100644 pr/395/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 create mode 100644 pr/395/docs/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 create mode 100644 pr/395/docs/static.files/favicon-044be391.svg create mode 100644 pr/395/docs/static.files/favicon-32x32-eab170b8.png create mode 100644 pr/395/docs/static.files/main-ce535bd0.js create mode 100644 pr/395/docs/static.files/normalize-9960930a.css create mode 100644 pr/395/docs/static.files/noscript-263c88ec.css create mode 100644 pr/395/docs/static.files/rust-logo-9a9549ea.svg create mode 100644 pr/395/docs/static.files/rustdoc-ca0dd0c4.css create mode 100644 pr/395/docs/static.files/scrape-examples-2bbcccac.js create mode 100644 pr/395/docs/static.files/search-8d3311b9.js create mode 100644 pr/395/docs/static.files/settings-c38705f0.js create mode 100644 pr/395/docs/static.files/src-script-813739b1.js create mode 100644 pr/395/docs/static.files/storage-e2aeef58.js create mode 100644 pr/395/docs/static.files/stringdex-828709d0.js create mode 100644 pr/395/docs/trait.impl/arbitrary/trait.Arbitrary.js create mode 100644 pr/395/docs/trait.impl/clap_builder/derive/trait.Args.js create mode 100644 pr/395/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js create mode 100644 pr/395/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js create mode 100644 pr/395/docs/trait.impl/clap_builder/derive/trait.Parser.js create mode 100644 pr/395/docs/trait.impl/clap_builder/derive/trait.Subcommand.js create mode 100644 pr/395/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js create mode 100644 pr/395/docs/trait.impl/core/clone/trait.Clone.js create mode 100644 pr/395/docs/trait.impl/core/cmp/trait.Eq.js create mode 100644 pr/395/docs/trait.impl/core/cmp/trait.Ord.js create mode 100644 pr/395/docs/trait.impl/core/cmp/trait.PartialEq.js create mode 100644 pr/395/docs/trait.impl/core/cmp/trait.PartialOrd.js create mode 100644 pr/395/docs/trait.impl/core/convert/trait.From.js create mode 100644 pr/395/docs/trait.impl/core/convert/trait.TryFrom.js create mode 100644 pr/395/docs/trait.impl/core/default/trait.Default.js create mode 100644 pr/395/docs/trait.impl/core/error/trait.Error.js create mode 100644 pr/395/docs/trait.impl/core/fmt/trait.Debug.js create mode 100644 pr/395/docs/trait.impl/core/fmt/trait.Display.js create mode 100644 pr/395/docs/trait.impl/core/future/future/trait.Future.js create mode 100644 pr/395/docs/trait.impl/core/future/into_future/trait.IntoFuture.js create mode 100644 pr/395/docs/trait.impl/core/hash/trait.Hash.js create mode 100644 pr/395/docs/trait.impl/core/marker/trait.Copy.js create mode 100644 pr/395/docs/trait.impl/core/marker/trait.Freeze.js create mode 100644 pr/395/docs/trait.impl/core/marker/trait.Send.js create mode 100644 pr/395/docs/trait.impl/core/marker/trait.StructuralPartialEq.js create mode 100644 pr/395/docs/trait.impl/core/marker/trait.Sync.js create mode 100644 pr/395/docs/trait.impl/core/marker/trait.Unpin.js create mode 100644 pr/395/docs/trait.impl/core/marker/trait.UnsafeUnpin.js create mode 100644 pr/395/docs/trait.impl/core/ops/bit/trait.Not.js create mode 100644 pr/395/docs/trait.impl/core/ops/deref/trait.Deref.js create mode 100644 pr/395/docs/trait.impl/core/ops/deref/trait.DerefMut.js create mode 100644 pr/395/docs/trait.impl/core/ops/drop/trait.Drop.js create mode 100644 pr/395/docs/trait.impl/core/ops/index/trait.Index.js create mode 100644 pr/395/docs/trait.impl/core/ops/index/trait.IndexMut.js create mode 100644 pr/395/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js create mode 100644 pr/395/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js create mode 100644 pr/395/docs/trait.impl/core/str/traits/trait.FromStr.js create mode 100644 pr/395/docs/trait.impl/futures_core/stream/trait.Stream.js create mode 100644 pr/395/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js create mode 100644 pr/395/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js create mode 100644 pr/395/docs/trait.impl/identity_hash/trait.IdentityHashable.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn/runtime/trait.AsyncTimer.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn/runtime/trait.Runtime.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/coding/trait.Decodable.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/coding/trait.Encodable.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/config/qlog/trait.QlogFactory.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/config/trait.TimeSource.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/congestion/trait.Controller.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/congestion/trait.ControllerFactory.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.AeadKey.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.ClientConfig.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.HeaderKey.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.HmacKey.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.PacketKey.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.ServerConfig.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.Session.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/packet/trait.ConnectionIdParser.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/token/trait.TokenLog.js create mode 100644 pr/395/docs/trait.impl/iroh_quinn_proto/token/trait.TokenStore.js create mode 100644 pr/395/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js create mode 100644 pr/395/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js create mode 100644 pr/395/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js create mode 100644 pr/395/docs/type.impl/core/result/enum.Result.js diff --git a/pr/395/docs/.lock b/pr/395/docs/.lock new file mode 100644 index 000000000..e69de29bb diff --git a/pr/395/docs/bench/all.html b/pr/395/docs/bench/all.html new file mode 100644 index 000000000..6778d9fe3 --- /dev/null +++ b/pr/395/docs/bench/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

\ No newline at end of file diff --git a/pr/395/docs/bench/enum.CipherSuite.html b/pr/395/docs/bench/enum.CipherSuite.html new file mode 100644 index 000000000..6f65dd21b --- /dev/null +++ b/pr/395/docs/bench/enum.CipherSuite.html @@ -0,0 +1,35 @@ +CipherSuite in bench - Rust

CipherSuite

Enum CipherSuite 

Source
pub enum CipherSuite {
+    Aes128,
+    Aes256,
+    Chacha20,
+}

Variants§

§

Aes128

§

Aes256

§

Chacha20

Implementations§

Source§

impl CipherSuite

Source

pub fn as_rustls(self) -> SupportedCipherSuite

Trait Implementations§

Source§

impl Clone for CipherSuite

Source§

fn clone(&self) -> CipherSuite

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for CipherSuite

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl FromStr for CipherSuite

Source§

type Err = Error

The associated error which can be returned from parsing.
Source§

fn from_str(s: &str) -> Result<Self, Self::Err>

Parses a string s to return a value of this type. Read more
Source§

impl PartialEq for CipherSuite

Source§

fn eq(&self, other: &CipherSuite) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for CipherSuite

Source§

impl Eq for CipherSuite

Source§

impl StructuralPartialEq for CipherSuite

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/bench/fn.configure_tracing_subscriber.html b/pr/395/docs/bench/fn.configure_tracing_subscriber.html new file mode 100644 index 000000000..93f1c308a --- /dev/null +++ b/pr/395/docs/bench/fn.configure_tracing_subscriber.html @@ -0,0 +1 @@ +configure_tracing_subscriber in bench - Rust

configure_tracing_subscriber

Function configure_tracing_subscriber 

Source
pub fn configure_tracing_subscriber()
\ No newline at end of file diff --git a/pr/395/docs/bench/fn.connect_client.html b/pr/395/docs/bench/fn.connect_client.html new file mode 100644 index 000000000..1953d8b68 --- /dev/null +++ b/pr/395/docs/bench/fn.connect_client.html @@ -0,0 +1,6 @@ +connect_client in bench - Rust

connect_client

Function connect_client 

Source
pub async fn connect_client(
+    server_addr: SocketAddr,
+    server_cert: CertificateDer<'_>,
+    opt: Opt,
+) -> Result<(Endpoint, Connection)>
Expand description

Create a client endpoint and client connection

+
\ No newline at end of file diff --git a/pr/395/docs/bench/fn.drain_stream.html b/pr/395/docs/bench/fn.drain_stream.html new file mode 100644 index 000000000..5cd0ee437 --- /dev/null +++ b/pr/395/docs/bench/fn.drain_stream.html @@ -0,0 +1,4 @@ +drain_stream in bench - Rust

drain_stream

Function drain_stream 

Source
pub async fn drain_stream(
+    stream: RecvStream,
+    read_unordered: bool,
+) -> Result<usize>
\ No newline at end of file diff --git a/pr/395/docs/bench/fn.rt.html b/pr/395/docs/bench/fn.rt.html new file mode 100644 index 000000000..982c7e743 --- /dev/null +++ b/pr/395/docs/bench/fn.rt.html @@ -0,0 +1 @@ +rt in bench - Rust

rt

Function rt 

Source
pub fn rt() -> Runtime
\ No newline at end of file diff --git a/pr/395/docs/bench/fn.send_data_on_stream.html b/pr/395/docs/bench/fn.send_data_on_stream.html new file mode 100644 index 000000000..e61f81d0b --- /dev/null +++ b/pr/395/docs/bench/fn.send_data_on_stream.html @@ -0,0 +1,4 @@ +send_data_on_stream in bench - Rust

send_data_on_stream

Function send_data_on_stream 

Source
pub async fn send_data_on_stream(
+    stream: &mut SendStream,
+    stream_size: u64,
+) -> Result<()>
\ No newline at end of file diff --git a/pr/395/docs/bench/fn.server_endpoint.html b/pr/395/docs/bench/fn.server_endpoint.html new file mode 100644 index 000000000..605fd3cf2 --- /dev/null +++ b/pr/395/docs/bench/fn.server_endpoint.html @@ -0,0 +1,7 @@ +server_endpoint in bench - Rust

server_endpoint

Function server_endpoint 

Source
pub fn server_endpoint(
+    rt: &Runtime,
+    cert: CertificateDer<'static>,
+    key: PrivateKeyDer<'static>,
+    opt: &Opt,
+) -> (SocketAddr, Endpoint)
Expand description

Creates a server endpoint which runs on the given runtime

+
\ No newline at end of file diff --git a/pr/395/docs/bench/fn.transport_config.html b/pr/395/docs/bench/fn.transport_config.html new file mode 100644 index 000000000..411bd33d7 --- /dev/null +++ b/pr/395/docs/bench/fn.transport_config.html @@ -0,0 +1 @@ +transport_config in bench - Rust

transport_config

Function transport_config 

Source
pub fn transport_config(opt: &Opt) -> TransportConfig
\ No newline at end of file diff --git a/pr/395/docs/bench/index.html b/pr/395/docs/bench/index.html new file mode 100644 index 000000000..4e57465a1 --- /dev/null +++ b/pr/395/docs/bench/index.html @@ -0,0 +1 @@ +bench - Rust

Crate bench

Crate bench 

Source

Modules§

stats

Structs§

Opt

Enums§

CipherSuite

Functions§

configure_tracing_subscriber
connect_client
Create a client endpoint and client connection
drain_stream
rt
send_data_on_stream
server_endpoint
Creates a server endpoint which runs on the given runtime
transport_config
\ No newline at end of file diff --git a/pr/395/docs/bench/sidebar-items.js b/pr/395/docs/bench/sidebar-items.js new file mode 100644 index 000000000..f56525d6b --- /dev/null +++ b/pr/395/docs/bench/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["CipherSuite"],"fn":["configure_tracing_subscriber","connect_client","drain_stream","rt","send_data_on_stream","server_endpoint","transport_config"],"mod":["stats"],"struct":["Opt"]}; \ No newline at end of file diff --git a/pr/395/docs/bench/stats/fn.throughput_bps.html b/pr/395/docs/bench/stats/fn.throughput_bps.html new file mode 100644 index 000000000..0a49b335e --- /dev/null +++ b/pr/395/docs/bench/stats/fn.throughput_bps.html @@ -0,0 +1 @@ +throughput_bps in bench::stats - Rust

throughput_bps

Function throughput_bps 

Source
pub fn throughput_bps(duration: Duration, size: u64) -> f64
\ No newline at end of file diff --git a/pr/395/docs/bench/stats/index.html b/pr/395/docs/bench/stats/index.html new file mode 100644 index 000000000..3655ae338 --- /dev/null +++ b/pr/395/docs/bench/stats/index.html @@ -0,0 +1 @@ +bench::stats - Rust

Module stats

Module stats 

Source

Structs§

Stats
StreamStats
TransferResult

Functions§

throughput_bps
\ No newline at end of file diff --git a/pr/395/docs/bench/stats/sidebar-items.js b/pr/395/docs/bench/stats/sidebar-items.js new file mode 100644 index 000000000..6272a13ae --- /dev/null +++ b/pr/395/docs/bench/stats/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["throughput_bps"],"struct":["Stats","StreamStats","TransferResult"]}; \ No newline at end of file diff --git a/pr/395/docs/bench/stats/struct.Stats.html b/pr/395/docs/bench/stats/struct.Stats.html new file mode 100644 index 000000000..0a4807d3e --- /dev/null +++ b/pr/395/docs/bench/stats/struct.Stats.html @@ -0,0 +1,29 @@ +Stats in bench::stats - Rust

Stats

Struct Stats 

Source
pub struct Stats {
+    pub total_size: u64,
+    pub total_duration: Duration,
+    pub streams: usize,
+    pub stream_stats: StreamStats,
+}

Fields§

§total_size: u64§total_duration: Duration§streams: usize§stream_stats: StreamStats

Implementations§

Source§

impl Stats

Source

pub fn stream_finished(&mut self, stream_result: TransferResult)

Source

pub fn print(&self, stat_name: &str)

Trait Implementations§

Source§

impl Default for Stats

Source§

fn default() -> Stats

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for Stats

§

impl RefUnwindSafe for Stats

§

impl Send for Stats

§

impl Sync for Stats

§

impl Unpin for Stats

§

impl UnwindSafe for Stats

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/bench/stats/struct.StreamStats.html b/pr/395/docs/bench/stats/struct.StreamStats.html new file mode 100644 index 000000000..80b2f0bf3 --- /dev/null +++ b/pr/395/docs/bench/stats/struct.StreamStats.html @@ -0,0 +1,27 @@ +StreamStats in bench::stats - Rust

StreamStats

Struct StreamStats 

Source
pub struct StreamStats {
+    pub duration_hist: Histogram<u64>,
+    pub throughput_hist: Histogram<u64>,
+}

Fields§

§duration_hist: Histogram<u64>§throughput_hist: Histogram<u64>

Trait Implementations§

Source§

impl Default for StreamStats

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/bench/stats/struct.TransferResult.html b/pr/395/docs/bench/stats/struct.TransferResult.html new file mode 100644 index 000000000..090628d4d --- /dev/null +++ b/pr/395/docs/bench/stats/struct.TransferResult.html @@ -0,0 +1,28 @@ +TransferResult in bench::stats - Rust

TransferResult

Struct TransferResult 

Source
pub struct TransferResult {
+    pub duration: Duration,
+    pub size: u64,
+    pub throughput: f64,
+}

Fields§

§duration: Duration§size: u64§throughput: f64

Implementations§

Source§

impl TransferResult

Source

pub fn new(duration: Duration, size: u64) -> Self

Trait Implementations§

Source§

impl Debug for TransferResult

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/bench/struct.Opt.html b/pr/395/docs/bench/struct.Opt.html new file mode 100644 index 000000000..82be8dcfd --- /dev/null +++ b/pr/395/docs/bench/struct.Opt.html @@ -0,0 +1,68 @@ +Opt in bench - Rust

Opt

Struct Opt 

Source
pub struct Opt {
+    pub clients: usize,
+    pub streams: usize,
+    pub max_streams: usize,
+    pub download_size: u64,
+    pub upload_size: u64,
+    pub stats: bool,
+    pub read_unordered: bool,
+    pub cipher: CipherSuite,
+    pub initial_mtu: u16,
+}

Fields§

§clients: usize

The total number of clients which should be created

+
§streams: usize

The total number of streams which should be created

+
§max_streams: usize

The amount of concurrent streams which should be used

+
§download_size: u64

Number of bytes to transmit from server to client

+

This can use SI suffixes for sizes. For example, 1M will transfer +1MiB, 10G will transfer 10GiB.

+
§upload_size: u64

Number of bytes to transmit from client to server

+

This can use SI suffixes for sizes. For example, 1M will transfer +1MiB, 10G will transfer 10GiB.

+
§stats: bool

Show connection stats the at the end of the benchmark

+
§read_unordered: bool

Whether to use the unordered read API

+
§cipher: CipherSuite

Allows to configure the desired cipher suite

+

Valid options are: aes128, aes256, chacha20

+
§initial_mtu: u16

Starting guess for maximum UDP payload size

+

Trait Implementations§

Source§

impl Args for Opt

Source§

fn group_id() -> Option<Id>

Report the [ArgGroup::id][crate::ArgGroup::id] for this set of arguments
Source§

fn augment_args<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate Self via +[FromArgMatches::from_arg_matches_mut] Read more
Source§

fn augment_args_for_update<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate self via +[FromArgMatches::update_from_arg_matches_mut] Read more
Source§

impl Clone for Opt

Source§

fn clone(&self) -> Opt

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl CommandFactory for Opt

Source§

fn command<'b>() -> Command

Build a [Command] that can instantiate Self. Read more
Source§

fn command_for_update<'b>() -> Command

Build a [Command] that can update self. Read more
Source§

impl Debug for Opt

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl FromArgMatches for Opt

Source§

fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn from_arg_matches_mut( + __clap_arg_matches: &mut ArgMatches, +) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn update_from_arg_matches( + &mut self, + __clap_arg_matches: &ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

fn update_from_arg_matches_mut( + &mut self, + __clap_arg_matches: &mut ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

impl Parser for Opt

§

fn parse() -> Self

Parse from std::env::args_os(), [exit][Error::exit] on error.
§

fn try_parse() -> Result<Self, Error>

Parse from std::env::args_os(), return Err on error.
§

fn parse_from<I, T>(itr: I) -> Self
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, [exit][Error::exit] on error.
§

fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, return Err on error.
§

fn update_from<I, T>(&mut self, itr: I)
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, [exit][Error::exit] on error. Read more
§

fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, return Err on error.
Source§

impl Copy for Opt

Auto Trait Implementations§

§

impl Freeze for Opt

§

impl RefUnwindSafe for Opt

§

impl Send for Opt

§

impl Sync for Opt

§

impl Unpin for Opt

§

impl UnwindSafe for Opt

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/bulk/all.html b/pr/395/docs/bulk/all.html new file mode 100644 index 000000000..0a6875d72 --- /dev/null +++ b/pr/395/docs/bulk/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

List of all items

Structs

Functions

\ No newline at end of file diff --git a/pr/395/docs/bulk/fn.client.html b/pr/395/docs/bulk/fn.client.html new file mode 100644 index 000000000..6a2f1e48a --- /dev/null +++ b/pr/395/docs/bulk/fn.client.html @@ -0,0 +1,5 @@ +client in bulk - Rust

client

Function client 

Source
pub(crate) async fn client(
+    server_addr: SocketAddr,
+    server_cert: CertificateDer<'static>,
+    opt: Opt,
+) -> Result<ClientStats>
\ No newline at end of file diff --git a/pr/395/docs/bulk/fn.handle_client_stream.html b/pr/395/docs/bulk/fn.handle_client_stream.html new file mode 100644 index 000000000..ba79a8757 --- /dev/null +++ b/pr/395/docs/bulk/fn.handle_client_stream.html @@ -0,0 +1,5 @@ +handle_client_stream in bulk - Rust

handle_client_stream

Function handle_client_stream 

Source
pub(crate) async fn handle_client_stream(
+    connection: Arc<Connection>,
+    upload_size: u64,
+    read_unordered: bool,
+) -> Result<(TransferResult, TransferResult)>
\ No newline at end of file diff --git a/pr/395/docs/bulk/fn.main.html b/pr/395/docs/bulk/fn.main.html new file mode 100644 index 000000000..e8cfaa7ca --- /dev/null +++ b/pr/395/docs/bulk/fn.main.html @@ -0,0 +1 @@ +main in bulk - Rust

main

Function main 

Source
pub(crate) fn main()
\ No newline at end of file diff --git a/pr/395/docs/bulk/fn.server.html b/pr/395/docs/bulk/fn.server.html new file mode 100644 index 000000000..2ea0860dd --- /dev/null +++ b/pr/395/docs/bulk/fn.server.html @@ -0,0 +1 @@ +server in bulk - Rust

server

Function server 

Source
pub(crate) async fn server(endpoint: Endpoint, opt: Opt) -> Result<()>
\ No newline at end of file diff --git a/pr/395/docs/bulk/index.html b/pr/395/docs/bulk/index.html new file mode 100644 index 000000000..6b9249f74 --- /dev/null +++ b/pr/395/docs/bulk/index.html @@ -0,0 +1 @@ +bulk - Rust

Crate bulk

Crate bulk 

Source

Structs§

ClientStats 🔒

Functions§

client 🔒
handle_client_stream 🔒
main 🔒
server 🔒
\ No newline at end of file diff --git a/pr/395/docs/bulk/sidebar-items.js b/pr/395/docs/bulk/sidebar-items.js new file mode 100644 index 000000000..4b4dceb17 --- /dev/null +++ b/pr/395/docs/bulk/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["client","handle_client_stream","main","server"],"struct":["ClientStats"]}; \ No newline at end of file diff --git a/pr/395/docs/bulk/struct.ClientStats.html b/pr/395/docs/bulk/struct.ClientStats.html new file mode 100644 index 000000000..7f78e9897 --- /dev/null +++ b/pr/395/docs/bulk/struct.ClientStats.html @@ -0,0 +1,27 @@ +ClientStats in bulk - Rust

ClientStats

Struct ClientStats 

Source
pub(crate) struct ClientStats {
+    pub(crate) upload_stats: Stats,
+    pub(crate) download_stats: Stats,
+}

Fields§

§upload_stats: Stats§download_stats: Stats

Implementations§

Source§

impl ClientStats

Source

pub(crate) fn print(&self, client_id: usize)

Trait Implementations§

Source§

impl Default for ClientStats

Source§

fn default() -> ClientStats

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/certificate/all.html b/pr/395/docs/certificate/all.html new file mode 100644 index 000000000..d6e1b79d5 --- /dev/null +++ b/pr/395/docs/certificate/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

\ No newline at end of file diff --git a/pr/395/docs/certificate/fn.configure_client.html b/pr/395/docs/certificate/fn.configure_client.html new file mode 100644 index 000000000..95e203add --- /dev/null +++ b/pr/395/docs/certificate/fn.configure_client.html @@ -0,0 +1 @@ +configure_client in certificate - Rust

configure_client

Function configure_client 

Source
pub(crate) fn configure_client() -> Result<ClientConfig, NoInitialCipherSuite>
\ No newline at end of file diff --git a/pr/395/docs/certificate/fn.generate_self_signed_cert.html b/pr/395/docs/certificate/fn.generate_self_signed_cert.html new file mode 100644 index 000000000..e91269156 --- /dev/null +++ b/pr/395/docs/certificate/fn.generate_self_signed_cert.html @@ -0,0 +1 @@ +generate_self_signed_cert in certificate - Rust

generate_self_signed_cert

Function generate_self_signed_cert 

Source
pub(crate) fn generate_self_signed_cert() -> Result<(CertificateDer<'static>, PrivatePkcs8KeyDer<'static>), Box<dyn Error>>
\ No newline at end of file diff --git a/pr/395/docs/certificate/fn.main.html b/pr/395/docs/certificate/fn.main.html new file mode 100644 index 000000000..461b95b57 --- /dev/null +++ b/pr/395/docs/certificate/fn.main.html @@ -0,0 +1 @@ +main in certificate - Rust

main

Function main 

Source
pub(crate) fn main()
\ No newline at end of file diff --git a/pr/395/docs/certificate/fn.read_certs_from_file.html b/pr/395/docs/certificate/fn.read_certs_from_file.html new file mode 100644 index 000000000..bd63cf355 --- /dev/null +++ b/pr/395/docs/certificate/fn.read_certs_from_file.html @@ -0,0 +1 @@ +read_certs_from_file in certificate - Rust

read_certs_from_file

Function read_certs_from_file 

Source
pub(crate) fn read_certs_from_file() -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>), Box<dyn Error>>
\ No newline at end of file diff --git a/pr/395/docs/certificate/index.html b/pr/395/docs/certificate/index.html new file mode 100644 index 000000000..eb429342b --- /dev/null +++ b/pr/395/docs/certificate/index.html @@ -0,0 +1 @@ +certificate - Rust

Crate certificate

Crate certificate 

Source

Structs§

SkipServerVerification 🔒

Functions§

configure_client 🔒
generate_self_signed_cert 🔒
main 🔒
read_certs_from_file 🔒
\ No newline at end of file diff --git a/pr/395/docs/certificate/sidebar-items.js b/pr/395/docs/certificate/sidebar-items.js new file mode 100644 index 000000000..0f8f69b2b --- /dev/null +++ b/pr/395/docs/certificate/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["configure_client","generate_self_signed_cert","main","read_certs_from_file"],"struct":["SkipServerVerification"]}; \ No newline at end of file diff --git a/pr/395/docs/certificate/struct.SkipServerVerification.html b/pr/395/docs/certificate/struct.SkipServerVerification.html new file mode 100644 index 000000000..c35462c07 --- /dev/null +++ b/pr/395/docs/certificate/struct.SkipServerVerification.html @@ -0,0 +1,44 @@ +SkipServerVerification in certificate - Rust

SkipServerVerification

Struct SkipServerVerification 

Source
pub(crate) struct SkipServerVerification(pub(crate) Arc<CryptoProvider>);

Tuple Fields§

§0: Arc<CryptoProvider>

Implementations§

Source§

impl SkipServerVerification

Source

pub(crate) fn new() -> Arc<Self>

Trait Implementations§

Source§

impl Debug for SkipServerVerification

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl ServerCertVerifier for SkipServerVerification

Source§

fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp: &[u8], + _now: UnixTime, +) -> Result<ServerCertVerified, Error>

Verify the end-entity certificate end_entity is valid for the +hostname dns_name and chains to at least one trust anchor. Read more
Source§

fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, +) -> Result<HandshakeSignatureValid, Error>

Verify a signature allegedly by the given server certificate. Read more
Source§

fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, +) -> Result<HandshakeSignatureValid, Error>

Verify a signature allegedly by the given server certificate. Read more
Source§

fn supported_verify_schemes(&self) -> Vec<SignatureScheme>

Return the list of SignatureSchemes that this verifier will handle, +in verify_tls12_signature and verify_tls13_signature calls. Read more
§

fn requires_raw_public_keys(&self) -> bool

Returns whether this verifier requires raw public keys as defined +in RFC 7250.
§

fn root_hint_subjects(&self) -> Option<&[DistinguishedName]>

Return the [DistinguishedName]s of certificate authorities that this verifier trusts. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/crates.js b/pr/395/docs/crates.js new file mode 100644 index 000000000..25a54490d --- /dev/null +++ b/pr/395/docs/crates.js @@ -0,0 +1,2 @@ +window.ALL_CRATES = ["bench","bulk","certificate","data_transfer","iroh_quinn","iroh_quinn_proto","iroh_quinn_udp","perf","quinn_perf","set_up_connection"]; +//{"start":21,"fragment_lengths":[7,7,14,16,13,19,17,7,13,20]} \ No newline at end of file diff --git a/pr/395/docs/data_transfer/all.html b/pr/395/docs/data_transfer/all.html new file mode 100644 index 000000000..fd64bbb8f --- /dev/null +++ b/pr/395/docs/data_transfer/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

\ No newline at end of file diff --git a/pr/395/docs/data_transfer/fn.main.html b/pr/395/docs/data_transfer/fn.main.html new file mode 100644 index 000000000..8bcfacdf5 --- /dev/null +++ b/pr/395/docs/data_transfer/fn.main.html @@ -0,0 +1 @@ +main in data_transfer - Rust

main

Function main 

Source
pub(crate) fn main()
\ No newline at end of file diff --git a/pr/395/docs/data_transfer/fn.open_bidirectional_stream.html b/pr/395/docs/data_transfer/fn.open_bidirectional_stream.html new file mode 100644 index 000000000..2c097384f --- /dev/null +++ b/pr/395/docs/data_transfer/fn.open_bidirectional_stream.html @@ -0,0 +1,3 @@ +open_bidirectional_stream in data_transfer - Rust

open_bidirectional_stream

Function open_bidirectional_stream 

Source
pub(crate) async fn open_bidirectional_stream(
+    connection: Connection,
+) -> Result<()>
\ No newline at end of file diff --git a/pr/395/docs/data_transfer/fn.open_unidirectional_stream.html b/pr/395/docs/data_transfer/fn.open_unidirectional_stream.html new file mode 100644 index 000000000..0d5cfbb84 --- /dev/null +++ b/pr/395/docs/data_transfer/fn.open_unidirectional_stream.html @@ -0,0 +1,3 @@ +open_unidirectional_stream in data_transfer - Rust

open_unidirectional_stream

Function open_unidirectional_stream 

Source
pub(crate) async fn open_unidirectional_stream(
+    connection: Connection,
+) -> Result<()>
\ No newline at end of file diff --git a/pr/395/docs/data_transfer/fn.receive_bidirectional_stream.html b/pr/395/docs/data_transfer/fn.receive_bidirectional_stream.html new file mode 100644 index 000000000..0180f8dca --- /dev/null +++ b/pr/395/docs/data_transfer/fn.receive_bidirectional_stream.html @@ -0,0 +1,3 @@ +receive_bidirectional_stream in data_transfer - Rust

receive_bidirectional_stream

Function receive_bidirectional_stream 

Source
pub(crate) async fn receive_bidirectional_stream(
+    connection: Connection,
+) -> Result<()>
\ No newline at end of file diff --git a/pr/395/docs/data_transfer/fn.receive_datagram.html b/pr/395/docs/data_transfer/fn.receive_datagram.html new file mode 100644 index 000000000..f5467d0f5 --- /dev/null +++ b/pr/395/docs/data_transfer/fn.receive_datagram.html @@ -0,0 +1 @@ +receive_datagram in data_transfer - Rust

receive_datagram

Function receive_datagram 

Source
pub(crate) async fn receive_datagram(connection: Connection) -> Result<()>
\ No newline at end of file diff --git a/pr/395/docs/data_transfer/fn.receive_unidirectional_stream.html b/pr/395/docs/data_transfer/fn.receive_unidirectional_stream.html new file mode 100644 index 000000000..5d00e7b8f --- /dev/null +++ b/pr/395/docs/data_transfer/fn.receive_unidirectional_stream.html @@ -0,0 +1,3 @@ +receive_unidirectional_stream in data_transfer - Rust

receive_unidirectional_stream

Function receive_unidirectional_stream 

Source
pub(crate) async fn receive_unidirectional_stream(
+    connection: Connection,
+) -> Result<()>
\ No newline at end of file diff --git a/pr/395/docs/data_transfer/fn.send_unreliable.html b/pr/395/docs/data_transfer/fn.send_unreliable.html new file mode 100644 index 000000000..ab8421938 --- /dev/null +++ b/pr/395/docs/data_transfer/fn.send_unreliable.html @@ -0,0 +1 @@ +send_unreliable in data_transfer - Rust

send_unreliable

Function send_unreliable 

Source
pub(crate) async fn send_unreliable(connection: Connection) -> Result<()>
\ No newline at end of file diff --git a/pr/395/docs/data_transfer/index.html b/pr/395/docs/data_transfer/index.html new file mode 100644 index 000000000..bc41bd1c4 --- /dev/null +++ b/pr/395/docs/data_transfer/index.html @@ -0,0 +1 @@ +data_transfer - Rust

Crate data_transfer

\ No newline at end of file diff --git a/pr/395/docs/data_transfer/sidebar-items.js b/pr/395/docs/data_transfer/sidebar-items.js new file mode 100644 index 000000000..4a3b9f284 --- /dev/null +++ b/pr/395/docs/data_transfer/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["main","open_bidirectional_stream","open_unidirectional_stream","receive_bidirectional_stream","receive_datagram","receive_unidirectional_stream","send_unreliable"]}; \ No newline at end of file diff --git a/pr/395/docs/help.html b/pr/395/docs/help.html new file mode 100644 index 000000000..2565ad385 --- /dev/null +++ b/pr/395/docs/help.html @@ -0,0 +1 @@ +Help

All

Rustdoc help

Back
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/all.html b/pr/395/docs/iroh_quinn/all.html new file mode 100644 index 000000000..9b90426bd --- /dev/null +++ b/pr/395/docs/iroh_quinn/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

List of all items

Structs

Enums

Traits

Functions

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/congestion/index.html b/pr/395/docs/iroh_quinn/congestion/index.html new file mode 100644 index 000000000..bba43c8a1 --- /dev/null +++ b/pr/395/docs/iroh_quinn/congestion/index.html @@ -0,0 +1,2 @@ +iroh_quinn::congestion - Rust

Module congestion

Module congestion 

Source
Expand description

Logic for controlling the rate at which data is sent

+

Structs§

Bbr
Experimental! Use at your own risk.
BbrConfig
Configuration for the Bbr congestion controller
ControllerMetrics
Common congestion controller metrics
Cubic
The RFC8312 congestion controller, as widely used for TCP
CubicConfig
Configuration for the Cubic congestion controller
NewReno
A simple, standard congestion controller
NewRenoConfig
Configuration for the NewReno congestion controller

Traits§

Controller
Common interface for different congestion controllers
ControllerFactory
Constructs controllers on demand
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/congestion/sidebar-items.js b/pr/395/docs/iroh_quinn/congestion/sidebar-items.js new file mode 100644 index 000000000..888f550d4 --- /dev/null +++ b/pr/395/docs/iroh_quinn/congestion/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["Bbr","BbrConfig","ControllerMetrics","Cubic","CubicConfig","NewReno","NewRenoConfig"],"trait":["Controller","ControllerFactory"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/congestion/struct.Bbr.html b/pr/395/docs/iroh_quinn/congestion/struct.Bbr.html new file mode 100644 index 000000000..94a3604b9 --- /dev/null +++ b/pr/395/docs/iroh_quinn/congestion/struct.Bbr.html @@ -0,0 +1,52 @@ +Bbr in iroh_quinn::congestion - Rust

Bbr

Struct Bbr 

Source
pub struct Bbr { /* private fields */ }
Expand description

Experimental! Use at your own risk.

+

Aims for reduced buffer bloat and improved performance over high bandwidth-delay product networks. +Based on google’s quiche implementation https://source.chromium.org/chromium/chromium/src/+/master:net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc +of BBR https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control. +More discussion and links at https://groups.google.com/g/bbr-dev.

+

Implementations§

Source§

impl Bbr

Source

pub fn new(config: Arc<BbrConfig>, current_mtu: u16) -> Bbr

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for Bbr

Source§

fn clone(&self) -> Bbr

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Controller for Bbr

Source§

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent
Source§

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.
Source§

fn on_congestion_event( + &mut self, + _now: Instant, + _sent: Instant, + _is_persistent_congestion: bool, + _is_ecn: bool, + lost_bytes: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated
Source§

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight
Source§

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled
Source§

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

fn into_any(self: Box<Bbr>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

impl Debug for Bbr

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Bbr

§

impl RefUnwindSafe for Bbr

§

impl Send for Bbr

§

impl Sync for Bbr

§

impl Unpin for Bbr

§

impl UnwindSafe for Bbr

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/congestion/struct.BbrConfig.html b/pr/395/docs/iroh_quinn/congestion/struct.BbrConfig.html new file mode 100644 index 000000000..2ae63d0bc --- /dev/null +++ b/pr/395/docs/iroh_quinn/congestion/struct.BbrConfig.html @@ -0,0 +1,33 @@ +BbrConfig in iroh_quinn::congestion - Rust

BbrConfig

Struct BbrConfig 

Source
pub struct BbrConfig { /* private fields */ }
Expand description

Configuration for the Bbr congestion controller

+

Implementations§

Source§

impl BbrConfig

Source

pub fn initial_window(&mut self, value: u64) -> &mut BbrConfig

Default limit on the amount of outstanding data in bytes.

+

Recommended value: min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))

+

Trait Implementations§

Source§

impl Clone for BbrConfig

Source§

fn clone(&self) -> BbrConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for BbrConfig

Source§

fn build( + self: Arc<BbrConfig>, + _now: Instant, + current_mtu: u16, +) -> Box<dyn Controller>

Construct a fresh Controller
Source§

impl Debug for BbrConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for BbrConfig

Source§

fn default() -> BbrConfig

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/congestion/struct.ControllerMetrics.html b/pr/395/docs/iroh_quinn/congestion/struct.ControllerMetrics.html new file mode 100644 index 000000000..f4ede9270 --- /dev/null +++ b/pr/395/docs/iroh_quinn/congestion/struct.ControllerMetrics.html @@ -0,0 +1,32 @@ +ControllerMetrics in iroh_quinn::congestion - Rust

ControllerMetrics

Struct ControllerMetrics 

Source
#[non_exhaustive]
pub struct ControllerMetrics { + pub congestion_window: u64, + pub ssthresh: Option<u64>, + pub pacing_rate: Option<u64>, +}
Expand description

Common congestion controller metrics

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§congestion_window: u64

Congestion window (bytes)

+
§ssthresh: Option<u64>

Slow start threshold (bytes)

+
§pacing_rate: Option<u64>

Pacing rate (bits/s)

+

Trait Implementations§

Source§

impl Debug for ControllerMetrics

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for ControllerMetrics

Source§

fn default() -> ControllerMetrics

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/congestion/struct.Cubic.html b/pr/395/docs/iroh_quinn/congestion/struct.Cubic.html new file mode 100644 index 000000000..af26f94a0 --- /dev/null +++ b/pr/395/docs/iroh_quinn/congestion/struct.Cubic.html @@ -0,0 +1,48 @@ +Cubic in iroh_quinn::congestion - Rust

Cubic

Struct Cubic 

Source
pub struct Cubic { /* private fields */ }
Expand description

The RFC8312 congestion controller, as widely used for TCP

+

Implementations§

Source§

impl Cubic

Source

pub fn new(config: Arc<CubicConfig>, _now: Instant, current_mtu: u16) -> Cubic

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for Cubic

Source§

fn clone(&self) -> Cubic

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Controller for Cubic

Source§

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + is_ecn: bool, + _lost_bytes: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated
Source§

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight
Source§

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled
Source§

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

fn into_any(self: Box<Cubic>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details
Source§

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent
Source§

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.
Source§

impl Debug for Cubic

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Cubic

§

impl RefUnwindSafe for Cubic

§

impl Send for Cubic

§

impl Sync for Cubic

§

impl Unpin for Cubic

§

impl UnwindSafe for Cubic

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/congestion/struct.CubicConfig.html b/pr/395/docs/iroh_quinn/congestion/struct.CubicConfig.html new file mode 100644 index 000000000..200f73d9a --- /dev/null +++ b/pr/395/docs/iroh_quinn/congestion/struct.CubicConfig.html @@ -0,0 +1,33 @@ +CubicConfig in iroh_quinn::congestion - Rust

CubicConfig

Struct CubicConfig 

Source
pub struct CubicConfig { /* private fields */ }
Expand description

Configuration for the Cubic congestion controller

+

Implementations§

Source§

impl CubicConfig

Source

pub fn initial_window(&mut self, value: u64) -> &mut CubicConfig

Default limit on the amount of outstanding data in bytes.

+

Recommended value: min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))

+

Trait Implementations§

Source§

impl Clone for CubicConfig

Source§

fn clone(&self) -> CubicConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for CubicConfig

Source§

fn build( + self: Arc<CubicConfig>, + now: Instant, + current_mtu: u16, +) -> Box<dyn Controller>

Construct a fresh Controller
Source§

impl Debug for CubicConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for CubicConfig

Source§

fn default() -> CubicConfig

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/congestion/struct.NewReno.html b/pr/395/docs/iroh_quinn/congestion/struct.NewReno.html new file mode 100644 index 000000000..d95229378 --- /dev/null +++ b/pr/395/docs/iroh_quinn/congestion/struct.NewReno.html @@ -0,0 +1,52 @@ +NewReno in iroh_quinn::congestion - Rust

NewReno

Struct NewReno 

Source
pub struct NewReno { /* private fields */ }
Expand description

A simple, standard congestion controller

+

Implementations§

Source§

impl NewReno

Source

pub fn new( + config: Arc<NewRenoConfig>, + now: Instant, + current_mtu: u16, +) -> NewReno

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for NewReno

Source§

fn clone(&self) -> NewReno

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Controller for NewReno

Source§

fn on_ack( + &mut self, + _now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + _rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + _is_ecn: bool, + _lost_bytes: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated
Source§

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight
Source§

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled
Source§

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

fn into_any(self: Box<NewReno>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details
Source§

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent
Source§

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

impl Debug for NewReno

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/congestion/struct.NewRenoConfig.html b/pr/395/docs/iroh_quinn/congestion/struct.NewRenoConfig.html new file mode 100644 index 000000000..89d6dd734 --- /dev/null +++ b/pr/395/docs/iroh_quinn/congestion/struct.NewRenoConfig.html @@ -0,0 +1,34 @@ +NewRenoConfig in iroh_quinn::congestion - Rust

NewRenoConfig

Struct NewRenoConfig 

Source
pub struct NewRenoConfig { /* private fields */ }
Expand description

Configuration for the NewReno congestion controller

+

Implementations§

Source§

impl NewRenoConfig

Source

pub fn initial_window(&mut self, value: u64) -> &mut NewRenoConfig

Default limit on the amount of outstanding data in bytes.

+

Recommended value: min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))

+
Source

pub fn loss_reduction_factor(&mut self, value: f32) -> &mut NewRenoConfig

Reduction in congestion window when a new loss event is detected.

+

Trait Implementations§

Source§

impl Clone for NewRenoConfig

Source§

fn clone(&self) -> NewRenoConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for NewRenoConfig

Source§

fn build( + self: Arc<NewRenoConfig>, + now: Instant, + current_mtu: u16, +) -> Box<dyn Controller>

Construct a fresh Controller
Source§

impl Debug for NewRenoConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for NewRenoConfig

Source§

fn default() -> NewRenoConfig

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/congestion/trait.Controller.html b/pr/395/docs/iroh_quinn/congestion/trait.Controller.html new file mode 100644 index 000000000..e0e5882dd --- /dev/null +++ b/pr/395/docs/iroh_quinn/congestion/trait.Controller.html @@ -0,0 +1,79 @@ +Controller in iroh_quinn::congestion - Rust

Controller

Trait Controller 

Source
pub trait Controller:
+    Send
+    + Sync
+    + Debug {
+    // Required methods
+    fn on_congestion_event(
+        &mut self,
+        now: Instant,
+        sent: Instant,
+        is_persistent_congestion: bool,
+        is_ecn: bool,
+        lost_bytes: u64,
+    );
+    fn on_mtu_update(&mut self, new_mtu: u16);
+    fn window(&self) -> u64;
+    fn clone_box(&self) -> Box<dyn Controller>;
+    fn initial_window(&self) -> u64;
+    fn into_any(self: Box<Self>) -> Box<dyn Any>;
+
+    // Provided methods
+    fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64) { ... }
+    fn on_ack(
+        &mut self,
+        now: Instant,
+        sent: Instant,
+        bytes: u64,
+        app_limited: bool,
+        rtt: &RttEstimator,
+    ) { ... }
+    fn on_end_acks(
+        &mut self,
+        now: Instant,
+        in_flight: u64,
+        app_limited: bool,
+        largest_packet_num_acked: Option<u64>,
+    ) { ... }
+    fn on_spurious_congestion_event(&mut self) { ... }
+    fn metrics(&self) -> ControllerMetrics { ... }
+}
Expand description

Common interface for different congestion controllers

+

Required Methods§

Source

fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + is_ecn: bool, + lost_bytes: u64, +)

Packets were deemed lost or marked congested

+

in_persistent_congestion indicates whether all packets sent within the persistent +congestion threshold period ending when the most recent packet in this batch was sent were +lost. +lost_bytes indicates how many bytes were lost. This value will be 0 for ECN triggers.

+
Source

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated

+
Source

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight

+
Source

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state

+
Source

fn initial_window(&self) -> u64

Initial congestion window

+
Source

fn into_any(self: Box<Self>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details

+

Provided Methods§

Source

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent

+
Source

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + rtt: &RttEstimator, +)

Packet deliveries were confirmed

+

app_limited indicates whether the connection was blocked on outgoing +application data prior to receiving these acknowledgements.

+
Source

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.

+
Source

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost

+

This function is called when all packets that were deemed lost (for instance because +of packet reordering) are acknowledged after the congestion event was raised.

+
Source

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/congestion/trait.ControllerFactory.html b/pr/395/docs/iroh_quinn/congestion/trait.ControllerFactory.html new file mode 100644 index 000000000..4b3d57ea9 --- /dev/null +++ b/pr/395/docs/iroh_quinn/congestion/trait.ControllerFactory.html @@ -0,0 +1,10 @@ +ControllerFactory in iroh_quinn::congestion - Rust

ControllerFactory

Trait ControllerFactory 

Source
pub trait ControllerFactory {
+    // Required method
+    fn build(
+        self: Arc<Self>,
+        now: Instant,
+        current_mtu: u16,
+    ) -> Box<dyn Controller>;
+}
Expand description

Constructs controllers on demand

+

Required Methods§

Source

fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller>

Construct a fresh Controller

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/connection/enum.SendDatagramError.html b/pr/395/docs/iroh_quinn/connection/enum.SendDatagramError.html new file mode 100644 index 000000000..8400f90c1 --- /dev/null +++ b/pr/395/docs/iroh_quinn/connection/enum.SendDatagramError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/enum.SendDatagramError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/connection/struct.AcceptBi.html b/pr/395/docs/iroh_quinn/connection/struct.AcceptBi.html new file mode 100644 index 000000000..d2f02bb8c --- /dev/null +++ b/pr/395/docs/iroh_quinn/connection/struct.AcceptBi.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.AcceptBi.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/connection/struct.AcceptUni.html b/pr/395/docs/iroh_quinn/connection/struct.AcceptUni.html new file mode 100644 index 000000000..175c32716 --- /dev/null +++ b/pr/395/docs/iroh_quinn/connection/struct.AcceptUni.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.AcceptUni.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/connection/struct.Connecting.html b/pr/395/docs/iroh_quinn/connection/struct.Connecting.html new file mode 100644 index 000000000..019f5b84c --- /dev/null +++ b/pr/395/docs/iroh_quinn/connection/struct.Connecting.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.Connecting.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/connection/struct.Connection.html b/pr/395/docs/iroh_quinn/connection/struct.Connection.html new file mode 100644 index 000000000..b2c74be20 --- /dev/null +++ b/pr/395/docs/iroh_quinn/connection/struct.Connection.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.Connection.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/connection/struct.OnClosed.html b/pr/395/docs/iroh_quinn/connection/struct.OnClosed.html new file mode 100644 index 000000000..c7c8fb856 --- /dev/null +++ b/pr/395/docs/iroh_quinn/connection/struct.OnClosed.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.OnClosed.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/connection/struct.OpenBi.html b/pr/395/docs/iroh_quinn/connection/struct.OpenBi.html new file mode 100644 index 000000000..7b7818a64 --- /dev/null +++ b/pr/395/docs/iroh_quinn/connection/struct.OpenBi.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.OpenBi.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/connection/struct.OpenUni.html b/pr/395/docs/iroh_quinn/connection/struct.OpenUni.html new file mode 100644 index 000000000..eedabce57 --- /dev/null +++ b/pr/395/docs/iroh_quinn/connection/struct.OpenUni.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.OpenUni.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/connection/struct.ReadDatagram.html b/pr/395/docs/iroh_quinn/connection/struct.ReadDatagram.html new file mode 100644 index 000000000..c7e97d172 --- /dev/null +++ b/pr/395/docs/iroh_quinn/connection/struct.ReadDatagram.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.ReadDatagram.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/connection/struct.SendDatagram.html b/pr/395/docs/iroh_quinn/connection/struct.SendDatagram.html new file mode 100644 index 000000000..78249fc09 --- /dev/null +++ b/pr/395/docs/iroh_quinn/connection/struct.SendDatagram.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.SendDatagram.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/connection/struct.WeakConnectionHandle.html b/pr/395/docs/iroh_quinn/connection/struct.WeakConnectionHandle.html new file mode 100644 index 000000000..1496376f0 --- /dev/null +++ b/pr/395/docs/iroh_quinn/connection/struct.WeakConnectionHandle.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.WeakConnectionHandle.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/connection/struct.ZeroRttAccepted.html b/pr/395/docs/iroh_quinn/connection/struct.ZeroRttAccepted.html new file mode 100644 index 000000000..5c7de33e5 --- /dev/null +++ b/pr/395/docs/iroh_quinn/connection/struct.ZeroRttAccepted.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.ZeroRttAccepted.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/index.html b/pr/395/docs/iroh_quinn/crypto/index.html new file mode 100644 index 000000000..615c48abe --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/index.html @@ -0,0 +1,8 @@ +iroh_quinn::crypto - Rust

Module crypto

Module crypto 

Source
Expand description

Traits and implementations for the QUIC cryptography protocol

+

The protocol logic in Quinn is contained in types that abstract over the actual +cryptographic protocol used. This module contains the traits used for this +abstraction layer as well as a single implementation of these traits that uses +ring and rustls to implement the TLS protocol support.

+

Note that usage of any protocol (version) other than TLS 1.3 does not conform to any +published versions of the specification, and will not be supported in QUIC v1.

+

Modules§

rustls
TLS interface based on rustls

Structs§

CryptoError
Generic crypto errors
ExportKeyingMaterialError
Error returned by Session::export_keying_material.
KeyPair
A pair of keys for bidirectional communication
Keys
A complete set of keys for a certain packet space
UnsupportedVersion
Error indicating that the specified QUIC version is not supported

Traits§

AeadKey
A key for sealing data with AEAD-based algorithms
ClientConfig
Client-side configuration for the crypto protocol
HandshakeTokenKey
A pseudo random key for HKDF
HeaderKey
Keys used to protect packet headers
HmacKey
A key for signing with HMAC-based algorithms
PacketKey
Keys used to protect packet payloads
ServerConfig
Server-side configuration for the crypto protocol
Session
A cryptographic session (commonly TLS)
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/rustls/enum.Error.html b/pr/395/docs/iroh_quinn/crypto/rustls/enum.Error.html new file mode 100644 index 000000000..4dbacc902 --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/rustls/enum.Error.html @@ -0,0 +1,101 @@ +Error in iroh_quinn::crypto::rustls - Rust

Error

Enum Error 

#[non_exhaustive]
pub enum Error { +
Show 22 variants InappropriateMessage { + expect_types: Vec<ContentType>, + got_type: ContentType, + }, + InappropriateHandshakeMessage { + expect_types: Vec<HandshakeType>, + got_type: HandshakeType, + }, + InvalidEncryptedClientHello(EncryptedClientHelloError), + InvalidMessage(InvalidMessage), + NoCertificatesPresented, + UnsupportedNameType, + DecryptError, + EncryptError, + PeerIncompatible(PeerIncompatible), + PeerMisbehaved(PeerMisbehaved), + AlertReceived(AlertDescription), + InvalidCertificate(CertificateError), + InvalidCertRevocationList(CertRevocationListError), + General(String), + FailedToGetCurrentTime, + FailedToGetRandomBytes, + HandshakeNotComplete, + PeerSentOversizedRecord, + NoApplicationProtocol, + BadMaxFragmentSize, + InconsistentKeys(InconsistentKeys), + Other(OtherError), +
}
Expand description

rustls reports protocol errors using this type.

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

InappropriateMessage

We received a TLS message that isn’t valid right now. +expect_types lists the message types we can expect right now. +got_type is the type we found. This error is typically +caused by a buggy TLS stack (the peer or this one), a broken +network, or an attack.

+

Fields

§expect_types: Vec<ContentType>

Which types we expected

+
§got_type: ContentType

What type we received

+
§

InappropriateHandshakeMessage

We received a TLS handshake message that isn’t valid right now. +expect_types lists the handshake message types we can expect +right now. got_type is the type we found.

+

Fields

§expect_types: Vec<HandshakeType>

Which handshake type we expected

+
§got_type: HandshakeType

What handshake type we received

+
§

InvalidEncryptedClientHello(EncryptedClientHelloError)

An error occurred while handling Encrypted Client Hello (ECH).

+
§

InvalidMessage(InvalidMessage)

The peer sent us a TLS message with invalid contents.

+
§

NoCertificatesPresented

The peer didn’t give us any certificates.

+
§

UnsupportedNameType

The certificate verifier doesn’t support the given type of name.

+
§

DecryptError

We couldn’t decrypt a message. This is invariably fatal.

+
§

EncryptError

We couldn’t encrypt a message because it was larger than the allowed message size. +This should never happen if the application is using valid record sizes.

+
§

PeerIncompatible(PeerIncompatible)

The peer doesn’t support a protocol version/feature we require. +The parameter gives a hint as to what version/feature it is.

+
§

PeerMisbehaved(PeerMisbehaved)

The peer deviated from the standard TLS protocol. +The parameter gives a hint where.

+
§

AlertReceived(AlertDescription)

We received a fatal alert. This means the peer is unhappy.

+
§

InvalidCertificate(CertificateError)

We saw an invalid certificate.

+

The contained error is from the certificate validation trait +implementation.

+
§

InvalidCertRevocationList(CertRevocationListError)

A provided certificate revocation list (CRL) was invalid.

+
§

General(String)

A catch-all error for unlikely errors.

+
§

FailedToGetCurrentTime

We failed to figure out what time it currently is.

+
§

FailedToGetRandomBytes

We failed to acquire random bytes from the system.

+
§

HandshakeNotComplete

This function doesn’t work until the TLS handshake +is complete.

+
§

PeerSentOversizedRecord

The peer sent an oversized record/fragment.

+
§

NoApplicationProtocol

An incoming connection did not support any known application protocol.

+
§

BadMaxFragmentSize

The max_fragment_size value supplied in configuration was too small, +or too large.

+
§

InconsistentKeys(InconsistentKeys)

Specific failure cases from keys_match or a [crate::crypto::signer::SigningKey] that cannot produce a corresponding public key.

+
§

Other(OtherError)

Any other error.

+

This variant should only be used when the error is not better described by a more +specific variant. For example, if a custom crypto provider returns a +provider specific error.

+

Enums holding this variant will never compare equal to each other.

+

Trait Implementations§

§

impl Clone for Error

§

fn clone(&self) -> Error

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for Error

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl Display for Error

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl Error for Error

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
§

impl From<CertRevocationListError> for Error

§

fn from(e: CertRevocationListError) -> Error

Converts to this type from the input type.
§

impl From<CertificateError> for Error

§

fn from(e: CertificateError) -> Error

Converts to this type from the input type.
§

impl From<EncryptedClientHelloError> for Error

§

fn from(e: EncryptedClientHelloError) -> Error

Converts to this type from the input type.
§

impl From<GetRandomFailed> for Error

§

fn from(_: GetRandomFailed) -> Error

Converts to this type from the input type.
§

impl From<InconsistentKeys> for Error

§

fn from(e: InconsistentKeys) -> Error

Converts to this type from the input type.
§

impl From<InvalidMessage> for Error

§

fn from(e: InvalidMessage) -> Error

Converts to this type from the input type.
§

impl From<OtherError> for Error

§

fn from(value: OtherError) -> Error

Converts to this type from the input type.
§

impl From<PeerIncompatible> for Error

§

fn from(e: PeerIncompatible) -> Error

Converts to this type from the input type.
§

impl From<PeerMisbehaved> for Error

§

fn from(e: PeerMisbehaved) -> Error

Converts to this type from the input type.
§

impl From<SystemTimeError> for Error

§

fn from(_: SystemTimeError) -> Error

Converts to this type from the input type.
§

impl From<UnsupportedOperationError> for Error

§

fn from(value: UnsupportedOperationError) -> Error

Converts to this type from the input type.
§

impl PartialEq for Error

§

fn eq(&self, other: &Error) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
§

impl StructuralPartialEq for Error

Auto Trait Implementations§

§

impl Freeze for Error

§

impl !RefUnwindSafe for Error

§

impl Send for Error

§

impl Sync for Error

§

impl Unpin for Error

§

impl !UnwindSafe for Error

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/rustls/index.html b/pr/395/docs/iroh_quinn/crypto/rustls/index.html new file mode 100644 index 000000000..408115e6c --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/rustls/index.html @@ -0,0 +1,2 @@ +iroh_quinn::crypto::rustls - Rust

Module rustls

Module rustls 

Source
Expand description

TLS interface based on rustls

+

Structs§

HandshakeData
Authentication data for (rustls) TLS session
NoInitialCipherSuite
The initial cipher suite (AES-128-GCM-SHA256) is not available
QuicClientConfig
A QUIC-compatible TLS client configuration
QuicServerConfig
A QUIC-compatible TLS server configuration
TlsSession
A rustls TLS session

Enums§

Error
rustls reports protocol errors using this type.
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/rustls/sidebar-items.js b/pr/395/docs/iroh_quinn/crypto/rustls/sidebar-items.js new file mode 100644 index 000000000..65f98d4d4 --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/rustls/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Error"],"struct":["HandshakeData","NoInitialCipherSuite","QuicClientConfig","QuicServerConfig","TlsSession"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/rustls/struct.HandshakeData.html b/pr/395/docs/iroh_quinn/crypto/rustls/struct.HandshakeData.html new file mode 100644 index 000000000..7c8a7b1cf --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/rustls/struct.HandshakeData.html @@ -0,0 +1,34 @@ +HandshakeData in iroh_quinn::crypto::rustls - Rust

HandshakeData

Struct HandshakeData 

Source
pub struct HandshakeData {
+    pub protocol: Option<Vec<u8>>,
+    pub server_name: Option<String>,
+    pub negotiated_key_exchange_group: NamedGroup,
+}
Expand description

Authentication data for (rustls) TLS session

+

Fields§

§protocol: Option<Vec<u8>>

The negotiated application protocol, if ALPN is in use

+

Guaranteed to be set if a nonempty list of protocols was specified for this connection.

+
§server_name: Option<String>

The server name specified by the client, if any

+

Always None for outgoing connections

+
§negotiated_key_exchange_group: NamedGroup

The key exchange group negotiated with the peer

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/rustls/struct.NoInitialCipherSuite.html b/pr/395/docs/iroh_quinn/crypto/rustls/struct.NoInitialCipherSuite.html new file mode 100644 index 000000000..1ce33bf8b --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/rustls/struct.NoInitialCipherSuite.html @@ -0,0 +1,31 @@ +NoInitialCipherSuite in iroh_quinn::crypto::rustls - Rust

NoInitialCipherSuite

Struct NoInitialCipherSuite 

Source
pub struct NoInitialCipherSuite { /* private fields */ }
Expand description

The initial cipher suite (AES-128-GCM-SHA256) is not available

+

When the cipher suite is supplied with_initial(), it must be +[CipherSuite::TLS13_AES_128_GCM_SHA256]. When the cipher suite is derived from a config’s +CryptoProvider, that provider must reference a cipher suite with the same ID.

+

Trait Implementations§

Source§

impl Clone for NoInitialCipherSuite

Source§

fn clone(&self) -> NoInitialCipherSuite

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for NoInitialCipherSuite

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for NoInitialCipherSuite

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for NoInitialCipherSuite

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/rustls/struct.QuicClientConfig.html b/pr/395/docs/iroh_quinn/crypto/rustls/struct.QuicClientConfig.html new file mode 100644 index 000000000..54d06690b --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/rustls/struct.QuicClientConfig.html @@ -0,0 +1,52 @@ +QuicClientConfig in iroh_quinn::crypto::rustls - Rust

QuicClientConfig

Struct QuicClientConfig 

Source
pub struct QuicClientConfig { /* private fields */ }
Expand description

A QUIC-compatible TLS client configuration

+

Quinn implicitly constructs a QuicClientConfig with reasonable defaults within +ClientConfig::with_root_certificates() and ClientConfig::try_with_platform_verifier(). +Alternatively, QuicClientConfig’s TryFrom implementation can be used to wrap around a +custom [rustls::ClientConfig], in which case care should be taken around certain points:

+
    +
  • If enable_early_data is not set to true, then sending 0-RTT data will not be possible on +outgoing connections.
  • +
  • The [rustls::ClientConfig] must have TLS 1.3 support enabled for conversion to succeed.
  • +
+

The object in the resumption field of the inner [rustls::ClientConfig] determines whether +calling into_0rtt on outgoing connections returns Ok or Err. It typically allows +into_0rtt to proceed if it recognizes the server name, and defaults to an in-memory cache of +256 server names.

+

Implementations§

Source§

impl QuicClientConfig

Source

pub fn with_initial( + inner: Arc<ClientConfig>, + initial: Suite, +) -> Result<QuicClientConfig, NoInitialCipherSuite>

Initialize a QUIC-compatible TLS client configuration with a separate initial cipher suite

+

This is useful if you want to avoid the initial cipher suite for traffic encryption.

+

Trait Implementations§

Source§

impl ClientConfig for QuicClientConfig

Source§

fn start_session( + self: Arc<QuicClientConfig>, + version: u32, + server_name: &str, + params: &TransportParameters, +) -> Result<Box<dyn Session>, ConnectError>

Start a client session with this configuration
Source§

impl TryFrom<Arc<ClientConfig>> for QuicClientConfig

Source§

type Error = NoInitialCipherSuite

The type returned in the event of a conversion error.
Source§

fn try_from( + inner: Arc<ClientConfig>, +) -> Result<QuicClientConfig, <QuicClientConfig as TryFrom<Arc<ClientConfig>>>::Error>

Performs the conversion.
Source§

impl TryFrom<ClientConfig> for QuicClientConfig

Source§

type Error = NoInitialCipherSuite

The type returned in the event of a conversion error.
Source§

fn try_from( + inner: ClientConfig, +) -> Result<QuicClientConfig, <QuicClientConfig as TryFrom<ClientConfig>>::Error>

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/rustls/struct.QuicServerConfig.html b/pr/395/docs/iroh_quinn/crypto/rustls/struct.QuicServerConfig.html new file mode 100644 index 000000000..a80bf2aad --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/rustls/struct.QuicServerConfig.html @@ -0,0 +1,56 @@ +QuicServerConfig in iroh_quinn::crypto::rustls - Rust

QuicServerConfig

Struct QuicServerConfig 

Source
pub struct QuicServerConfig { /* private fields */ }
Expand description

A QUIC-compatible TLS server configuration

+

Quinn implicitly constructs a QuicServerConfig with reasonable defaults within +ServerConfig::with_single_cert(). Alternatively, QuicServerConfig’s TryFrom +implementation or with_initial method can be used to wrap around a custom +[rustls::ServerConfig], in which case care should be taken around certain points:

+
    +
  • If max_early_data_size is not set to u32::MAX, the server will not be able to accept +incoming 0-RTT data. QUIC prohibits max_early_data_size values other than 0 or u32::MAX.
  • +
  • The rustls::ServerConfig must have TLS 1.3 support enabled for conversion to succeed.
  • +
+

Implementations§

Source§

impl QuicServerConfig

Source

pub fn with_initial( + inner: Arc<ServerConfig>, + initial: Suite, +) -> Result<QuicServerConfig, NoInitialCipherSuite>

Initialize a QUIC-compatible TLS client configuration with a separate initial cipher suite

+

This is useful if you want to avoid the initial cipher suite for traffic encryption.

+

Trait Implementations§

Source§

impl ServerConfig for QuicServerConfig

Source§

fn start_session( + self: Arc<QuicServerConfig>, + version: u32, + params: &TransportParameters, +) -> Box<dyn Session>

Start a server session with this configuration Read more
Source§

fn initial_keys( + &self, + version: u32, + dst_cid: ConnectionId, +) -> Result<Keys, UnsupportedVersion>

Create the initial set of keys given the client’s initial destination ConnectionId
Source§

fn retry_tag( + &self, + version: u32, + orig_dst_cid: ConnectionId, + packet: &[u8], +) -> [u8; 16]

Generate the integrity tag for a retry packet Read more
Source§

impl TryFrom<Arc<ServerConfig>> for QuicServerConfig

Source§

type Error = NoInitialCipherSuite

The type returned in the event of a conversion error.
Source§

fn try_from( + inner: Arc<ServerConfig>, +) -> Result<QuicServerConfig, <QuicServerConfig as TryFrom<Arc<ServerConfig>>>::Error>

Performs the conversion.
Source§

impl TryFrom<ServerConfig> for QuicServerConfig

Source§

type Error = NoInitialCipherSuite

The type returned in the event of a conversion error.
Source§

fn try_from( + inner: ServerConfig, +) -> Result<QuicServerConfig, <QuicServerConfig as TryFrom<ServerConfig>>::Error>

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/rustls/struct.TlsSession.html b/pr/395/docs/iroh_quinn/crypto/rustls/struct.TlsSession.html new file mode 100644 index 000000000..55fa0eedd --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/rustls/struct.TlsSession.html @@ -0,0 +1,38 @@ +TlsSession in iroh_quinn::crypto::rustls - Rust

TlsSession

Struct TlsSession 

Source
pub struct TlsSession { /* private fields */ }
Expand description

A rustls TLS session

+

Trait Implementations§

Source§

impl Session for TlsSession

Source§

fn peer_identity(&self) -> Option<Box<dyn Any>>

For the rustls TlsSession, the Any type is Vec<rustls::pki_types::CertificateDer>

+
Source§

fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys

Create the initial set of keys given the client’s initial destination ConnectionId
Source§

fn handshake_data(&self) -> Option<Box<dyn Any>>

Get data negotiated during the handshake, if available Read more
Source§

fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>

Get the 0-RTT keys if available (clients only) Read more
Source§

fn early_data_accepted(&self) -> Option<bool>

If the 0-RTT-encrypted data has been accepted by the peer
Source§

fn is_handshaking(&self) -> bool

Returns true until the connection is fully established.
Source§

fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, Error>

Read bytes of handshake data Read more
Source§

fn transport_parameters(&self) -> Result<Option<TransportParameters>, Error>

The peer’s QUIC transport parameters Read more
Source§

fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>

Writes handshake bytes into the given buffer and optionally returns the negotiated keys Read more
Source§

fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>

Compute keys for the next key update
Source§

fn is_valid_retry( + &self, + orig_dst_cid: ConnectionId, + header: &[u8], + payload: &[u8], +) -> bool

Verify the integrity of a retry packet
Source§

fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: &[u8], +) -> Result<(), ExportKeyingMaterialError>

Fill output with output.len() bytes of keying material derived +from the Session’s secrets, using label and context for domain +separation. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/sidebar-items.js b/pr/395/docs/iroh_quinn/crypto/sidebar-items.js new file mode 100644 index 000000000..375413e92 --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["rustls"],"struct":["CryptoError","ExportKeyingMaterialError","KeyPair","Keys","UnsupportedVersion"],"trait":["AeadKey","ClientConfig","HandshakeTokenKey","HeaderKey","HmacKey","PacketKey","ServerConfig","Session"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/struct.CryptoError.html b/pr/395/docs/iroh_quinn/crypto/struct.CryptoError.html new file mode 100644 index 000000000..c7e440336 --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/struct.CryptoError.html @@ -0,0 +1,25 @@ +CryptoError in iroh_quinn::crypto - Rust

CryptoError

Struct CryptoError 

Source
pub struct CryptoError;
Expand description

Generic crypto errors

+

Trait Implementations§

Source§

impl Debug for CryptoError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl From<Unspecified> for CryptoError

Source§

fn from(_: Unspecified) -> CryptoError

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/struct.ExportKeyingMaterialError.html b/pr/395/docs/iroh_quinn/crypto/struct.ExportKeyingMaterialError.html new file mode 100644 index 000000000..40bed3edb --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/struct.ExportKeyingMaterialError.html @@ -0,0 +1,31 @@ +ExportKeyingMaterialError in iroh_quinn::crypto - Rust

ExportKeyingMaterialError

Struct ExportKeyingMaterialError 

Source
pub struct ExportKeyingMaterialError;
Expand description

Error returned by Session::export_keying_material.

+

This error occurs if the requested output length is too large.

+

Trait Implementations§

Source§

impl Debug for ExportKeyingMaterialError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl PartialEq for ExportKeyingMaterialError

Source§

fn eq(&self, other: &ExportKeyingMaterialError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ExportKeyingMaterialError

Source§

impl StructuralPartialEq for ExportKeyingMaterialError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/struct.KeyPair.html b/pr/395/docs/iroh_quinn/crypto/struct.KeyPair.html new file mode 100644 index 000000000..20757ff04 --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/struct.KeyPair.html @@ -0,0 +1,36 @@ +KeyPair in iroh_quinn::crypto - Rust

KeyPair

Struct KeyPair 

Source
pub struct KeyPair<T> {
+    pub local: T,
+    pub remote: T,
+}
Expand description

A pair of keys for bidirectional communication

+

Fields§

§local: T

Key for encrypting data

+
§remote: T

Key for decrypting data

+

Auto Trait Implementations§

§

impl<T> Freeze for KeyPair<T>
where + T: Freeze,

§

impl<T> RefUnwindSafe for KeyPair<T>
where + T: RefUnwindSafe,

§

impl<T> Send for KeyPair<T>
where + T: Send,

§

impl<T> Sync for KeyPair<T>
where + T: Sync,

§

impl<T> Unpin for KeyPair<T>
where + T: Unpin,

§

impl<T> UnwindSafe for KeyPair<T>
where + T: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/struct.Keys.html b/pr/395/docs/iroh_quinn/crypto/struct.Keys.html new file mode 100644 index 000000000..491208804 --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/struct.Keys.html @@ -0,0 +1,30 @@ +Keys in iroh_quinn::crypto - Rust

Keys

Struct Keys 

Source
pub struct Keys {
+    pub header: KeyPair<Box<dyn HeaderKey>>,
+    pub packet: KeyPair<Box<dyn PacketKey>>,
+}
Expand description

A complete set of keys for a certain packet space

+

Fields§

§header: KeyPair<Box<dyn HeaderKey>>

Header protection keys

+
§packet: KeyPair<Box<dyn PacketKey>>

Packet protection keys

+

Auto Trait Implementations§

§

impl Freeze for Keys

§

impl !RefUnwindSafe for Keys

§

impl Send for Keys

§

impl Sync for Keys

§

impl Unpin for Keys

§

impl !UnwindSafe for Keys

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/struct.UnsupportedVersion.html b/pr/395/docs/iroh_quinn/crypto/struct.UnsupportedVersion.html new file mode 100644 index 000000000..6e6350375 --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/struct.UnsupportedVersion.html @@ -0,0 +1,25 @@ +UnsupportedVersion in iroh_quinn::crypto - Rust

UnsupportedVersion

Struct UnsupportedVersion 

Source
pub struct UnsupportedVersion;
Expand description

Error indicating that the specified QUIC version is not supported

+

Trait Implementations§

Source§

impl Debug for UnsupportedVersion

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl From<UnsupportedVersion> for ConnectError

Source§

fn from(_: UnsupportedVersion) -> ConnectError

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/trait.AeadKey.html b/pr/395/docs/iroh_quinn/crypto/trait.AeadKey.html new file mode 100644 index 000000000..c587dd06a --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/trait.AeadKey.html @@ -0,0 +1,32 @@ +AeadKey in iroh_quinn::crypto - Rust

AeadKey

Trait AeadKey 

Source
pub trait AeadKey {
+    // Required methods
+    fn seal(
+        &self,
+        data: &mut Vec<u8>,
+        additional_data: &[u8],
+    ) -> Result<(), CryptoError>;
+    fn open<'a>(
+        &self,
+        data: &'a mut [u8],
+        additional_data: &[u8],
+    ) -> Result<&'a mut [u8], CryptoError>;
+}
Expand description

A key for sealing data with AEAD-based algorithms

+

Required Methods§

Source

fn seal( + &self, + data: &mut Vec<u8>, + additional_data: &[u8], +) -> Result<(), CryptoError>

Method for sealing message data

+
Source

fn open<'a>( + &self, + data: &'a mut [u8], + additional_data: &[u8], +) -> Result<&'a mut [u8], CryptoError>

Method for opening a sealed message data

+

Implementations on Foreign Types§

Source§

impl AeadKey for LessSafeKey

Source§

fn seal( + &self, + data: &mut Vec<u8>, + additional_data: &[u8], +) -> Result<(), CryptoError>

Source§

fn open<'a>( + &self, + data: &'a mut [u8], + additional_data: &[u8], +) -> Result<&'a mut [u8], CryptoError>

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/trait.ClientConfig.html b/pr/395/docs/iroh_quinn/crypto/trait.ClientConfig.html new file mode 100644 index 000000000..65375c0ab --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/trait.ClientConfig.html @@ -0,0 +1,16 @@ +ClientConfig in iroh_quinn::crypto - Rust

ClientConfig

Trait ClientConfig 

Source
pub trait ClientConfig: Send + Sync {
+    // Required method
+    fn start_session(
+        self: Arc<Self>,
+        version: u32,
+        server_name: &str,
+        params: &TransportParameters,
+    ) -> Result<Box<dyn Session>, ConnectError>;
+}
Expand description

Client-side configuration for the crypto protocol

+

Required Methods§

Source

fn start_session( + self: Arc<Self>, + version: u32, + server_name: &str, + params: &TransportParameters, +) -> Result<Box<dyn Session>, ConnectError>

Start a client session with this configuration

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/trait.HandshakeTokenKey.html b/pr/395/docs/iroh_quinn/crypto/trait.HandshakeTokenKey.html new file mode 100644 index 000000000..59f558014 --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/trait.HandshakeTokenKey.html @@ -0,0 +1,6 @@ +HandshakeTokenKey in iroh_quinn::crypto - Rust

HandshakeTokenKey

Trait HandshakeTokenKey 

Source
pub trait HandshakeTokenKey: Send + Sync {
+    // Required method
+    fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>;
+}
Expand description

A pseudo random key for HKDF

+

Required Methods§

Source

fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>

Derive AEAD using hkdf

+

Implementations on Foreign Types§

Source§

impl HandshakeTokenKey for Prk

Source§

fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/trait.HeaderKey.html b/pr/395/docs/iroh_quinn/crypto/trait.HeaderKey.html new file mode 100644 index 000000000..414ce78e4 --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/trait.HeaderKey.html @@ -0,0 +1,10 @@ +HeaderKey in iroh_quinn::crypto - Rust

HeaderKey

Trait HeaderKey 

Source
pub trait HeaderKey: Send + Sync {
+    // Required methods
+    fn decrypt(&self, pn_offset: usize, packet: &mut [u8]);
+    fn encrypt(&self, pn_offset: usize, packet: &mut [u8]);
+    fn sample_size(&self) -> usize;
+}
Expand description

Keys used to protect packet headers

+

Required Methods§

Source

fn decrypt(&self, pn_offset: usize, packet: &mut [u8])

Decrypt the given packet’s header

+
Source

fn encrypt(&self, pn_offset: usize, packet: &mut [u8])

Encrypt the given packet’s header

+
Source

fn sample_size(&self) -> usize

The sample size used for this key’s algorithm

+

Implementations on Foreign Types§

Source§

impl HeaderKey for Box<dyn HeaderProtectionKey>

Source§

fn decrypt(&self, pn_offset: usize, packet: &mut [u8])

Source§

fn encrypt(&self, pn_offset: usize, packet: &mut [u8])

Source§

fn sample_size(&self) -> usize

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/trait.HmacKey.html b/pr/395/docs/iroh_quinn/crypto/trait.HmacKey.html new file mode 100644 index 000000000..1a6f96b11 --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/trait.HmacKey.html @@ -0,0 +1,10 @@ +HmacKey in iroh_quinn::crypto - Rust

HmacKey

Trait HmacKey 

Source
pub trait HmacKey: Send + Sync {
+    // Required methods
+    fn sign(&self, data: &[u8], signature_out: &mut [u8]);
+    fn signature_len(&self) -> usize;
+    fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>;
+}
Expand description

A key for signing with HMAC-based algorithms

+

Required Methods§

Source

fn sign(&self, data: &[u8], signature_out: &mut [u8])

Method for signing a message

+
Source

fn signature_len(&self) -> usize

Length of sign’s output

+
Source

fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>

Method for verifying a message

+

Implementations on Foreign Types§

Source§

impl HmacKey for Key

Source§

fn sign(&self, data: &[u8], out: &mut [u8])

Source§

fn signature_len(&self) -> usize

Source§

fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/trait.PacketKey.html b/pr/395/docs/iroh_quinn/crypto/trait.PacketKey.html new file mode 100644 index 000000000..70d769abf --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/trait.PacketKey.html @@ -0,0 +1,51 @@ +PacketKey in iroh_quinn::crypto - Rust

PacketKey

Trait PacketKey 

Source
pub trait PacketKey: Send + Sync {
+    // Required methods
+    fn encrypt(
+        &self,
+        path_id: PathId,
+        packet: u64,
+        buf: &mut [u8],
+        header_len: usize,
+    );
+    fn decrypt(
+        &self,
+        path_id: PathId,
+        packet: u64,
+        header: &[u8],
+        payload: &mut BytesMut,
+    ) -> Result<(), CryptoError>;
+    fn tag_len(&self) -> usize;
+    fn confidentiality_limit(&self) -> u64;
+    fn integrity_limit(&self) -> u64;
+}
Expand description

Keys used to protect packet payloads

+

Required Methods§

Source

fn encrypt( + &self, + path_id: PathId, + packet: u64, + buf: &mut [u8], + header_len: usize, +)

Encrypt the packet payload with the given packet number

+
Source

fn decrypt( + &self, + path_id: PathId, + packet: u64, + header: &[u8], + payload: &mut BytesMut, +) -> Result<(), CryptoError>

Decrypt the packet payload with the given packet number

+
Source

fn tag_len(&self) -> usize

The length of the AEAD tag appended to packets on encryption

+
Source

fn confidentiality_limit(&self) -> u64

Maximum number of packets that may be sent using a single key

+
Source

fn integrity_limit(&self) -> u64

Maximum number of incoming packets that may fail decryption before the connection must be +abandoned

+

Implementations on Foreign Types§

Source§

impl PacketKey for Box<dyn PacketKey>

Source§

fn encrypt( + &self, + path_id: PathId, + packet: u64, + buf: &mut [u8], + header_len: usize, +)

Source§

fn decrypt( + &self, + path_id: PathId, + packet: u64, + header: &[u8], + payload: &mut BytesMut, +) -> Result<(), CryptoError>

Source§

fn tag_len(&self) -> usize

Source§

fn confidentiality_limit(&self) -> u64

Source§

fn integrity_limit(&self) -> u64

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/trait.ServerConfig.html b/pr/395/docs/iroh_quinn/crypto/trait.ServerConfig.html new file mode 100644 index 000000000..7e75b0427 --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/trait.ServerConfig.html @@ -0,0 +1,38 @@ +ServerConfig in iroh_quinn::crypto - Rust

ServerConfig

Trait ServerConfig 

Source
pub trait ServerConfig: Send + Sync {
+    // Required methods
+    fn initial_keys(
+        &self,
+        version: u32,
+        dst_cid: ConnectionId,
+    ) -> Result<Keys, UnsupportedVersion>;
+    fn retry_tag(
+        &self,
+        version: u32,
+        orig_dst_cid: ConnectionId,
+        packet: &[u8],
+    ) -> [u8; 16];
+    fn start_session(
+        self: Arc<Self>,
+        version: u32,
+        params: &TransportParameters,
+    ) -> Box<dyn Session>;
+}
Expand description

Server-side configuration for the crypto protocol

+

Required Methods§

Source

fn initial_keys( + &self, + version: u32, + dst_cid: ConnectionId, +) -> Result<Keys, UnsupportedVersion>

Create the initial set of keys given the client’s initial destination ConnectionId

+
Source

fn retry_tag( + &self, + version: u32, + orig_dst_cid: ConnectionId, + packet: &[u8], +) -> [u8; 16]

Generate the integrity tag for a retry packet

+

Never called if initial_keys rejected version.

+
Source

fn start_session( + self: Arc<Self>, + version: u32, + params: &TransportParameters, +) -> Box<dyn Session>

Start a server session with this configuration

+

Never called if initial_keys rejected version.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/crypto/trait.Session.html b/pr/395/docs/iroh_quinn/crypto/trait.Session.html new file mode 100644 index 000000000..cd1c5850c --- /dev/null +++ b/pr/395/docs/iroh_quinn/crypto/trait.Session.html @@ -0,0 +1,68 @@ +Session in iroh_quinn::crypto - Rust

Session

Trait Session 

Source
pub trait Session:
+    Send
+    + Sync
+    + 'static {
+    // Required methods
+    fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys;
+    fn handshake_data(&self) -> Option<Box<dyn Any>>;
+    fn peer_identity(&self) -> Option<Box<dyn Any>>;
+    fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>;
+    fn early_data_accepted(&self) -> Option<bool>;
+    fn is_handshaking(&self) -> bool;
+    fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, Error>;
+    fn transport_parameters(&self) -> Result<Option<TransportParameters>, Error>;
+    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>;
+    fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>;
+    fn is_valid_retry(
+        &self,
+        orig_dst_cid: ConnectionId,
+        header: &[u8],
+        payload: &[u8],
+    ) -> bool;
+    fn export_keying_material(
+        &self,
+        output: &mut [u8],
+        label: &[u8],
+        context: &[u8],
+    ) -> Result<(), ExportKeyingMaterialError>;
+}
Expand description

A cryptographic session (commonly TLS)

+

Required Methods§

Source

fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys

Create the initial set of keys given the client’s initial destination ConnectionId

+
Source

fn handshake_data(&self) -> Option<Box<dyn Any>>

Get data negotiated during the handshake, if available

+

Returns None until the connection emits HandshakeDataReady.

+
Source

fn peer_identity(&self) -> Option<Box<dyn Any>>

Get the peer’s identity, if available

+
Source

fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>

Get the 0-RTT keys if available (clients only)

+

On the client side, this method can be used to see if 0-RTT key material is available +to start sending data before the protocol handshake has completed.

+

Returns None if the key material is not available. This might happen if you have +not connected to this server before.

+
Source

fn early_data_accepted(&self) -> Option<bool>

If the 0-RTT-encrypted data has been accepted by the peer

+
Source

fn is_handshaking(&self) -> bool

Returns true until the connection is fully established.

+
Source

fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, Error>

Read bytes of handshake data

+

This should be called with the contents of CRYPTO frames. If it returns Ok, the +caller should call write_handshake() to check if the crypto protocol has anything +to send to the peer. This method will only return true the first time that +handshake data is available. Future calls will always return false.

+

On success, returns true iff self.handshake_data() has been populated.

+
Source

fn transport_parameters(&self) -> Result<Option<TransportParameters>, Error>

The peer’s QUIC transport parameters

+

These are only available after the first flight from the peer has been received.

+
Source

fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>

Writes handshake bytes into the given buffer and optionally returns the negotiated keys

+

When the handshake proceeds to the next phase, this method will return a new set of +keys to encrypt data with.

+
Source

fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>

Compute keys for the next key update

+
Source

fn is_valid_retry( + &self, + orig_dst_cid: ConnectionId, + header: &[u8], + payload: &[u8], +) -> bool

Verify the integrity of a retry packet

+
Source

fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: &[u8], +) -> Result<(), ExportKeyingMaterialError>

Fill output with output.len() bytes of keying material derived +from the Session’s secrets, using label and context for domain +separation.

+

This function will fail, returning ExportKeyingMaterialError, +if the requested output length is too large.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/endpoint/struct.Accept.html b/pr/395/docs/iroh_quinn/endpoint/struct.Accept.html new file mode 100644 index 000000000..4a9a29dbf --- /dev/null +++ b/pr/395/docs/iroh_quinn/endpoint/struct.Accept.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.Accept.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/endpoint/struct.Endpoint.html b/pr/395/docs/iroh_quinn/endpoint/struct.Endpoint.html new file mode 100644 index 000000000..8f85d0127 --- /dev/null +++ b/pr/395/docs/iroh_quinn/endpoint/struct.Endpoint.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.Endpoint.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/endpoint/struct.EndpointStats.html b/pr/395/docs/iroh_quinn/endpoint/struct.EndpointStats.html new file mode 100644 index 000000000..c95830426 --- /dev/null +++ b/pr/395/docs/iroh_quinn/endpoint/struct.EndpointStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.EndpointStats.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.ConfigError.html b/pr/395/docs/iroh_quinn/enum.ConfigError.html new file mode 100644 index 000000000..5ee8c912e --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.ConfigError.html @@ -0,0 +1,36 @@ +ConfigError in iroh_quinn - Rust

ConfigError

Enum ConfigError 

Source
#[non_exhaustive]
pub enum ConfigError { + OutOfBounds, +}
Expand description

Errors in the configuration of an endpoint

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

OutOfBounds

Value exceeds supported bounds

+

Trait Implementations§

Source§

impl Clone for ConfigError

Source§

fn clone(&self) -> ConfigError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConfigError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for ConfigError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for ConfigError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<TryFromIntError> for ConfigError

Source§

fn from(_: TryFromIntError) -> ConfigError

Converts to this type from the input type.
Source§

impl From<VarIntBoundsExceeded> for ConfigError

Source§

fn from(_: VarIntBoundsExceeded) -> ConfigError

Converts to this type from the input type.
Source§

impl PartialEq for ConfigError

Source§

fn eq(&self, other: &ConfigError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConfigError

Source§

impl StructuralPartialEq for ConfigError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.ConnectError.html b/pr/395/docs/iroh_quinn/enum.ConnectError.html new file mode 100644 index 000000000..e2081e7e0 --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.ConnectError.html @@ -0,0 +1,51 @@ +ConnectError in iroh_quinn - Rust

ConnectError

Enum ConnectError 

Source
pub enum ConnectError {
+    EndpointStopping,
+    CidsExhausted,
+    InvalidServerName(String),
+    InvalidRemoteAddress(SocketAddr),
+    NoDefaultClientConfig,
+    UnsupportedVersion,
+}
Expand description

Errors in the parameters being used to create a new connection

+

These arise before any I/O has been performed.

+

Variants§

§

EndpointStopping

The endpoint can no longer create new connections

+

Indicates that a necessary component of the endpoint has been dropped or otherwise disabled.

+
§

CidsExhausted

The connection could not be created because not enough of the CID space is available

+

Try using longer connection IDs

+
§

InvalidServerName(String)

The given server name was malformed

+
§

InvalidRemoteAddress(SocketAddr)

The remote SocketAddr supplied was malformed

+

Examples include attempting to connect to port 0, or using an inappropriate address family.

+
§

NoDefaultClientConfig

No default client configuration was set up

+

Use Endpoint::connect_with to specify a client configuration.

+
§

UnsupportedVersion

The local endpoint does not support the QUIC version specified in the client configuration

+

Trait Implementations§

Source§

impl Clone for ConnectError

Source§

fn clone(&self) -> ConnectError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<UnsupportedVersion> for ConnectError

Source§

fn from(_: UnsupportedVersion) -> ConnectError

Converts to this type from the input type.
Source§

impl PartialEq for ConnectError

Source§

fn eq(&self, other: &ConnectError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConnectError

Source§

impl StructuralPartialEq for ConnectError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.ConnectionError.html b/pr/395/docs/iroh_quinn/enum.ConnectionError.html new file mode 100644 index 000000000..10570f351 --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.ConnectionError.html @@ -0,0 +1,54 @@ +ConnectionError in iroh_quinn - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
+    VersionMismatch,
+    TransportError(Error),
+    ConnectionClosed(ConnectionClose),
+    ApplicationClosed(ApplicationClose),
+    Reset,
+    TimedOut,
+    LocallyClosed,
+    CidsExhausted,
+}
Expand description

Reasons why a connection might be lost

+

Variants§

§

VersionMismatch

The peer doesn’t implement any supported version

+
§

TransportError(Error)

The peer violated the QUIC specification as understood by this implementation

+
§

ConnectionClosed(ConnectionClose)

The peer’s QUIC stack aborted the connection automatically

+
§

ApplicationClosed(ApplicationClose)

The peer closed the connection

+
§

Reset

The peer is unable to continue processing this connection, usually due to having restarted

+
§

TimedOut

Communication with the peer has lapsed for longer than the negotiated idle timeout

+

If neither side is sending keep-alives, a connection will time out after a long enough idle +period even if the peer is still reachable. See also TransportConfig::max_idle_timeout() +and TransportConfig::keep_alive_interval().

+
§

LocallyClosed

The local application closed the connection

+
§

CidsExhausted

The connection could not be created because not enough of the CID space is available

+

Try using longer connection IDs.

+

Trait Implementations§

Source§

impl Clone for ConnectionError

Source§

fn clone(&self) -> ConnectionError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectionError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectionError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<Close> for ConnectionError

Source§

fn from(x: Close) -> ConnectionError

Converts to this type from the input type.
Source§

impl From<CloseReason> for ConnectionError

Source§

fn from(value: CloseReason) -> ConnectionError

Converts to this type from the input type.
Source§

impl From<ConnectionError> for ReadError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for ResetError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for SendDatagramError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for StoppedError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for WriteError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: Error) -> ConnectionError

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionError

Source§

fn eq(&self, other: &ConnectionError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConnectionError

Source§

impl StructuralPartialEq for ConnectionError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.Dir.html b/pr/395/docs/iroh_quinn/enum.Dir.html new file mode 100644 index 000000000..620593800 --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.Dir.html @@ -0,0 +1,54 @@ +Dir in iroh_quinn - Rust

Dir

Enum Dir 

Source
pub enum Dir {
+    Bi = 0,
+    Uni = 1,
+}
Expand description

Whether a stream communicates data in both directions or only from the initiator

+

Variants§

§

Bi = 0

Data flows in both directions

+
§

Uni = 1

Data flows only from the stream’s initiator

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for Dir

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Dir, Error>

Generate an arbitrary value of Self from the given unstructured data. Read more
Source§

fn arbitrary_take_rest(u: Unstructured<'arbitrary>) -> Result<Dir, Error>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
Source§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for Dir

Source§

fn clone(&self) -> Dir

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Dir

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for Dir

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Hash for Dir

Source§

fn hash<__H>(&self, state: &mut __H)
where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for Dir

Source§

fn cmp(&self, other: &Dir) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for Dir

Source§

fn eq(&self, other: &Dir) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for Dir

Source§

fn partial_cmp(&self, other: &Dir) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for Dir

Source§

impl Eq for Dir

Source§

impl StructuralPartialEq for Dir

Auto Trait Implementations§

§

impl Freeze for Dir

§

impl RefUnwindSafe for Dir

§

impl Send for Dir

§

impl Sync for Dir

§

impl Unpin for Dir

§

impl UnwindSafe for Dir

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.EcnCodepoint.html b/pr/395/docs/iroh_quinn/enum.EcnCodepoint.html new file mode 100644 index 000000000..0e41f2048 --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.EcnCodepoint.html @@ -0,0 +1,41 @@ +EcnCodepoint in iroh_quinn - Rust

EcnCodepoint

Enum EcnCodepoint 

Source
#[repr(u8)]
pub enum EcnCodepoint { + Ect0 = 2, + Ect1 = 1, + Ce = 3, +}
Expand description

Explicit congestion notification codepoint

+

Variants§

§

Ect0 = 2

The ECT(0) codepoint, indicating that an endpoint is ECN-capable

+
§

Ect1 = 1

The ECT(1) codepoint, indicating that an endpoint is ECN-capable

+
§

Ce = 3

The CE codepoint, signalling that congestion was experienced

+

Implementations§

Source§

impl EcnCodepoint

Source

pub fn from_bits(x: u8) -> Option<EcnCodepoint>

Create new object from the given bits

+
Source

pub fn is_ce(self) -> bool

Returns whether the codepoint is a CE, signalling that congestion was experienced

+

Trait Implementations§

Source§

impl Clone for EcnCodepoint

Source§

fn clone(&self) -> EcnCodepoint

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EcnCodepoint

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl PartialEq for EcnCodepoint

Source§

fn eq(&self, other: &EcnCodepoint) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for EcnCodepoint

Source§

impl Eq for EcnCodepoint

Source§

impl StructuralPartialEq for EcnCodepoint

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.FrameType.html b/pr/395/docs/iroh_quinn/enum.FrameType.html new file mode 100644 index 000000000..10e16ff16 --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.FrameType.html @@ -0,0 +1,79 @@ +FrameType in iroh_quinn - Rust

FrameType

Enum FrameType 

Source
pub enum FrameType {
+
Show 44 variants Padding, + Ping, + Ack, + AckEcn, + ResetStream, + StopSending, + Crypto, + NewToken, + Stream(StreamInfo), + MaxData, + MaxStreamData, + MaxStreamsBidi, + MaxStreamsUni, + DataBlocked, + StreamDataBlocked, + StreamsBlockedBidi, + StreamsBlockedUni, + NewConnectionId, + RetireConnectionId, + PathChallenge, + PathResponse, + ConnectionClose, + ApplicationClose, + HandshakeDone, + AckFrequency, + ImmediateAck, + Datagram(DatagramInfo), + ObservedIpv4Addr, + ObservedIpv6Addr, + PathAck, + PathAckEcn, + PathAbandon, + PathStatusBackup, + PathStatusAvailable, + PathNewConnectionId, + PathRetireConnectionId, + MaxPathId, + PathsBlocked, + PathCidsBlocked, + AddIpv4Address, + AddIpv6Address, + ReachOutAtIpv4, + ReachOutAtIpv6, + RemoveAddress, +
}

Variants§

§

Padding

§

Ping

§

Ack

§

AckEcn

§

ResetStream

§

StopSending

§

Crypto

§

NewToken

§

Stream(StreamInfo)

§

MaxData

§

MaxStreamData

§

MaxStreamsBidi

§

MaxStreamsUni

§

DataBlocked

§

StreamDataBlocked

§

StreamsBlockedBidi

§

StreamsBlockedUni

§

NewConnectionId

§

RetireConnectionId

§

PathChallenge

§

PathResponse

§

ConnectionClose

§

ApplicationClose

§

HandshakeDone

§

AckFrequency

§

ImmediateAck

§

Datagram(DatagramInfo)

§

ObservedIpv4Addr

§

ObservedIpv6Addr

§

PathAck

§

PathAckEcn

§

PathAbandon

§

PathStatusBackup

§

PathStatusAvailable

§

PathNewConnectionId

§

PathRetireConnectionId

§

MaxPathId

§

PathsBlocked

§

PathCidsBlocked

§

AddIpv4Address

§

AddIpv6Address

§

ReachOutAtIpv4

§

ReachOutAtIpv6

§

RemoveAddress

Trait Implementations§

Source§

impl Clone for FrameType

Source§

fn clone(&self) -> FrameType

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FrameType

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Decodable for FrameType

Source§

fn decode<B>(buf: &mut B) -> Result<FrameType, UnexpectedEnd>
where + B: Buf,

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for FrameType

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Encodable for FrameType

Source§

fn encode<B>(&self, buf: &mut B)
where + B: BufMut,

Append the encoding of self to the provided buffer.
Source§

impl PartialEq for FrameType

Source§

fn eq(&self, other: &FrameType) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl TryFrom<u64> for FrameType

Source§

type Error = InvalidFrameId

The type returned in the event of a conversion error.
Source§

fn try_from(value: u64) -> Result<FrameType, <FrameType as TryFrom<u64>>::Error>

Performs the conversion.
Source§

impl Copy for FrameType

Source§

impl Eq for FrameType

Source§

impl StructuralPartialEq for FrameType

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.ReadError.html b/pr/395/docs/iroh_quinn/enum.ReadError.html new file mode 100644 index 000000000..ce7870933 --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.ReadError.html @@ -0,0 +1,45 @@ +ReadError in iroh_quinn - Rust

ReadError

Enum ReadError 

Source
pub enum ReadError {
+    Reset(VarInt),
+    ConnectionLost(ConnectionError),
+    ClosedStream,
+    ZeroRttRejected,
+}
Expand description

Errors that arise from reading from a stream.

+

Variants§

§

Reset(VarInt)

The peer abandoned transmitting data on this stream

+

Carries an application-defined error code.

+
§

ConnectionLost(ConnectionError)

The connection was lost

+
§

ClosedStream

The stream has already been stopped, finished, or reset

+
§

ZeroRttRejected

This was a 0-RTT stream and the server rejected it

+

Can only occur on clients for 0-RTT streams, which can be opened using +Connecting::into_0rtt().

+

Trait Implementations§

Source§

impl Clone for ReadError

Source§

fn clone(&self) -> ReadError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ReadError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ReadError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ReadError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ConnectionError> for ReadError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ReadError> for Error

Source§

fn from(x: ReadError) -> Self

Converts to this type from the input type.
Source§

impl From<ReadError> for ReadExactError

Source§

fn from(source: ReadError) -> Self

Converts to this type from the input type.
Source§

impl From<ReadError> for ReadToEndError

Source§

fn from(source: ReadError) -> Self

Converts to this type from the input type.
Source§

impl From<ResetError> for ReadError

Source§

fn from(e: ResetError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ReadError

Source§

fn eq(&self, other: &ReadError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ReadError

Source§

impl StructuralPartialEq for ReadError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.ReadExactError.html b/pr/395/docs/iroh_quinn/enum.ReadExactError.html new file mode 100644 index 000000000..597fd7ec6 --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.ReadExactError.html @@ -0,0 +1,38 @@ +ReadExactError in iroh_quinn - Rust

ReadExactError

Enum ReadExactError 

Source
pub enum ReadExactError {
+    FinishedEarly(usize),
+    ReadError(ReadError),
+}
Expand description

Errors that arise from reading from a stream.

+

Variants§

§

FinishedEarly(usize)

The stream finished before all bytes were read

+
§

ReadError(ReadError)

A read error occurred

+

Trait Implementations§

Source§

impl Clone for ReadExactError

Source§

fn clone(&self) -> ReadExactError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ReadExactError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ReadExactError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ReadExactError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ReadError> for ReadExactError

Source§

fn from(source: ReadError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ReadExactError

Source§

fn eq(&self, other: &ReadExactError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ReadExactError

Source§

impl StructuralPartialEq for ReadExactError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.ReadToEndError.html b/pr/395/docs/iroh_quinn/enum.ReadToEndError.html new file mode 100644 index 000000000..766ff1519 --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.ReadToEndError.html @@ -0,0 +1,38 @@ +ReadToEndError in iroh_quinn - Rust

ReadToEndError

Enum ReadToEndError 

Source
pub enum ReadToEndError {
+    Read(ReadError),
+    TooLong,
+}
Expand description

Variants§

§

Read(ReadError)

An error occurred during reading

+
§

TooLong

The stream is larger than the user-supplied limit

+

Trait Implementations§

Source§

impl Clone for ReadToEndError

Source§

fn clone(&self) -> ReadToEndError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ReadToEndError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ReadToEndError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ReadToEndError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ReadError> for ReadToEndError

Source§

fn from(source: ReadError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ReadToEndError

Source§

fn eq(&self, other: &ReadToEndError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ReadToEndError

Source§

impl StructuralPartialEq for ReadToEndError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.ResetError.html b/pr/395/docs/iroh_quinn/enum.ResetError.html new file mode 100644 index 000000000..fc4b3323a --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.ResetError.html @@ -0,0 +1,40 @@ +ResetError in iroh_quinn - Rust

ResetError

Enum ResetError 

Source
pub enum ResetError {
+    ConnectionLost(ConnectionError),
+    ZeroRttRejected,
+}
Expand description

Errors that arise while waiting for a stream to be reset

+

Variants§

§

ConnectionLost(ConnectionError)

The connection was lost

+
§

ZeroRttRejected

This was a 0-RTT stream and the server rejected it

+

Can only occur on clients for 0-RTT streams, which can be opened using +Connecting::into_0rtt().

+

Trait Implementations§

Source§

impl Clone for ResetError

Source§

fn clone(&self) -> ResetError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ResetError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ResetError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ResetError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ConnectionError> for ResetError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ResetError> for Error

Source§

fn from(x: ResetError) -> Self

Converts to this type from the input type.
Source§

impl From<ResetError> for ReadError

Source§

fn from(e: ResetError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ResetError

Source§

fn eq(&self, other: &ResetError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ResetError

Source§

impl StructuralPartialEq for ResetError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.SendDatagramError.html b/pr/395/docs/iroh_quinn/enum.SendDatagramError.html new file mode 100644 index 000000000..ddfc4b282 --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.SendDatagramError.html @@ -0,0 +1,44 @@ +SendDatagramError in iroh_quinn - Rust

SendDatagramError

Enum SendDatagramError 

Source
pub enum SendDatagramError {
+    UnsupportedByPeer,
+    Disabled,
+    TooLarge,
+    ConnectionLost(ConnectionError),
+}
Expand description

Errors that can arise when sending a datagram

+

Variants§

§

UnsupportedByPeer

The peer does not support receiving datagram frames

+
§

Disabled

Datagram support is disabled locally

+
§

TooLarge

The datagram is larger than the connection can currently accommodate

+

Indicates that the path MTU minus overhead or the limit advertised by the peer has been +exceeded.

+
§

ConnectionLost(ConnectionError)

The connection was lost

+

Trait Implementations§

Source§

impl Clone for SendDatagramError

Source§

fn clone(&self) -> SendDatagramError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SendDatagramError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for SendDatagramError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for SendDatagramError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ConnectionError> for SendDatagramError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for SendDatagramError

Source§

fn eq(&self, other: &SendDatagramError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for SendDatagramError

Source§

impl StructuralPartialEq for SendDatagramError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.Side.html b/pr/395/docs/iroh_quinn/enum.Side.html new file mode 100644 index 000000000..323768e71 --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.Side.html @@ -0,0 +1,55 @@ +Side in iroh_quinn - Rust

Side

Enum Side 

Source
pub enum Side {
+    Client = 0,
+    Server = 1,
+}
Expand description

Whether an endpoint was the initiator of a connection

+

Variants§

§

Client = 0

The initiator of a connection

+
§

Server = 1

The acceptor of a connection

+

Implementations§

Source§

impl Side

Source

pub fn is_client(self) -> bool

Shorthand for self == Side::Client

+
Source

pub fn is_server(self) -> bool

Shorthand for self == Side::Server

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for Side

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Side, Error>

Generate an arbitrary value of Self from the given unstructured data. Read more
Source§

fn arbitrary_take_rest(u: Unstructured<'arbitrary>) -> Result<Side, Error>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
Source§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for Side

Source§

fn clone(&self) -> Side

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Side

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Hash for Side

Source§

fn hash<__H>(&self, state: &mut __H)
where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Not for Side

Source§

type Output = Side

The resulting type after applying the ! operator.
Source§

fn not(self) -> Side

Performs the unary ! operation. Read more
Source§

impl Ord for Side

Source§

fn cmp(&self, other: &Side) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for Side

Source§

fn eq(&self, other: &Side) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for Side

Source§

fn partial_cmp(&self, other: &Side) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for Side

Source§

impl Eq for Side

Source§

impl StructuralPartialEq for Side

Auto Trait Implementations§

§

impl Freeze for Side

§

impl RefUnwindSafe for Side

§

impl Send for Side

§

impl Sync for Side

§

impl Unpin for Side

§

impl UnwindSafe for Side

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.StoppedError.html b/pr/395/docs/iroh_quinn/enum.StoppedError.html new file mode 100644 index 000000000..b6dd1d97d --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.StoppedError.html @@ -0,0 +1,40 @@ +StoppedError in iroh_quinn - Rust

StoppedError

Enum StoppedError 

Source
pub enum StoppedError {
+    ConnectionLost(ConnectionError),
+    ZeroRttRejected,
+}
Expand description

Errors that arise while monitoring for a send stream stop from the peer

+

Variants§

§

ConnectionLost(ConnectionError)

The connection was lost

+
§

ZeroRttRejected

This was a 0-RTT stream and the server rejected it

+

Can only occur on clients for 0-RTT streams, which can be opened using +Connecting::into_0rtt().

+

Trait Implementations§

Source§

impl Clone for StoppedError

Source§

fn clone(&self) -> StoppedError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for StoppedError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for StoppedError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for StoppedError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ConnectionError> for StoppedError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<StoppedError> for Error

Source§

fn from(x: StoppedError) -> Self

Converts to this type from the input type.
Source§

impl From<StoppedError> for WriteError

Source§

fn from(x: StoppedError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for StoppedError

Source§

fn eq(&self, other: &StoppedError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for StoppedError

Source§

impl StructuralPartialEq for StoppedError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/enum.WriteError.html b/pr/395/docs/iroh_quinn/enum.WriteError.html new file mode 100644 index 000000000..2366c22c2 --- /dev/null +++ b/pr/395/docs/iroh_quinn/enum.WriteError.html @@ -0,0 +1,45 @@ +WriteError in iroh_quinn - Rust

WriteError

Enum WriteError 

Source
pub enum WriteError {
+    Stopped(VarInt),
+    ConnectionLost(ConnectionError),
+    ClosedStream,
+    ZeroRttRejected,
+}
Expand description

Errors that arise from writing to a stream

+

Variants§

§

Stopped(VarInt)

The peer is no longer accepting data on this stream

+

Carries an application-defined error code.

+
§

ConnectionLost(ConnectionError)

The connection was lost

+
§

ClosedStream

The stream has already been finished or reset

+
§

ZeroRttRejected

This was a 0-RTT stream and the server rejected it

+

Can only occur on clients for 0-RTT streams, which can be opened using +Connecting::into_0rtt().

+

Trait Implementations§

Source§

impl Clone for WriteError

Source§

fn clone(&self) -> WriteError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for WriteError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for WriteError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for WriteError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ClosedStream> for WriteError

Source§

fn from(_: ClosedStream) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for WriteError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<StoppedError> for WriteError

Source§

fn from(x: StoppedError) -> Self

Converts to this type from the input type.
Source§

impl From<WriteError> for Error

Source§

fn from(x: WriteError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for WriteError

Source§

fn eq(&self, other: &WriteError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for WriteError

Source§

impl StructuralPartialEq for WriteError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/fn.default_runtime.html b/pr/395/docs/iroh_quinn/fn.default_runtime.html new file mode 100644 index 000000000..ec132fc52 --- /dev/null +++ b/pr/395/docs/iroh_quinn/fn.default_runtime.html @@ -0,0 +1,5 @@ +default_runtime in iroh_quinn - Rust

default_runtime

Function default_runtime 

Source
pub fn default_runtime() -> Option<Arc<dyn Runtime>>
Expand description

Automatically select an appropriate runtime from those enabled at compile time

+

If runtime-tokio is enabled and this function is called from within a Tokio runtime context, +then TokioRuntime is returned. Otherwise, if runtime-smol is enabled, SmolRuntime is +returned. Otherwise, None is returned.

+
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/incoming/struct.Incoming.html b/pr/395/docs/iroh_quinn/incoming/struct.Incoming.html new file mode 100644 index 000000000..abd44a6e1 --- /dev/null +++ b/pr/395/docs/iroh_quinn/incoming/struct.Incoming.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.Incoming.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/incoming/struct.IncomingFuture.html b/pr/395/docs/iroh_quinn/incoming/struct.IncomingFuture.html new file mode 100644 index 000000000..4471430fd --- /dev/null +++ b/pr/395/docs/iroh_quinn/incoming/struct.IncomingFuture.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.IncomingFuture.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/incoming/struct.RetryError.html b/pr/395/docs/iroh_quinn/incoming/struct.RetryError.html new file mode 100644 index 000000000..5376fb9c1 --- /dev/null +++ b/pr/395/docs/iroh_quinn/incoming/struct.RetryError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.RetryError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/index.html b/pr/395/docs/iroh_quinn/index.html new file mode 100644 index 000000000..43f26c78c --- /dev/null +++ b/pr/395/docs/iroh_quinn/index.html @@ -0,0 +1,34 @@ +iroh_quinn - Rust

Crate iroh_quinn

Crate iroh_quinn 

Source
Expand description

QUIC transport protocol implementation

+

QUIC is a modern transport protocol addressing +shortcomings of TCP, such as head-of-line blocking, poor security, slow handshakes, and +inefficient congestion control. This crate provides a portable userspace implementation. It +builds on top of quinn-proto, which implements protocol logic independent of any particular +runtime.

+

The entry point of this crate is the Endpoint.

+

§About QUIC

+

A QUIC connection is an association between two endpoints. The endpoint which initiates the +connection is termed the client, and the endpoint which accepts it is termed the server. A +single endpoint may function as both client and server for different connections, for example +in a peer-to-peer application. To communicate application data, each endpoint may open streams +up to a limit dictated by its peer. Typically, that limit is increased as old streams are +finished.

+

Streams may be unidirectional or bidirectional, and are cheap to create and disposable. For +example, a traditionally datagram-oriented application could use a new stream for every +message it wants to send, no longer needing to worry about MTUs. Bidirectional streams behave +much like a traditional TCP connection, and are useful for sending messages that have an +immediate response, such as an HTTP request. Stream data is delivered reliably, and there is no +ordering enforced between data on different streams.

+

By avoiding head-of-line blocking and providing unified congestion control across all streams +of a connection, QUIC is able to provide higher throughput and lower latency than one or +multiple TCP connections between the same two hosts, while providing more useful behavior than +raw UDP sockets.

+

Quinn also exposes unreliable datagrams, which are a low-level primitive preferred when +automatic fragmentation and retransmission of certain data is not desired.

+

QUIC uses encryption and identity verification built directly on TLS 1.3. Just as with a TLS +server, it is useful for a QUIC server to be identified by a certificate signed by a trusted +authority. If this is infeasible–for example, if servers are short-lived or not associated +with a domain name–then as with TLS, self-signed certificates can be used to provide +encryption alone.

+

Re-exports§

pub use rustls;
pub use udp;

Modules§

congestion
Logic for controlling the rate at which data is sent
crypto
Traits and implementations for the QUIC cryptography protocol

Structs§

Accept
Future produced by Endpoint::accept
AcceptBi
Future produced by Connection::accept_bi
AcceptUni
Future produced by Connection::accept_uni
AckFrequencyConfig
Parameters for controlling the peer’s acknowledgement frequency
AddressDiscovery
Stream produced by Path::observed_external_addr
ApplicationClose
Reason given by an application for closing the connection
BloomTokenLog
Bloom filter-based TokenLog
Chunk
A chunk of data from the receive stream
ClientConfig
Configuration for outgoing connections
ClosePath
Future produced by Path::close
ClosedStream
Error indicating that a stream has not been opened or has already been finished or reset
Connecting
In-progress connection attempt future
Connection
A QUIC connection.
ConnectionClose
Reason given by the transport for closing the connection
ConnectionId
Protocol-level identifier for a connection.
ConnectionStats
Connection statistics
Endpoint
A QUIC endpoint.
EndpointConfig
Global configuration for the endpoint, affecting all connections
EndpointStats
Statistics on Endpoint activity
FrameStats
Number of frames transmitted or received of each frame type
IdleTimeout
Maximum duration of inactivity to accept before timing out the connection
Incoming
An incoming connection for which the server has not yet begun its part of the handshake
IncomingFuture
Basic adapter to let Incoming be await-ed like a Connecting
InvalidCid
The connection ID was not recognized by the ConnectionIdGenerator
MtuDiscoveryConfig
Parameters governing MTU discovery.
NoneTokenLog
Null implementation of TokenLog, which never accepts tokens
NoneTokenStore
Null implementation of TokenStore, which does not store any tokens
OnClosed
Future returned by Connection::on_closed
OpenBi
Future produced by Connection::open_bi
OpenPath
Future produced by crate::Connection::open_path
OpenUni
Future produced by Connection::open_uni
Path
An open (Multi)Path
PathId
Id representing different paths when using multipath extension
PathStats
Statistics related to a transmission path
QlogConfig
Configuration for qlog trace logging.
QlogFileFactory
Enables writing qlog traces to a directory.
ReadDatagram
Future produced by Connection::read_datagram
RecvStream
A stream that can only be used to receive data
RetryError
Error for attempting to retry an Incoming which already bears a token from a previous retry
SendDatagram
Future produced by Connection::send_datagram_wait
SendStream
A stream that can only be used to send data
ServerConfig
Parameters governing incoming connections
SmolRuntime
A Quinn runtime for smol
StdSystemTime
Default implementation of TimeSource
StreamId
Identifier for a stream within a particular connection
TokenMemoryCache
TokenStore implementation that stores up to N tokens per server name for up to a +limited number of server names, in-memory
TokenReuseError
Error for when a validation token may have been reused
TokioRuntime
A Quinn runtime for Tokio
Transmit
An outgoing packet
TransportConfig
Parameters governing the core QUIC state machine
TransportErrorCode
Transport-level error code
UdpStats
Statistics about UDP datagrams transmitted or received on a connection
UnorderedRecvStream
A stream that can be used to receive data out-of-order.
ValidationTokenConfig
Configuration for sending and handling validation tokens in incoming connections
VarInt
An integer less than 2^62
VarIntBoundsExceeded
Error returned when constructing a VarInt from a value >= 2^62
WeakConnectionHandle
A handle to some connection internals, use with care.
Written
Indicates how many bytes and chunks had been transferred in a write operation
ZeroRttAccepted
Future that completes when a connection is fully established

Enums§

ConfigError
Errors in the configuration of an endpoint
ConnectError
Errors in the parameters being used to create a new connection
ConnectionError
Reasons why a connection might be lost
Dir
Whether a stream communicates data in both directions or only from the initiator
EcnCodepoint
Explicit congestion notification codepoint
FrameType
ReadError
Errors that arise from reading from a stream.
ReadExactError
Errors that arise from reading from a stream.
ReadToEndError
Errors from RecvStream::read_to_end
ResetError
Errors that arise while waiting for a stream to be reset
SendDatagramError
Errors that can arise when sending a datagram
Side
Whether an endpoint was the initiator of a connection
StoppedError
Errors that arise while monitoring for a send stream stop from the peer
WriteError
Errors that arise from writing to a stream

Traits§

AsyncTimer
Abstract implementation of an async timer for runtime independence
AsyncUdpSocket
Abstract implementation of a UDP socket for runtime independence
ConnectionIdGenerator
Generates connection IDs for incoming connections
QlogFactory
Constructs a QlogConfig for individual connections.
Runtime
Abstracts I/O and timer operations for runtime independence
TimeSource
Object to get current SystemTime
TokenLog
Responsible for limiting clients’ ability to reuse validation tokens
TokenStore
Responsible for storing validation tokens received from servers and retrieving them for use in +subsequent connections
UdpSender
An object for asynchronously writing to an associated AsyncUdpSocket.

Functions§

default_runtime
Automatically select an appropriate runtime from those enabled at compile time
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/path/struct.AddressDiscovery.html b/pr/395/docs/iroh_quinn/path/struct.AddressDiscovery.html new file mode 100644 index 000000000..e4476ec35 --- /dev/null +++ b/pr/395/docs/iroh_quinn/path/struct.AddressDiscovery.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.AddressDiscovery.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/path/struct.ClosePath.html b/pr/395/docs/iroh_quinn/path/struct.ClosePath.html new file mode 100644 index 000000000..56c3283bf --- /dev/null +++ b/pr/395/docs/iroh_quinn/path/struct.ClosePath.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.ClosePath.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/path/struct.OpenPath.html b/pr/395/docs/iroh_quinn/path/struct.OpenPath.html new file mode 100644 index 000000000..98307dd6b --- /dev/null +++ b/pr/395/docs/iroh_quinn/path/struct.OpenPath.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.OpenPath.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/path/struct.Path.html b/pr/395/docs/iroh_quinn/path/struct.Path.html new file mode 100644 index 000000000..ca4fa66c4 --- /dev/null +++ b/pr/395/docs/iroh_quinn/path/struct.Path.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.Path.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/recv_stream/enum.ReadError.html b/pr/395/docs/iroh_quinn/recv_stream/enum.ReadError.html new file mode 100644 index 000000000..a7c5a50f0 --- /dev/null +++ b/pr/395/docs/iroh_quinn/recv_stream/enum.ReadError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/enum.ReadError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/recv_stream/enum.ReadExactError.html b/pr/395/docs/iroh_quinn/recv_stream/enum.ReadExactError.html new file mode 100644 index 000000000..5ff8c04ac --- /dev/null +++ b/pr/395/docs/iroh_quinn/recv_stream/enum.ReadExactError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/enum.ReadExactError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/recv_stream/enum.ReadToEndError.html b/pr/395/docs/iroh_quinn/recv_stream/enum.ReadToEndError.html new file mode 100644 index 000000000..0297f93ff --- /dev/null +++ b/pr/395/docs/iroh_quinn/recv_stream/enum.ReadToEndError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/enum.ReadToEndError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/recv_stream/enum.ResetError.html b/pr/395/docs/iroh_quinn/recv_stream/enum.ResetError.html new file mode 100644 index 000000000..9f60c8827 --- /dev/null +++ b/pr/395/docs/iroh_quinn/recv_stream/enum.ResetError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/enum.ResetError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/recv_stream/struct.RecvStream.html b/pr/395/docs/iroh_quinn/recv_stream/struct.RecvStream.html new file mode 100644 index 000000000..2842cf45e --- /dev/null +++ b/pr/395/docs/iroh_quinn/recv_stream/struct.RecvStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.RecvStream.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/recv_stream/struct.UnorderedRecvStream.html b/pr/395/docs/iroh_quinn/recv_stream/struct.UnorderedRecvStream.html new file mode 100644 index 000000000..d3cfadcfc --- /dev/null +++ b/pr/395/docs/iroh_quinn/recv_stream/struct.UnorderedRecvStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.UnorderedRecvStream.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/runtime/fn.default_runtime.html b/pr/395/docs/iroh_quinn/runtime/fn.default_runtime.html new file mode 100644 index 000000000..0b519171e --- /dev/null +++ b/pr/395/docs/iroh_quinn/runtime/fn.default_runtime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/fn.default_runtime.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/runtime/smol/struct.SmolRuntime.html b/pr/395/docs/iroh_quinn/runtime/smol/struct.SmolRuntime.html new file mode 100644 index 000000000..1bf6a4945 --- /dev/null +++ b/pr/395/docs/iroh_quinn/runtime/smol/struct.SmolRuntime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn/struct.SmolRuntime.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/runtime/tokio/struct.TokioRuntime.html b/pr/395/docs/iroh_quinn/runtime/tokio/struct.TokioRuntime.html new file mode 100644 index 000000000..d4a7961a5 --- /dev/null +++ b/pr/395/docs/iroh_quinn/runtime/tokio/struct.TokioRuntime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn/struct.TokioRuntime.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/runtime/trait.AsyncTimer.html b/pr/395/docs/iroh_quinn/runtime/trait.AsyncTimer.html new file mode 100644 index 000000000..2a738be07 --- /dev/null +++ b/pr/395/docs/iroh_quinn/runtime/trait.AsyncTimer.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/trait.AsyncTimer.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/runtime/trait.AsyncUdpSocket.html b/pr/395/docs/iroh_quinn/runtime/trait.AsyncUdpSocket.html new file mode 100644 index 000000000..47814c9b7 --- /dev/null +++ b/pr/395/docs/iroh_quinn/runtime/trait.AsyncUdpSocket.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/trait.AsyncUdpSocket.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/runtime/trait.Runtime.html b/pr/395/docs/iroh_quinn/runtime/trait.Runtime.html new file mode 100644 index 000000000..bdd2f356e --- /dev/null +++ b/pr/395/docs/iroh_quinn/runtime/trait.Runtime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/trait.Runtime.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/runtime/trait.UdpSender.html b/pr/395/docs/iroh_quinn/runtime/trait.UdpSender.html new file mode 100644 index 000000000..b63b9c497 --- /dev/null +++ b/pr/395/docs/iroh_quinn/runtime/trait.UdpSender.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/trait.UdpSender.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/send_stream/enum.StoppedError.html b/pr/395/docs/iroh_quinn/send_stream/enum.StoppedError.html new file mode 100644 index 000000000..931afce4d --- /dev/null +++ b/pr/395/docs/iroh_quinn/send_stream/enum.StoppedError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/enum.StoppedError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/send_stream/enum.WriteError.html b/pr/395/docs/iroh_quinn/send_stream/enum.WriteError.html new file mode 100644 index 000000000..b701786e6 --- /dev/null +++ b/pr/395/docs/iroh_quinn/send_stream/enum.WriteError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/enum.WriteError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/send_stream/struct.SendStream.html b/pr/395/docs/iroh_quinn/send_stream/struct.SendStream.html new file mode 100644 index 000000000..fec51cd9d --- /dev/null +++ b/pr/395/docs/iroh_quinn/send_stream/struct.SendStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.SendStream.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/sidebar-items.js b/pr/395/docs/iroh_quinn/sidebar-items.js new file mode 100644 index 000000000..2af1c653a --- /dev/null +++ b/pr/395/docs/iroh_quinn/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["ConfigError","ConnectError","ConnectionError","Dir","EcnCodepoint","FrameType","ReadError","ReadExactError","ReadToEndError","ResetError","SendDatagramError","Side","StoppedError","WriteError"],"fn":["default_runtime"],"mod":["congestion","crypto"],"struct":["Accept","AcceptBi","AcceptUni","AckFrequencyConfig","AddressDiscovery","ApplicationClose","BloomTokenLog","Chunk","ClientConfig","ClosePath","ClosedStream","Connecting","Connection","ConnectionClose","ConnectionId","ConnectionStats","Endpoint","EndpointConfig","EndpointStats","FrameStats","IdleTimeout","Incoming","IncomingFuture","InvalidCid","MtuDiscoveryConfig","NoneTokenLog","NoneTokenStore","OnClosed","OpenBi","OpenPath","OpenUni","Path","PathId","PathStats","QlogConfig","QlogFileFactory","ReadDatagram","RecvStream","RetryError","SendDatagram","SendStream","ServerConfig","SmolRuntime","StdSystemTime","StreamId","TokenMemoryCache","TokenReuseError","TokioRuntime","Transmit","TransportConfig","TransportErrorCode","UdpStats","UnorderedRecvStream","ValidationTokenConfig","VarInt","VarIntBoundsExceeded","WeakConnectionHandle","Written","ZeroRttAccepted"],"trait":["AsyncTimer","AsyncUdpSocket","ConnectionIdGenerator","QlogFactory","Runtime","TimeSource","TokenLog","TokenStore","UdpSender"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.Accept.html b/pr/395/docs/iroh_quinn/struct.Accept.html new file mode 100644 index 000000000..0cef1610b --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.Accept.html @@ -0,0 +1,50 @@ +Accept in iroh_quinn - Rust

Accept

Struct Accept 

Source
pub struct Accept<'a> { /* private fields */ }
Expand description

Future produced by Endpoint::accept

+

Trait Implementations§

Source§

impl Future for Accept<'_>

Source§

type Output = Option<Incoming>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for Accept<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for Accept<'a>

§

impl<'a> !RefUnwindSafe for Accept<'a>

§

impl<'a> Send for Accept<'a>

§

impl<'a> Sync for Accept<'a>

§

impl<'a> !UnwindSafe for Accept<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.AcceptBi.html b/pr/395/docs/iroh_quinn/struct.AcceptBi.html new file mode 100644 index 000000000..85b92603a --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.AcceptBi.html @@ -0,0 +1,54 @@ +AcceptBi in iroh_quinn - Rust

AcceptBi

Struct AcceptBi 

Source
pub struct AcceptBi<'a> { /* private fields */ }
Expand description

Future produced by Connection::accept_bi

+

Trait Implementations§

Source§

impl Future for AcceptBi<'_>

Source§

type Output = Result<(SendStream, RecvStream), ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for AcceptBi<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for AcceptBi<'a>

§

impl<'a> !RefUnwindSafe for AcceptBi<'a>

§

impl<'a> Send for AcceptBi<'a>

§

impl<'a> Sync for AcceptBi<'a>

§

impl<'a> !UnwindSafe for AcceptBi<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.AcceptUni.html b/pr/395/docs/iroh_quinn/struct.AcceptUni.html new file mode 100644 index 000000000..e27354dee --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.AcceptUni.html @@ -0,0 +1,54 @@ +AcceptUni in iroh_quinn - Rust

AcceptUni

Struct AcceptUni 

Source
pub struct AcceptUni<'a> { /* private fields */ }
Expand description

Future produced by Connection::accept_uni

+

Trait Implementations§

Source§

impl Future for AcceptUni<'_>

Source§

type Output = Result<RecvStream, ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for AcceptUni<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for AcceptUni<'a>

§

impl<'a> !RefUnwindSafe for AcceptUni<'a>

§

impl<'a> Send for AcceptUni<'a>

§

impl<'a> Sync for AcceptUni<'a>

§

impl<'a> !UnwindSafe for AcceptUni<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.AckFrequencyConfig.html b/pr/395/docs/iroh_quinn/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..8af0205c6 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.AckFrequencyConfig.html @@ -0,0 +1,65 @@ +AckFrequencyConfig in iroh_quinn - Rust

AckFrequencyConfig

Struct AckFrequencyConfig 

Source
pub struct AckFrequencyConfig { /* private fields */ }
Expand description

Parameters for controlling the peer’s acknowledgement frequency

+

The parameters provided in this config will be sent to the peer at the beginning of the +connection, so it can take them into account when sending acknowledgements (see each parameter’s +description for details on how it influences acknowledgement frequency).

+

Quinn’s implementation follows the fourth draft of the +QUIC Acknowledgement Frequency extension. +The defaults produce behavior slightly different than the behavior without this extension, +because they change the way reordered packets are handled (see +AckFrequencyConfig::reordering_threshold for details).

+

Implementations§

Source§

impl AckFrequencyConfig

Source

pub fn ack_eliciting_threshold( + &mut self, + value: VarInt, +) -> &mut AckFrequencyConfig

The ack-eliciting threshold we will request the peer to use

+

This threshold represents the number of ack-eliciting packets an endpoint may receive +without immediately sending an ACK.

+

The remote peer should send at least one ACK frame when more than this number of +ack-eliciting packets have been received. A value of 0 results in a receiver immediately +acknowledging every ack-eliciting packet.

+

Defaults to 1, which sends ACK frames for every other ack-eliciting packet.

+
Source

pub fn max_ack_delay( + &mut self, + value: Option<Duration>, +) -> &mut AckFrequencyConfig

The max_ack_delay we will request the peer to use

+

This parameter represents the maximum amount of time that an endpoint waits before sending +an ACK when the ack-eliciting threshold hasn’t been reached.

+

The effective max_ack_delay will be clamped to be at least the peer’s min_ack_delay +transport parameter, and at most the greater of the current path RTT or 25ms.

+

Defaults to None, in which case the peer’s original max_ack_delay will be used, as +obtained from its transport parameters.

+
Source

pub fn reordering_threshold(&mut self, value: VarInt) -> &mut AckFrequencyConfig

The reordering threshold we will request the peer to use

+

This threshold represents the amount of out-of-order packets that will trigger an endpoint +to send an ACK, without waiting for ack_eliciting_threshold to be exceeded or for +max_ack_delay to be elapsed.

+

A value of 0 indicates out-of-order packets do not elicit an immediate ACK. A value of 1 +immediately acknowledges any packets that are received out of order (this is also the +behavior when the extension is disabled).

+

It is recommended to set this value to TransportConfig::packet_threshold minus one. +Since the default value for TransportConfig::packet_threshold is 3, this value defaults +to 2.

+

Trait Implementations§

Source§

impl Clone for AckFrequencyConfig

Source§

fn clone(&self) -> AckFrequencyConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for AckFrequencyConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for AckFrequencyConfig

Source§

fn default() -> AckFrequencyConfig

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.AddressDiscovery.html b/pr/395/docs/iroh_quinn/struct.AddressDiscovery.html new file mode 100644 index 000000000..3c59b81e7 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.AddressDiscovery.html @@ -0,0 +1,173 @@ +AddressDiscovery in iroh_quinn - Rust

AddressDiscovery

Struct AddressDiscovery 

Source
pub struct AddressDiscovery { /* private fields */ }
Expand description

Stream produced by Path::observed_external_addr

+

This will always return the external address most recently reported by the remote over this +path. If the extension is not negotiated, this stream will never return.

+

Trait Implementations§

Source§

impl Stream for AddressDiscovery

Source§

type Item = SocketAddr

Values yielded by the stream.
Source§

fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, +) -> Poll<Option<Self::Item>>

Attempt to pull out the next value of this stream, registering the +current task for wakeup if the value is not yet available, and returning +None if the stream is exhausted. Read more
§

fn size_hint(&self) -> (usize, Option<usize>)

Returns the bounds on the remaining length of the stream. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<S> StreamExt for S
where + S: Stream + ?Sized,

§

fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>
where + Self: Unpin,

A convenience for calling [Stream::poll_next()] on !Unpin types.
§

fn next(&mut self) -> NextFuture<'_, Self>
where + Self: Unpin,

Retrieves the next item in the stream. Read more
§

fn try_next<T, E>(&mut self) -> TryNextFuture<'_, Self>
where + Self: Stream<Item = Result<T, E>> + Unpin,

Retrieves the next item in the stream. Read more
§

fn count(self) -> CountFuture<Self>
where + Self: Sized,

Counts the number of items in the stream. Read more
§

fn map<T, F>(self, f: F) -> Map<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> T,

Maps items of the stream to new values using a closure. Read more
§

fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
where + Self: Sized, + U: Stream, + F: FnMut(Self::Item) -> U,

Maps items to streams and then concatenates them. Read more
§

fn flatten(self) -> Flatten<Self>
where + Self: Sized, + Self::Item: Stream,

Concatenates inner streams. Read more
§

fn then<F, Fut>(self, f: F) -> Then<Self, F, Fut>
where + Self: Sized, + F: FnMut(Self::Item) -> Fut, + Fut: Future,

Maps items of the stream to new values using an async closure. Read more
§

fn filter<P>(self, predicate: P) -> Filter<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Keeps items of the stream for which predicate returns true. Read more
§

fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> Option<T>,

Filters and maps items of the stream using a closure. Read more
§

fn take(self, n: usize) -> Take<Self>
where + Self: Sized,

Takes only the first n items of the stream. Read more
§

fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Takes items while predicate returns true. Read more
§

fn map_while<B, P>(self, predicate: P) -> MapWhile<Self, P>
where + Self: Sized, + P: FnMut(Self::Item) -> Option<B>,

Maps items while predicate returns Some. Read more
§

fn skip(self, n: usize) -> Skip<Self>
where + Self: Sized,

Skips the first n items of the stream. Read more
§

fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Skips items while predicate returns true. Read more
§

fn step_by(self, step: usize) -> StepBy<Self>
where + Self: Sized,

Yields every stepth item. Read more
§

fn chain<U>(self, other: U) -> Chain<Self, U>
where + Self: Sized, + U: Stream<Item = Self::Item>,

Appends another stream to the end of this one. Read more
§

fn cloned<'a, T>(self) -> Cloned<Self>
where + Self: Sized + Stream<Item = &'a T>, + T: Clone + 'a,

Clones all items. Read more
§

fn copied<'a, T>(self) -> Copied<Self>
where + Self: Sized + Stream<Item = &'a T>, + T: Copy + 'a,

Copies all items. Read more
§

fn collect<C>(self) -> CollectFuture<Self, C>
where + Self: Sized, + C: Default + Extend<Self::Item>,

Collects all items in the stream into a collection. Read more
§

fn try_collect<T, E, C>(self) -> TryCollectFuture<Self, C>
where + Self: Sized + Stream<Item = Result<T, E>>, + C: Default + Extend<T>,

Collects all items in the fallible stream into a collection. Read more
§

fn partition<B, P>(self, predicate: P) -> PartitionFuture<Self, P, B>
where + Self: Sized, + B: Default + Extend<Self::Item>, + P: FnMut(&Self::Item) -> bool,

Partitions items into those for which predicate is true and those for which it is +false, and then collects them into two collections. Read more
§

fn fold<T, F>(self, init: T, f: F) -> FoldFuture<Self, F, T>
where + Self: Sized, + F: FnMut(T, Self::Item) -> T,

Accumulates a computation over the stream. Read more
§

fn try_fold<T, E, F, B>( + &mut self, + init: B, + f: F, +) -> TryFoldFuture<'_, Self, F, B>
where + Self: Sized + Stream<Item = Result<T, E>> + Unpin, + F: FnMut(B, T) -> Result<B, E>,

Accumulates a fallible computation over the stream. Read more
§

fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
where + Self: Sized, + F: FnMut(&mut St, Self::Item) -> Option<B>,

Maps items of the stream to new values using a state value and a closure. Read more
§

fn fuse(self) -> Fuse<Self>
where + Self: Sized,

Fuses the stream so that it stops yielding items after the first None. Read more
§

fn cycle(self) -> Cycle<Self>
where + Self: Sized + Clone,

Repeats the stream from beginning to end, forever. Read more
§

fn enumerate(self) -> Enumerate<Self>
where + Self: Sized,

Enumerates items, mapping them to (index, item). Read more
§

fn inspect<F>(self, f: F) -> Inspect<Self, F>
where + Self: Sized, + F: FnMut(&Self::Item),

Calls a closure on each item and passes it on. Read more
§

fn nth(&mut self, n: usize) -> NthFuture<'_, Self>
where + Self: Unpin,

Gets the nth item of the stream. Read more
§

fn last(self) -> LastFuture<Self>
where + Self: Sized,

Returns the last item in the stream. Read more
§

fn find<P>(&mut self, predicate: P) -> FindFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(&Self::Item) -> bool,

Finds the first item of the stream for which predicate returns true. Read more
§

fn find_map<F, B>(&mut self, f: F) -> FindMapFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> Option<B>,

Applies a closure to items in the stream and returns the first Some result. Read more
§

fn position<P>(&mut self, predicate: P) -> PositionFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Finds the index of the first item of the stream for which predicate returns true. Read more
§

fn all<P>(&mut self, predicate: P) -> AllFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Tests if predicate returns true for all items in the stream. Read more
§

fn any<P>(&mut self, predicate: P) -> AnyFuture<'_, Self, P>
where + Self: Unpin, + P: FnMut(Self::Item) -> bool,

Tests if predicate returns true for any item in the stream. Read more
§

fn for_each<F>(self, f: F) -> ForEachFuture<Self, F>
where + Self: Sized, + F: FnMut(Self::Item),

Calls a closure on each item of the stream. Read more
§

fn try_for_each<F, E>(&mut self, f: F) -> TryForEachFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> Result<(), E>,

Calls a fallible closure on each item of the stream, stopping on first error. Read more
§

fn zip<U>(self, other: U) -> Zip<Self, U>
where + Self: Sized, + U: Stream,

Zips up two streams into a single stream of pairs. Read more
§

fn unzip<A, B, FromA, FromB>(self) -> UnzipFuture<Self, FromA, FromB>
where + FromA: Default + Extend<A>, + FromB: Default + Extend<B>, + Self: Sized + Stream<Item = (A, B)>,

Collects a stream of pairs into a pair of collections. Read more
§

fn or<S>(self, other: S) -> Or<Self, S>
where + Self: Sized, + S: Stream<Item = Self::Item>,

Merges with other stream, preferring items from self whenever both streams are ready. Read more
§

fn race<S>(self, other: S) -> Race<Self, S>
where + Self: Sized, + S: Stream<Item = Self::Item>,

Merges with other stream, with no preference for either stream when both are ready. Read more
§

fn drain(&mut self) -> Drain<'_, Self>

Yields all immediately available values from a stream. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Stream<Item = Self::Item> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the stream and changes its type to dyn Stream + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Stream<Item = Self::Item> + 'a>>
where + Self: Sized + 'a,

Boxes the stream and changes its type to dyn Stream + 'a. Read more
§

impl<St> StreamExt for St
where + St: Stream + ?Sized,

§

fn next(&mut self) -> Next<'_, Self>
where + Self: Unpin,

Consumes and returns the next value in the stream or None if the +stream is finished. Read more
§

fn try_next<T, E>(&mut self) -> TryNext<'_, Self>
where + Self: Stream<Item = Result<T, E>> + Unpin,

Consumes and returns the next item in the stream. If an error is +encountered before the next item, the error is returned instead. Read more
§

fn map<T, F>(self, f: F) -> Map<Self, F>
where + F: FnMut(Self::Item) -> T, + Self: Sized,

Maps this stream’s items to a different type, returning a new stream of +the resulting type. Read more
§

fn map_while<T, F>(self, f: F) -> MapWhile<Self, F>
where + F: FnMut(Self::Item) -> Option<T>, + Self: Sized,

Map this stream’s items to a different type for as long as determined by +the provided closure. A stream of the target type will be returned, +which will yield elements until the closure returns None. Read more
§

fn then<F, Fut>(self, f: F) -> Then<Self, Fut, F>
where + F: FnMut(Self::Item) -> Fut, + Fut: Future, + Self: Sized,

Maps this stream’s items asynchronously to a different type, returning a +new stream of the resulting type. Read more
§

fn merge<U>(self, other: U) -> Merge<Self, U>
where + U: Stream<Item = Self::Item>, + Self: Sized,

Combine two streams into one by interleaving the output of both as it +is produced. Read more
§

fn filter<F>(self, f: F) -> Filter<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Filters the values produced by this stream according to the provided +predicate. Read more
§

fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
where + F: FnMut(Self::Item) -> Option<T>, + Self: Sized,

Filters the values produced by this stream while simultaneously mapping +them to a different type according to the provided closure. Read more
§

fn fuse(self) -> Fuse<Self>
where + Self: Sized,

Creates a stream which ends after the first None. Read more
§

fn take(self, n: usize) -> Take<Self>
where + Self: Sized,

Creates a new stream of at most n items of the underlying stream. Read more
§

fn take_while<F>(self, f: F) -> TakeWhile<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Take elements from this stream while the provided predicate +resolves to true. Read more
§

fn skip(self, n: usize) -> Skip<Self>
where + Self: Sized,

Creates a new stream that will skip the n first items of the +underlying stream. Read more
§

fn skip_while<F>(self, f: F) -> SkipWhile<Self, F>
where + F: FnMut(&Self::Item) -> bool, + Self: Sized,

Skip elements from the underlying stream while the provided predicate +resolves to true. Read more
§

fn all<F>(&mut self, f: F) -> AllFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> bool,

Tests if every element of the stream matches a predicate. Read more
§

fn any<F>(&mut self, f: F) -> AnyFuture<'_, Self, F>
where + Self: Unpin, + F: FnMut(Self::Item) -> bool,

Tests if any element of the stream matches a predicate. Read more
§

fn chain<U>(self, other: U) -> Chain<Self, U>
where + U: Stream<Item = Self::Item>, + Self: Sized,

Combine two streams into one by first returning all values from the +first stream then all values from the second stream. Read more
§

fn fold<B, F>(self, init: B, f: F) -> FoldFuture<Self, B, F>
where + Self: Sized, + F: FnMut(B, Self::Item) -> B,

A combinator that applies a function to every element in a stream +producing a single, final value. Read more
§

fn collect<T>(self) -> Collect<Self, T>
where + T: FromStream<Self::Item>, + Self: Sized,

Drain stream pushing all emitted values into a collection. Read more
§

fn timeout(self, duration: Duration) -> Timeout<Self>
where + Self: Sized,

Applies a per-item timeout to the passed stream. Read more
§

fn timeout_repeating(self, interval: Interval) -> TimeoutRepeating<Self>
where + Self: Sized,

Applies a per-item timeout to the passed stream. Read more
§

fn throttle(self, duration: Duration) -> Throttle<Self>
where + Self: Sized,

Slows down a stream by enforcing a delay between items. Read more
§

fn chunks_timeout( + self, + max_size: usize, + duration: Duration, +) -> ChunksTimeout<Self>
where + Self: Sized,

Batches the items in the given stream using a maximum duration and size for each batch. Read more
§

fn peekable(self) -> Peekable<Self>
where + Self: Sized,

Turns the stream into a peekable stream, whose next element can be peeked at without being +consumed. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.ApplicationClose.html b/pr/395/docs/iroh_quinn/struct.ApplicationClose.html new file mode 100644 index 000000000..390c99703 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.ApplicationClose.html @@ -0,0 +1,38 @@ +ApplicationClose in iroh_quinn - Rust

ApplicationClose

Struct ApplicationClose 

Source
pub struct ApplicationClose {
+    pub error_code: VarInt,
+    pub reason: Bytes,
+}
Expand description

Reason given by an application for closing the connection

+

Fields§

§error_code: VarInt

Application-specific reason code

+
§reason: Bytes

Human-readable reason for the close

+

Trait Implementations§

Source§

impl Clone for ApplicationClose

Source§

fn clone(&self) -> ApplicationClose

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ApplicationClose

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for ApplicationClose

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl PartialEq for ApplicationClose

Source§

fn eq(&self, other: &ApplicationClose) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ApplicationClose

Source§

impl StructuralPartialEq for ApplicationClose

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.BloomTokenLog.html b/pr/395/docs/iroh_quinn/struct.BloomTokenLog.html new file mode 100644 index 000000000..254d486ab --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.BloomTokenLog.html @@ -0,0 +1,48 @@ +BloomTokenLog in iroh_quinn - Rust

BloomTokenLog

Struct BloomTokenLog 

Source
pub struct BloomTokenLog(/* private fields */);
Expand description

Bloom filter-based TokenLog

+

Parameterizable over an approximate maximum number of bytes to allocate. Starts out by storing +used tokens in a hash set. Once the hash set becomes too large, converts it to a bloom filter. +This achieves a memory profile of linear growth with an upper bound.

+

Divides time into periods based on lifetime and stores two filters at any given moment, for +each of the two periods currently non-expired tokens could expire in. As such, turns over +filters as time goes on to avoid bloom filter false positive rate increasing infinitely over +time.

+

Implementations§

Source§

impl BloomTokenLog

Source

pub fn new_expected_items(max_bytes: usize, expected_hits: u64) -> BloomTokenLog

Construct with an approximate maximum memory usage and expected number of validation token +usages per expiration period

+

Calculates the optimal bloom filter k number automatically.

+
Source

pub fn new(max_bytes: usize, k_num: u32) -> BloomTokenLog

Construct with an approximate maximum memory usage and a bloom filter k number

+

If choosing a custom k number, note that BloomTokenLog always maintains two filters +between them and divides the allocation budget of max_bytes evenly between them. As such, +each bloom filter will contain max_bytes * 4 bits.

+

Trait Implementations§

Source§

impl Default for BloomTokenLog

Default to 20 MiB max memory consumption and expected one million hits

+

With the default validation token lifetime of 2 weeks, this corresponds to one token usage per +1.21 seconds.

+
Source§

fn default() -> BloomTokenLog

Returns the “default value” for a type. Read more
Source§

impl TokenLog for BloomTokenLog

Source§

fn check_and_insert( + &self, + nonce: u128, + issued: SystemTime, + lifetime: Duration, +) -> Result<(), TokenReuseError>

Record that the token was used and, ideally, return a token reuse error if the token may +have been already used previously Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.Chunk.html b/pr/395/docs/iroh_quinn/struct.Chunk.html new file mode 100644 index 000000000..fe76d959e --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.Chunk.html @@ -0,0 +1,35 @@ +Chunk in iroh_quinn - Rust

Chunk

Struct Chunk 

Source
pub struct Chunk {
+    pub offset: u64,
+    pub bytes: Bytes,
+}
Expand description

A chunk of data from the receive stream

+

Fields§

§offset: u64

The offset in the stream

+
§bytes: Bytes

The contents of the chunk

+

Trait Implementations§

Source§

impl Debug for Chunk

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl PartialEq for Chunk

Source§

fn eq(&self, other: &Chunk) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for Chunk

Source§

impl StructuralPartialEq for Chunk

Auto Trait Implementations§

§

impl !Freeze for Chunk

§

impl RefUnwindSafe for Chunk

§

impl Send for Chunk

§

impl Sync for Chunk

§

impl Unpin for Chunk

§

impl UnwindSafe for Chunk

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.ClientConfig.html b/pr/395/docs/iroh_quinn/struct.ClientConfig.html new file mode 100644 index 000000000..eb0720862 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.ClientConfig.html @@ -0,0 +1,49 @@ +ClientConfig in iroh_quinn - Rust

ClientConfig

Struct ClientConfig 

Source
#[non_exhaustive]
pub struct ClientConfig { /* private fields */ }
Expand description

Configuration for outgoing connections

+

Default values should be suitable for most internet applications.

+

Implementations§

Source§

impl ClientConfig

Source

pub fn new(crypto: Arc<dyn ClientConfig>) -> ClientConfig

Create a default config with a particular cryptographic config

+
Source

pub fn initial_dst_cid_provider( + &mut self, + initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>, +) -> &mut ClientConfig

Configure how to populate the destination CID of the initial packet when attempting to +establish a new connection

+

By default, it’s populated with random bytes with reasonable length, so unless you have +a good reason, you do not need to change it.

+

When prefer to override the default, please note that the generated connection ID MUST be +at least 8 bytes long and unpredictable, as per section 7.2 of RFC 9000.

+
Source

pub fn transport_config( + &mut self, + transport: Arc<TransportConfig>, +) -> &mut ClientConfig

Set a custom TransportConfig

+
Source

pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut ClientConfig

Set a custom TokenStore

+

Defaults to TokenMemoryCache, which is suitable for most internet applications.

+
Source

pub fn version(&mut self, version: u32) -> &mut ClientConfig

Set the QUIC version to use

+
Source§

impl ClientConfig

Source

pub fn try_with_platform_verifier() -> Result<ClientConfig, Error>

Create a client configuration that trusts the platform’s native roots

+
Source

pub fn with_root_certificates( + roots: Arc<RootCertStore>, +) -> Result<ClientConfig, VerifierBuilderError>

Create a client configuration that trusts specified trust anchors

+

Trait Implementations§

Source§

impl Clone for ClientConfig

Source§

fn clone(&self) -> ClientConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClientConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.ClosePath.html b/pr/395/docs/iroh_quinn/struct.ClosePath.html new file mode 100644 index 000000000..471d42d6b --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.ClosePath.html @@ -0,0 +1,53 @@ +ClosePath in iroh_quinn - Rust

ClosePath

Struct ClosePath 

Source
pub struct ClosePath { /* private fields */ }
Expand description

Future produced by Path::close

+

Trait Implementations§

Source§

impl Future for ClosePath

Source§

type Output = Result<VarInt, ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.ClosedStream.html b/pr/395/docs/iroh_quinn/struct.ClosedStream.html new file mode 100644 index 000000000..eb725b4bb --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.ClosedStream.html @@ -0,0 +1,33 @@ +ClosedStream in iroh_quinn - Rust

ClosedStream

Struct ClosedStream 

Source
pub struct ClosedStream { /* private fields */ }
Expand description

Error indicating that a stream has not been opened or has already been finished or reset

+

Trait Implementations§

Source§

impl Clone for ClosedStream

Source§

fn clone(&self) -> ClosedStream

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosedStream

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for ClosedStream

Source§

fn default() -> ClosedStream

Returns the “default value” for a type. Read more
Source§

impl Display for ClosedStream

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for ClosedStream

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ClosedStream> for WriteError

Source§

fn from(_: ClosedStream) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ClosedStream

Source§

fn eq(&self, other: &ClosedStream) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosedStream

Source§

impl StructuralPartialEq for ClosedStream

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.Connecting.html b/pr/395/docs/iroh_quinn/struct.Connecting.html new file mode 100644 index 000000000..be02cffcf --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.Connecting.html @@ -0,0 +1,101 @@ +Connecting in iroh_quinn - Rust

Connecting

Struct Connecting 

Source
pub struct Connecting { /* private fields */ }
Expand description

In-progress connection attempt future

+

Implementations§

Source§

impl Connecting

Source

pub fn into_0rtt(self) -> Result<(Connection, ZeroRttAccepted), Self>

Convert into a 0-RTT or 0.5-RTT connection at the cost of weakened security

+

Returns Ok immediately if the local endpoint is able to attempt sending 0/0.5-RTT data. +If so, the returned Connection can be used to send application data without waiting for +the rest of the handshake to complete, at the cost of weakened cryptographic security +guarantees. The returned ZeroRttAccepted future resolves when the handshake does +complete, at which point subsequently opened streams and written data will have full +cryptographic protection.

+
§Outgoing
+

For outgoing connections, the initial attempt to convert to a Connection which sends +0-RTT data will proceed if the crypto::ClientConfig +attempts to resume a previous TLS session. However, the remote endpoint may not actually +accept the 0-RTT data–yet still accept the connection attempt in general. This +possibility is conveyed through the ZeroRttAccepted future–when the handshake +completes, it resolves to true if the 0-RTT data was accepted and false if it was rejected. +If it was rejected, the existence of streams opened and other application data sent prior +to the handshake completing will not be conveyed to the remote application, and local +operations on them will return ZeroRttRejected errors.

+

A server may reject 0-RTT data at its discretion, but accepting 0-RTT data requires the +relevant resumption state to be stored in the server, which servers may limit or lose for +various reasons including not persisting resumption state across server restarts.

+

If manually providing a crypto::ClientConfig, check your +implementation’s docs for 0-RTT pitfalls.

+
§Incoming
+

For incoming connections, conversion to 0.5-RTT will always fully succeed. into_0rtt will +always return Ok and the ZeroRttAccepted will always resolve to true.

+

If manually providing a crypto::ServerConfig, check your +implementation’s docs for 0-RTT pitfalls.

+
§Security
+

On outgoing connections, this enables transmission of 0-RTT data, which is vulnerable to +replay attacks, and should therefore never invoke non-idempotent operations.

+

On incoming connections, this enables transmission of 0.5-RTT data, which may be sent +before TLS client authentication has occurred, and should therefore not be used to send +data for which client authentication is being used.

+
Source

pub async fn handshake_data(&mut self) -> Result<Box<dyn Any>, ConnectionError>

Parameters negotiated during the handshake

+

The dynamic type returned is determined by the configured +Session. For the default rustls session, the return value can +be downcast to a +crypto::rustls::HandshakeData.

+
Source

pub fn local_ip(&self) -> Option<IpAddr>

The local IP address which was used when the peer established +the connection

+

This can be different from the address the endpoint is bound to, in case +the endpoint is bound to a wildcard address like 0.0.0.0 or ::.

+

This will return None for clients, or when the platform does not expose this +information. See quinn_udp::RecvMeta::dst_ip for a list of +supported platforms when using quinn_udp for I/O, which is the default.

+

Will panic if called after poll has returned Ready.

+
Source

pub fn remote_address(&self) -> SocketAddr

The peer’s UDP addresses

+

Will panic if called after poll has returned Ready.

+

Trait Implementations§

Source§

impl Debug for Connecting

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Future for Connecting

Source§

type Output = Result<Connection, ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.Connection.html b/pr/395/docs/iroh_quinn/struct.Connection.html new file mode 100644 index 000000000..5c48cc006 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.Connection.html @@ -0,0 +1,216 @@ +Connection in iroh_quinn - Rust

Connection

Struct Connection 

Source
pub struct Connection(/* private fields */);
Expand description

A QUIC connection.

+

If all references to a connection (including every clone of the Connection handle, streams of +incoming streams, and the various stream types) have been dropped, then the connection will be +automatically closed with an error_code of 0 and an empty reason. You can also close the +connection explicitly by calling Connection::close().

+

Closing the connection immediately abandons efforts to deliver data to the peer. Upon +receiving CONNECTION_CLOSE the peer may drop any stream data not yet delivered to the +application. Connection::close() describes in more detail how to gracefully close a +connection without losing application data.

+

May be cloned to obtain another handle to the same connection.

+

Implementations§

Source§

impl Connection

Source

pub fn weak_handle(&self) -> WeakConnectionHandle

Returns a weak reference to the inner connection struct.

+
Source

pub fn open_uni(&self) -> OpenUni<'_>

Initiate a new outgoing unidirectional stream.

+

Streams are cheap and instantaneous to open unless blocked by flow control. As a +consequence, the peer won’t be notified that a stream has been opened until the stream is +actually used.

+
Source

pub fn open_bi(&self) -> OpenBi<'_>

Initiate a new outgoing bidirectional stream.

+

Streams are cheap and instantaneous to open unless blocked by flow control. As a +consequence, the peer won’t be notified that a stream has been opened until the stream is +actually used. Calling open_bi() then waiting on the RecvStream without writing +anything to SendStream will never succeed.

+
Source

pub fn accept_uni(&self) -> AcceptUni<'_>

Accept the next incoming uni-directional stream

+
Source

pub fn accept_bi(&self) -> AcceptBi<'_>

Accept the next incoming bidirectional stream

+

Important Note: The Connection that calls open_bi() must write to its SendStream +before the other Connection is able to accept_bi(). Calling open_bi() then +waiting on the RecvStream without writing anything to SendStream will never succeed.

+
Source

pub fn read_datagram(&self) -> ReadDatagram<'_>

Receive an application datagram

+
Source

pub fn open_path_ensure( + &self, + addr: SocketAddr, + initial_status: PathStatus, +) -> OpenPath

Opens a new path if no path exists yet for the remote address.

+

Otherwise behaves exactly as open_path.

+
Source

pub fn open_path( + &self, + addr: SocketAddr, + initial_status: PathStatus, +) -> OpenPath

Opens an additional path if the multipath extension is negotiated.

+

The returned future completes once the path is either fully opened and ready to +carry application data, or if there was an error.

+

Dropping the returned future does not cancel the opening of the path, the +PathEvent::Opened event will still be emitted from Self::path_events if the +path opens. The PathId for the events can be extracted from +OpenPath::path_id.

+

Failure to open a path can either occur immediately, before polling the returned +future, or at a later time. If the failure is immediate OpenPath::path_id will +return None and the future will be ready immediately. If the failure happens +later, a PathEvent will be emitted.

+
Source

pub fn path(&self, id: PathId) -> Option<Path>

Returns the Path structure of an open path

+
Source

pub fn path_events(&self) -> Receiver<PathEvent>

A broadcast receiver of PathEvents for all paths in this connection

+
Source

pub fn nat_traversal_updates(&self) -> Receiver<Event>

A broadcast receiver of iroh_hp::Events for updates about server addresses

+
Source

pub async fn closed(&self) -> ConnectionError

Wait for the connection to be closed for any reason

+

Despite the return type’s name, closed connections are often not an error condition at the +application layer. Cases that might be routine include ConnectionError::LocallyClosed +and ConnectionError::ApplicationClosed.

+
Source

pub fn on_closed(&self) -> OnClosed

Wait for the connection to be closed without keeping a strong reference to the connection

+

Returns a future that resolves, once the connection is closed, to a tuple of +(ConnectionError, ConnectionStats).

+

Calling Self::closed keeps the connection alive until it is either closed locally via Connection::close +or closed by the remote peer. This function instead does not keep the connection itself alive, +so if all other clones of the connection are dropped, the connection will be closed implicitly even +if there are futures returned from this function still being awaited.

+
Source

pub fn close_reason(&self) -> Option<ConnectionError>

If the connection is closed, the reason why.

+

Returns None if the connection is still open.

+
Source

pub fn close(&self, error_code: VarInt, reason: &[u8])

Close the connection immediately.

+

Pending operations will fail immediately with ConnectionError::LocallyClosed. No +more data is sent to the peer and the peer may drop buffered data upon receiving +the CONNECTION_CLOSE frame.

+

error_code and reason are not interpreted, and are provided directly to the peer.

+

reason will be truncated to fit in a single packet with overhead; to improve odds that it +is preserved in full, it should be kept under 1KiB.

+
§Gracefully closing a connection
+

Only the peer last receiving application data can be certain that all data is +delivered. The only reliable action it can then take is to close the connection, +potentially with a custom error code. The delivery of the final CONNECTION_CLOSE +frame is very likely if both endpoints stay online long enough, and +Endpoint::wait_idle() can be used to provide sufficient time. Otherwise, the +remote peer will time out the connection, provided that the idle timeout is not +disabled.

+

The sending side can not guarantee all stream data is delivered to the remote +application. It only knows the data is delivered to the QUIC stack of the remote +endpoint. Once the local side sends a CONNECTION_CLOSE frame in response to calling +close() the remote endpoint may drop any data it received but is as yet +undelivered to the application, including data that was acknowledged as received to +the local endpoint.

+
Source

pub async fn handshake_confirmed(&self) -> Result<(), ConnectionError>

Wait for the handshake to be confirmed.

+

As a server, who must be authenticated by clients, +this happens when the handshake completes +upon receiving a TLS Finished message from the client. +In return, the server send a HANDSHAKE_DONE frame.

+

As a client, this happens when receiving a HANDSHAKE_DONE frame. +At this point, the server has either accepted our authentication, +or, if client authentication is not required, accepted our lack of authentication.

+
Source

pub fn send_datagram(&self, data: Bytes) -> Result<(), SendDatagramError>

Transmit data as an unreliable, unordered application datagram

+

Application datagrams are a low-level primitive. They may be lost or delivered out of order, +and data must both fit inside a single QUIC packet and be smaller than the maximum +dictated by the peer.

+

Previously queued datagrams which are still unsent may be discarded to make space for this +datagram, in order of oldest to newest.

+
Source

pub fn send_datagram_wait(&self, data: Bytes) -> SendDatagram<'_>

Transmit data as an unreliable, unordered application datagram

+

Unlike send_datagram(), this method will wait for buffer space during congestion +conditions, which effectively prioritizes old datagrams over new datagrams.

+

See send_datagram() for details.

+
Source

pub fn max_datagram_size(&self) -> Option<usize>

Compute the maximum size of datagrams that may be passed to send_datagram().

+

Returns None if datagrams are unsupported by the peer or disabled locally.

+

This may change over the lifetime of a connection according to variation in the path MTU +estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer’s +limit is large this is guaranteed to be a little over a kilobyte at minimum.

+

Not necessarily the maximum size of received datagrams.

+
Source

pub fn datagram_send_buffer_space(&self) -> usize

Bytes available in the outgoing datagram buffer

+

When greater than zero, calling send_datagram() with a datagram of +at most this size is guaranteed not to cause older datagrams to be dropped.

+
Source

pub fn side(&self) -> Side

The side of the connection (client or server)

+
Source

pub fn remote_address(&self) -> SocketAddr

The peer’s UDP address

+

If ServerConfig::migration is true, clients may change addresses at will, +e.g. when switching to a cellular internet connection.

+

If multipath is enabled this will return the address of any +path, and may not be consistent. Prefer Path::remote_address instead.

+
Source

pub fn local_ip(&self) -> Option<IpAddr>

The local IP address which was used when the peer established +the connection

+

This can be different from the address the endpoint is bound to, in case +the endpoint is bound to a wildcard address like 0.0.0.0 or ::.

+

This will return None for clients, or when the platform does not expose this +information. See quinn_udp::RecvMeta::dst_ip for a list of +supported platforms when using quinn_udp for I/O, which is the default.

+
Source

pub fn rtt(&self, path_id: PathId) -> Option<Duration>

Current best estimate of this connection’s latency (round-trip-time)

+
Source

pub fn stats(&self) -> ConnectionStats

Returns connection statistics

+
Source

pub fn path_stats(&self, path_id: PathId) -> Option<PathStats>

Returns path statistics

+
Source

pub fn congestion_state(&self, path_id: PathId) -> Option<Box<dyn Controller>>

Current state of the congestion control algorithm, for debugging purposes

+
Source

pub fn handshake_data(&self) -> Option<Box<dyn Any>>

Parameters negotiated during the handshake

+

Guaranteed to return Some on fully established connections or after +Connecting::handshake_data() succeeds. See that method’s documentations for details on +the returned value.

+
Source

pub fn peer_identity(&self) -> Option<Box<dyn Any>>

Cryptographic identity of the peer

+

The dynamic type returned is determined by the configured +Session. For the default rustls session, the return value can +be downcast to a Vec<[rustls::pki_types::CertificateDer]>

+
Source

pub fn stable_id(&self) -> usize

A stable identifier for this connection

+

Peer addresses and connection IDs can change, but this value will remain +fixed for the lifetime of the connection.

+
Source

pub fn force_key_update(&self)

Update traffic keys spontaneously

+

This primarily exists for testing purposes.

+
Source

pub fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: &[u8], +) -> Result<(), ExportKeyingMaterialError>

Derive keying material from this connection’s TLS session secrets.

+

When both peers call this method with the same label and context +arguments and output buffers of equal length, they will get the +same sequence of bytes in output. These bytes are cryptographically +strong and pseudorandom, and are suitable for use as keying material.

+

See RFC5705 for more information.

+
Source

pub fn set_max_concurrent_uni_streams(&self, count: VarInt)

Modify the number of remotely initiated unidirectional streams that may be concurrently open

+

No streams may be opened by the peer unless fewer than count are already open. Large +counts increase both minimum and worst-case memory consumption.

+
Source

pub fn set_send_window(&self, send_window: u64)

Source

pub fn set_receive_window(&self, receive_window: VarInt)

Source

pub fn set_max_concurrent_bi_streams(&self, count: VarInt)

Modify the number of remotely initiated bidirectional streams that may be concurrently open

+

No streams may be opened by the peer unless fewer than count are already open. Large +counts increase both minimum and worst-case memory consumption.

+
Source

pub fn observed_external_addr(&self) -> Receiver<Option<SocketAddr>>

Track changed on our external address as reported by the peer.

+
Source

pub fn is_multipath_enabled(&self) -> bool

Is multipath enabled?

+
Source

pub fn add_nat_traversal_address( + &self, + address: SocketAddr, +) -> Result<(), Error>

Registers one address at which this endpoint might be reachable

+

When the NAT traversal extension is negotiated, servers send these addresses to clients in +ADD_ADDRESS frames. This allows clients to obtain server address candidates to initiate +NAT traversal attempts. Clients provide their own reachable addresses in REACH_OUT frames +when Self::initiate_nat_traversal_round is called.

+
Source

pub fn remove_nat_traversal_address( + &self, + address: SocketAddr, +) -> Result<(), Error>

Removes one or more addresses from the set of addresses at which this endpoint is reachable

+

When the NAT traversal extension is negotiated, servers send address removals to +clients in REMOVE_ADDRESS frames. This allows clients to stop using outdated +server address candidates that are no longer valid for NAT traversal.

+

For clients, removed addresses will no longer be advertised in REACH_OUT frames.

+

Addresses not present in the set will be silently ignored.

+
Source

pub fn get_local_nat_traversal_addresses( + &self, +) -> Result<Vec<SocketAddr>, Error>

Get the current local nat traversal addresses

+
Source

pub fn get_remote_nat_traversal_addresses( + &self, +) -> Result<Vec<SocketAddr>, Error>

Get the currently advertised nat traversal addresses by the server

+
Source

pub fn initiate_nat_traversal_round(&self) -> Result<Vec<SocketAddr>, Error>

Initiates a new nat traversal round

+

A nat traversal round involves advertising the client’s local addresses in REACH_OUT +frames, and initiating probing of the known remote addresses. When a new round is +initiated, the previous one is cancelled, and paths that have not been opened are closed.

+

Returns the server addresses that are now being probed.

+

Trait Implementations§

Source§

impl Clone for Connection

Source§

fn clone(&self) -> Connection

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Connection

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.ConnectionClose.html b/pr/395/docs/iroh_quinn/struct.ConnectionClose.html new file mode 100644 index 000000000..b635063b0 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.ConnectionClose.html @@ -0,0 +1,40 @@ +ConnectionClose in iroh_quinn - Rust

ConnectionClose

Struct ConnectionClose 

Source
pub struct ConnectionClose {
+    pub error_code: Code,
+    pub frame_type: MaybeFrame,
+    pub reason: Bytes,
+}
Expand description

Reason given by the transport for closing the connection

+

Fields§

§error_code: Code

Class of error as encoded in the specification

+
§frame_type: MaybeFrame

Type of frame that caused the close

+
§reason: Bytes

Human-readable reason for the close

+

Trait Implementations§

Source§

impl Clone for ConnectionClose

Source§

fn clone(&self) -> ConnectionClose

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionClose

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectionClose

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl From<Error> for ConnectionClose

Source§

fn from(x: Error) -> ConnectionClose

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionClose

Source§

fn eq(&self, other: &ConnectionClose) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConnectionClose

Source§

impl StructuralPartialEq for ConnectionClose

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.ConnectionId.html b/pr/395/docs/iroh_quinn/struct.ConnectionId.html new file mode 100644 index 000000000..1c1c574cf --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.ConnectionId.html @@ -0,0 +1,2469 @@ +ConnectionId in iroh_quinn - Rust

ConnectionId

Struct ConnectionId 

Source
pub struct ConnectionId { /* private fields */ }
Expand description

Protocol-level identifier for a connection.

+

Mainly useful for identifying this connection’s packets on the wire with tools like Wireshark.

+

Implementations§

Source§

impl ConnectionId

Source

pub fn new(bytes: &[u8]) -> ConnectionId

Construct cid from byte array

+
Source

pub fn from_buf(buf: &mut (impl Buf + ?Sized), len: usize) -> ConnectionId

Constructs cid by reading len bytes from a Buf

+

Callers need to assure that buf.remaining() >= len

+

Methods from Deref<Target = [u8]>§

1.23.0 · Source

pub fn is_ascii(&self) -> bool

Checks if all bytes in this slice are within the ASCII range.

+
Source

pub fn as_ascii(&self) -> Option<&[AsciiChar]>

🔬This is a nightly-only experimental API. (ascii_char)

If this slice is_ascii, returns it as a slice of +ASCII characters, otherwise returns None.

+
Source

pub unsafe fn as_ascii_unchecked(&self) -> &[AsciiChar]

🔬This is a nightly-only experimental API. (ascii_char)

Converts this slice of bytes into a slice of ASCII characters, +without checking whether they’re valid.

+
§Safety
+

Every byte in the slice must be in 0..=127, or else this is UB.

+
1.23.0 · Source

pub fn eq_ignore_ascii_case(&self, other: &[u8]) -> bool

Checks that two slices are an ASCII case-insensitive match.

+

Same as to_ascii_lowercase(a) == to_ascii_lowercase(b), +but without allocating and copying temporaries.

+
1.23.0 · Source

pub fn make_ascii_uppercase(&mut self)

Converts this slice to its ASCII upper case equivalent in-place.

+

ASCII letters ‘a’ to ‘z’ are mapped to ‘A’ to ‘Z’, +but non-ASCII letters are unchanged.

+

To return a new uppercased value without modifying the existing one, use +to_ascii_uppercase.

+
1.23.0 · Source

pub fn make_ascii_lowercase(&mut self)

Converts this slice to its ASCII lower case equivalent in-place.

+

ASCII letters ‘A’ to ‘Z’ are mapped to ‘a’ to ‘z’, +but non-ASCII letters are unchanged.

+

To return a new lowercased value without modifying the existing one, use +to_ascii_lowercase.

+
1.60.0 · Source

pub fn escape_ascii(&self) -> EscapeAscii<'_>

Returns an iterator that produces an escaped version of this slice, +treating it as an ASCII string.

+
§Examples
+
let s = b"0\t\r\n'\"\\\x9d";
+let escaped = s.escape_ascii().to_string();
+assert_eq!(escaped, "0\\t\\r\\n\\'\\\"\\\\\\x9d");
1.80.0 · Source

pub fn trim_ascii_start(&self) -> &[u8]

Returns a byte slice with leading ASCII whitespace bytes removed.

+

‘Whitespace’ refers to the definition used by +u8::is_ascii_whitespace.

+
§Examples
+
assert_eq!(b" \t hello world\n".trim_ascii_start(), b"hello world\n");
+assert_eq!(b"  ".trim_ascii_start(), b"");
+assert_eq!(b"".trim_ascii_start(), b"");
1.80.0 · Source

pub fn trim_ascii_end(&self) -> &[u8]

Returns a byte slice with trailing ASCII whitespace bytes removed.

+

‘Whitespace’ refers to the definition used by +u8::is_ascii_whitespace.

+
§Examples
+
assert_eq!(b"\r hello world\n ".trim_ascii_end(), b"\r hello world");
+assert_eq!(b"  ".trim_ascii_end(), b"");
+assert_eq!(b"".trim_ascii_end(), b"");
1.80.0 · Source

pub fn trim_ascii(&self) -> &[u8]

Returns a byte slice with leading and trailing ASCII whitespace bytes +removed.

+

‘Whitespace’ refers to the definition used by +u8::is_ascii_whitespace.

+
§Examples
+
assert_eq!(b"\r hello world\n ".trim_ascii(), b"hello world");
+assert_eq!(b"  ".trim_ascii(), b"");
+assert_eq!(b"".trim_ascii(), b"");
1.0.0 · Source

pub fn len(&self) -> usize

Returns the number of elements in the slice.

+
§Examples
+
let a = [1, 2, 3];
+assert_eq!(a.len(), 3);
1.0.0 · Source

pub fn is_empty(&self) -> bool

Returns true if the slice has a length of 0.

+
§Examples
+
let a = [1, 2, 3];
+assert!(!a.is_empty());
+
+let b: &[i32] = &[];
+assert!(b.is_empty());
1.0.0 · Source

pub fn first(&self) -> Option<&T>

Returns the first element of the slice, or None if it is empty.

+
§Examples
+
let v = [10, 40, 30];
+assert_eq!(Some(&10), v.first());
+
+let w: &[i32] = &[];
+assert_eq!(None, w.first());
1.0.0 · Source

pub fn first_mut(&mut self) -> Option<&mut T>

Returns a mutable reference to the first element of the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(first) = x.first_mut() {
+    *first = 5;
+}
+assert_eq!(x, &[5, 1, 2]);
+
+let y: &mut [i32] = &mut [];
+assert_eq!(None, y.first_mut());
1.5.0 · Source

pub fn split_first(&self) -> Option<(&T, &[T])>

Returns the first and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((first, elements)) = x.split_first() {
+    assert_eq!(first, &0);
+    assert_eq!(elements, &[1, 2]);
+}
1.5.0 · Source

pub fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])>

Returns the first and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_mut() {
+    *first = 3;
+    elements[0] = 4;
+    elements[1] = 5;
+}
+assert_eq!(x, &[3, 4, 5]);
1.5.0 · Source

pub fn split_last(&self) -> Option<(&T, &[T])>

Returns the last and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((last, elements)) = x.split_last() {
+    assert_eq!(last, &2);
+    assert_eq!(elements, &[0, 1]);
+}
1.5.0 · Source

pub fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])>

Returns the last and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((last, elements)) = x.split_last_mut() {
+    *last = 3;
+    elements[0] = 4;
+    elements[1] = 5;
+}
+assert_eq!(x, &[4, 5, 3]);
1.0.0 · Source

pub fn last(&self) -> Option<&T>

Returns the last element of the slice, or None if it is empty.

+
§Examples
+
let v = [10, 40, 30];
+assert_eq!(Some(&30), v.last());
+
+let w: &[i32] = &[];
+assert_eq!(None, w.last());
1.0.0 · Source

pub fn last_mut(&mut self) -> Option<&mut T>

Returns a mutable reference to the last item in the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(last) = x.last_mut() {
+    *last = 10;
+}
+assert_eq!(x, &[0, 1, 10]);
+
+let y: &mut [i32] = &mut [];
+assert_eq!(None, y.last_mut());
1.77.0 · Source

pub fn first_chunk<const N: usize>(&self) -> Option<&[T; N]>

Returns an array reference to the first N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let u = [10, 40, 30];
+assert_eq!(Some(&[10, 40]), u.first_chunk::<2>());
+
+let v: &[i32] = &[10];
+assert_eq!(None, v.first_chunk::<2>());
+
+let w: &[i32] = &[];
+assert_eq!(Some(&[]), w.first_chunk::<0>());
1.77.0 · Source

pub fn first_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>

Returns a mutable array reference to the first N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(first) = x.first_chunk_mut::<2>() {
+    first[0] = 5;
+    first[1] = 4;
+}
+assert_eq!(x, &[5, 4, 2]);
+
+assert_eq!(None, x.first_chunk_mut::<4>());
1.77.0 · Source

pub fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])>

Returns an array reference to the first N items in the slice and the remaining slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_chunk::<2>() {
+    assert_eq!(first, &[0, 1]);
+    assert_eq!(elements, &[2]);
+}
+
+assert_eq!(None, x.split_first_chunk::<4>());
1.77.0 · Source

pub fn split_first_chunk_mut<const N: usize>( + &mut self, +) -> Option<(&mut [T; N], &mut [T])>

Returns a mutable array reference to the first N items in the slice and the remaining +slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_chunk_mut::<2>() {
+    first[0] = 3;
+    first[1] = 4;
+    elements[0] = 5;
+}
+assert_eq!(x, &[3, 4, 5]);
+
+assert_eq!(None, x.split_first_chunk_mut::<4>());
1.77.0 · Source

pub fn split_last_chunk<const N: usize>(&self) -> Option<(&[T], &[T; N])>

Returns an array reference to the last N items in the slice and the remaining slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((elements, last)) = x.split_last_chunk::<2>() {
+    assert_eq!(elements, &[0]);
+    assert_eq!(last, &[1, 2]);
+}
+
+assert_eq!(None, x.split_last_chunk::<4>());
1.77.0 · Source

pub fn split_last_chunk_mut<const N: usize>( + &mut self, +) -> Option<(&mut [T], &mut [T; N])>

Returns a mutable array reference to the last N items in the slice and the remaining +slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((elements, last)) = x.split_last_chunk_mut::<2>() {
+    last[0] = 3;
+    last[1] = 4;
+    elements[0] = 5;
+}
+assert_eq!(x, &[5, 3, 4]);
+
+assert_eq!(None, x.split_last_chunk_mut::<4>());
1.77.0 · Source

pub fn last_chunk<const N: usize>(&self) -> Option<&[T; N]>

Returns an array reference to the last N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let u = [10, 40, 30];
+assert_eq!(Some(&[40, 30]), u.last_chunk::<2>());
+
+let v: &[i32] = &[10];
+assert_eq!(None, v.last_chunk::<2>());
+
+let w: &[i32] = &[];
+assert_eq!(Some(&[]), w.last_chunk::<0>());
1.77.0 · Source

pub fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>

Returns a mutable array reference to the last N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(last) = x.last_chunk_mut::<2>() {
+    last[0] = 10;
+    last[1] = 20;
+}
+assert_eq!(x, &[0, 10, 20]);
+
+assert_eq!(None, x.last_chunk_mut::<4>());
1.0.0 · Source

pub fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output>
where + I: SliceIndex<[T]>,

Returns a reference to an element or subslice depending on the type of +index.

+
    +
  • If given a position, returns a reference to the element at that +position or None if out of bounds.
  • +
  • If given a range, returns the subslice corresponding to that range, +or None if out of bounds.
  • +
+
§Examples
+
let v = [10, 40, 30];
+assert_eq!(Some(&40), v.get(1));
+assert_eq!(Some(&[10, 40][..]), v.get(0..2));
+assert_eq!(None, v.get(3));
+assert_eq!(None, v.get(0..4));
1.0.0 · Source

pub fn get_mut<I>( + &mut self, + index: I, +) -> Option<&mut <I as SliceIndex<[T]>>::Output>
where + I: SliceIndex<[T]>,

Returns a mutable reference to an element or subslice depending on the +type of index (see get) or None if the index is out of bounds.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(elem) = x.get_mut(1) {
+    *elem = 42;
+}
+assert_eq!(x, &[0, 42, 2]);
1.0.0 · Source

pub unsafe fn get_unchecked<I>( + &self, + index: I, +) -> &<I as SliceIndex<[T]>>::Output
where + I: SliceIndex<[T]>,

Returns a reference to an element or subslice, without doing bounds +checking.

+

For a safe alternative see get.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used.

+

You can think of this like .get(index).unwrap_unchecked(). It’s UB +to call .get_unchecked(len), even if you immediately convert to a +pointer. And it’s UB to call .get_unchecked(..len + 1), +.get_unchecked(..=len), or similar.

+
§Examples
+
let x = &[1, 2, 4];
+
+unsafe {
+    assert_eq!(x.get_unchecked(1), &2);
+}
1.0.0 · Source

pub unsafe fn get_unchecked_mut<I>( + &mut self, + index: I, +) -> &mut <I as SliceIndex<[T]>>::Output
where + I: SliceIndex<[T]>,

Returns a mutable reference to an element or subslice, without doing +bounds checking.

+

For a safe alternative see get_mut.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used.

+

You can think of this like .get_mut(index).unwrap_unchecked(). It’s +UB to call .get_unchecked_mut(len), even if you immediately convert +to a pointer. And it’s UB to call .get_unchecked_mut(..len + 1), +.get_unchecked_mut(..=len), or similar.

+
§Examples
+
let x = &mut [1, 2, 4];
+
+unsafe {
+    let elem = x.get_unchecked_mut(1);
+    *elem = 13;
+}
+assert_eq!(x, &[1, 13, 4]);
1.0.0 · Source

pub fn as_ptr(&self) -> *const T

Returns a raw pointer to the slice’s buffer.

+

The caller must ensure that the slice outlives the pointer this +function returns, or else it will end up dangling.

+

The caller must also ensure that the memory the pointer (non-transitively) points to +is never written to (except inside an UnsafeCell) using this pointer or any pointer +derived from it. If you need to mutate the contents of the slice, use as_mut_ptr.

+

Modifying the container referenced by this slice may cause its buffer +to be reallocated, which would also make any pointers to it invalid.

+
§Examples
+
let x = &[1, 2, 4];
+let x_ptr = x.as_ptr();
+
+unsafe {
+    for i in 0..x.len() {
+        assert_eq!(x.get_unchecked(i), &*x_ptr.add(i));
+    }
+}
1.0.0 · Source

pub fn as_mut_ptr(&mut self) -> *mut T

Returns an unsafe mutable pointer to the slice’s buffer.

+

The caller must ensure that the slice outlives the pointer this +function returns, or else it will end up dangling.

+

Modifying the container referenced by this slice may cause its buffer +to be reallocated, which would also make any pointers to it invalid.

+
§Examples
+
let x = &mut [1, 2, 4];
+let x_ptr = x.as_mut_ptr();
+
+unsafe {
+    for i in 0..x.len() {
+        *x_ptr.add(i) += 2;
+    }
+}
+assert_eq!(x, &[3, 4, 6]);
1.48.0 · Source

pub fn as_ptr_range(&self) -> Range<*const T>

Returns the two raw pointers spanning the slice.

+

The returned range is half-open, which means that the end pointer +points one past the last element of the slice. This way, an empty +slice is represented by two equal pointers, and the difference between +the two pointers represents the size of the slice.

+

See as_ptr for warnings on using these pointers. The end pointer +requires extra caution, as it does not point to a valid element in the +slice.

+

This function is useful for interacting with foreign interfaces which +use two pointers to refer to a range of elements in memory, as is +common in C++.

+

It can also be useful to check if a pointer to an element refers to an +element of this slice:

+ +
let a = [1, 2, 3];
+let x = &a[1] as *const _;
+let y = &5 as *const _;
+
+assert!(a.as_ptr_range().contains(&x));
+assert!(!a.as_ptr_range().contains(&y));
1.48.0 · Source

pub fn as_mut_ptr_range(&mut self) -> Range<*mut T>

Returns the two unsafe mutable pointers spanning the slice.

+

The returned range is half-open, which means that the end pointer +points one past the last element of the slice. This way, an empty +slice is represented by two equal pointers, and the difference between +the two pointers represents the size of the slice.

+

See as_mut_ptr for warnings on using these pointers. The end +pointer requires extra caution, as it does not point to a valid element +in the slice.

+

This function is useful for interacting with foreign interfaces which +use two pointers to refer to a range of elements in memory, as is +common in C++.

+
Source

pub fn as_array<const N: usize>(&self) -> Option<&[T; N]>

🔬This is a nightly-only experimental API. (slice_as_array)

Gets a reference to the underlying array.

+

If N is not exactly equal to the length of self, then this method returns None.

+
Source

pub fn as_mut_array<const N: usize>(&mut self) -> Option<&mut [T; N]>

🔬This is a nightly-only experimental API. (slice_as_array)

Gets a mutable reference to the slice’s underlying array.

+

If N is not exactly equal to the length of self, then this method returns None.

+
1.0.0 · Source

pub fn swap(&mut self, a: usize, b: usize)

Swaps two elements in the slice.

+

If a equals to b, it’s guaranteed that elements won’t change value.

+
§Arguments
+
    +
  • a - The index of the first element
  • +
  • b - The index of the second element
  • +
+
§Panics
+

Panics if a or b are out of bounds.

+
§Examples
+
let mut v = ["a", "b", "c", "d", "e"];
+v.swap(2, 4);
+assert!(v == ["a", "b", "e", "d", "c"]);
Source

pub unsafe fn swap_unchecked(&mut self, a: usize, b: usize)

🔬This is a nightly-only experimental API. (slice_swap_unchecked)

Swaps two elements in the slice, without doing bounds checking.

+

For a safe alternative see swap.

+
§Arguments
+
    +
  • a - The index of the first element
  • +
  • b - The index of the second element
  • +
+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior. +The caller has to ensure that a < self.len() and b < self.len().

+
§Examples
+
#![feature(slice_swap_unchecked)]
+
+let mut v = ["a", "b", "c", "d"];
+// SAFETY: we know that 1 and 3 are both indices of the slice
+unsafe { v.swap_unchecked(1, 3) };
+assert!(v == ["a", "d", "c", "b"]);
1.0.0 · Source

pub fn reverse(&mut self)

Reverses the order of elements in the slice, in place.

+
§Examples
+
let mut v = [1, 2, 3];
+v.reverse();
+assert!(v == [3, 2, 1]);
1.0.0 · Source

pub fn iter(&self) -> Iter<'_, T>

Returns an iterator over the slice.

+

The iterator yields all items from start to end.

+
§Examples
+
let x = &[1, 2, 4];
+let mut iterator = x.iter();
+
+assert_eq!(iterator.next(), Some(&1));
+assert_eq!(iterator.next(), Some(&2));
+assert_eq!(iterator.next(), Some(&4));
+assert_eq!(iterator.next(), None);
1.0.0 · Source

pub fn iter_mut(&mut self) -> IterMut<'_, T>

Returns an iterator that allows modifying each value.

+

The iterator yields all items from start to end.

+
§Examples
+
let x = &mut [1, 2, 4];
+for elem in x.iter_mut() {
+    *elem += 2;
+}
+assert_eq!(x, &[3, 4, 6]);
1.0.0 · Source

pub fn windows(&self, size: usize) -> Windows<'_, T>

Returns an iterator over all contiguous windows of length +size. The windows overlap. If the slice is shorter than +size, the iterator returns no values.

+
§Panics
+

Panics if size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.windows(3);
+assert_eq!(iter.next().unwrap(), &['l', 'o', 'r']);
+assert_eq!(iter.next().unwrap(), &['o', 'r', 'e']);
+assert_eq!(iter.next().unwrap(), &['r', 'e', 'm']);
+assert!(iter.next().is_none());
+

If the slice is shorter than size:

+ +
let slice = ['f', 'o', 'o'];
+let mut iter = slice.windows(4);
+assert!(iter.next().is_none());
+

Because the Iterator trait cannot represent the required lifetimes, +there is no windows_mut analog to windows; +[0,1,2].windows_mut(2).collect() would violate the rules of references +(though a LendingIterator analog is possible). You can sometimes use +Cell::as_slice_of_cells in +conjunction with windows instead:

+ +
use std::cell::Cell;
+
+let mut array = ['R', 'u', 's', 't', ' ', '2', '0', '1', '5'];
+let slice = &mut array[..];
+let slice_of_cells: &[Cell<char>] = Cell::from_mut(slice).as_slice_of_cells();
+for w in slice_of_cells.windows(3) {
+    Cell::swap(&w[0], &w[2]);
+}
+assert_eq!(array, ['s', 't', ' ', '2', '0', '1', '5', 'u', 'R']);
1.0.0 · Source

pub fn chunks(&self, chunk_size: usize) -> Chunks<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last chunk will not have length chunk_size.

+

See chunks_exact for a variant of this iterator that returns chunks of always exactly +chunk_size elements, and rchunks for the same iterator but starting at the end of the +slice.

+

If your chunk_size is a constant, consider using as_chunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.chunks(2);
+assert_eq!(iter.next().unwrap(), &['l', 'o']);
+assert_eq!(iter.next().unwrap(), &['r', 'e']);
+assert_eq!(iter.next().unwrap(), &['m']);
+assert!(iter.next().is_none());
1.0.0 · Source

pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last chunk will not have length chunk_size.

+

See chunks_exact_mut for a variant of this iterator that returns chunks of always +exactly chunk_size elements, and rchunks_mut for the same iterator but starting at +the end of the slice.

+

If your chunk_size is a constant, consider using as_chunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.chunks_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 3]);
1.31.0 · Source

pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last up to chunk_size-1 elements will be omitted and can be retrieved +from the remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of chunks.

+

See chunks for a variant of this iterator that also returns the remainder as a smaller +chunk, and rchunks_exact for the same iterator but starting at the end of the slice.

+

If your chunk_size is a constant, consider using as_chunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.chunks_exact(2);
+assert_eq!(iter.next().unwrap(), &['l', 'o']);
+assert_eq!(iter.next().unwrap(), &['r', 'e']);
+assert!(iter.next().is_none());
+assert_eq!(iter.remainder(), &['m']);
1.31.0 · Source

pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last up to chunk_size-1 elements will be omitted and can be +retrieved from the into_remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of chunks_mut.

+

See chunks_mut for a variant of this iterator that also returns the remainder as a +smaller chunk, and rchunks_exact_mut for the same iterator but starting at the end of +the slice.

+

If your chunk_size is a constant, consider using as_chunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.chunks_exact_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 0]);
1.88.0 · Source

pub unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]]

Splits the slice into a slice of N-element arrays, +assuming that there’s no remainder.

+

This is the inverse operation to as_flattened.

+

As this is unsafe, consider whether you could use as_chunks or +as_rchunks instead, perhaps via something like +if let (chunks, []) = slice.as_chunks() or +let (chunks, []) = slice.as_chunks() else { unreachable!() };.

+
§Safety
+

This may only be called when

+
    +
  • The slice splits exactly into N-element chunks (aka self.len() % N == 0).
  • +
  • N != 0.
  • +
+
§Examples
+
let slice: &[char] = &['l', 'o', 'r', 'e', 'm', '!'];
+let chunks: &[[char; 1]] =
+    // SAFETY: 1-element chunks never have remainder
+    unsafe { slice.as_chunks_unchecked() };
+assert_eq!(chunks, &[['l'], ['o'], ['r'], ['e'], ['m'], ['!']]);
+let chunks: &[[char; 3]] =
+    // SAFETY: The slice length (6) is a multiple of 3
+    unsafe { slice.as_chunks_unchecked() };
+assert_eq!(chunks, &[['l', 'o', 'r'], ['e', 'm', '!']]);
+
+// These would be unsound:
+// let chunks: &[[_; 5]] = slice.as_chunks_unchecked() // The slice length is not a multiple of 5
+// let chunks: &[[_; 0]] = slice.as_chunks_unchecked() // Zero-length chunks are never allowed
1.88.0 · Source

pub fn as_chunks<const N: usize>(&self) -> (&[[T; N]], &[T])

Splits the slice into a slice of N-element arrays, +starting at the beginning of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (chunks, remainder) = slice.as_chunks(), then:

+
    +
  • chunks.len() equals slice.len() / N,
  • +
  • remainder.len() equals slice.len() % N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let (chunks, remainder) = slice.as_chunks();
+assert_eq!(chunks, &[['l', 'o'], ['r', 'e']]);
+assert_eq!(remainder, &['m']);
+

If you expect the slice to be an exact multiple, you can combine +let-else with an empty slice pattern:

+ +
let slice = ['R', 'u', 's', 't'];
+let (chunks, []) = slice.as_chunks::<2>() else {
+    panic!("slice didn't have even length")
+};
+assert_eq!(chunks, &[['R', 'u'], ['s', 't']]);
1.88.0 · Source

pub fn as_rchunks<const N: usize>(&self) -> (&[T], &[[T; N]])

Splits the slice into a slice of N-element arrays, +starting at the end of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (remainder, chunks) = slice.as_rchunks(), then:

+
    +
  • remainder.len() equals slice.len() % N,
  • +
  • chunks.len() equals slice.len() / N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let (remainder, chunks) = slice.as_rchunks();
+assert_eq!(remainder, &['l']);
+assert_eq!(chunks, &[['o', 'r'], ['e', 'm']]);
1.88.0 · Source

pub unsafe fn as_chunks_unchecked_mut<const N: usize>( + &mut self, +) -> &mut [[T; N]]

Splits the slice into a slice of N-element arrays, +assuming that there’s no remainder.

+

This is the inverse operation to as_flattened_mut.

+

As this is unsafe, consider whether you could use as_chunks_mut or +as_rchunks_mut instead, perhaps via something like +if let (chunks, []) = slice.as_chunks_mut() or +let (chunks, []) = slice.as_chunks_mut() else { unreachable!() };.

+
§Safety
+

This may only be called when

+
    +
  • The slice splits exactly into N-element chunks (aka self.len() % N == 0).
  • +
  • N != 0.
  • +
+
§Examples
+
let slice: &mut [char] = &mut ['l', 'o', 'r', 'e', 'm', '!'];
+let chunks: &mut [[char; 1]] =
+    // SAFETY: 1-element chunks never have remainder
+    unsafe { slice.as_chunks_unchecked_mut() };
+chunks[0] = ['L'];
+assert_eq!(chunks, &[['L'], ['o'], ['r'], ['e'], ['m'], ['!']]);
+let chunks: &mut [[char; 3]] =
+    // SAFETY: The slice length (6) is a multiple of 3
+    unsafe { slice.as_chunks_unchecked_mut() };
+chunks[1] = ['a', 'x', '?'];
+assert_eq!(slice, &['L', 'o', 'r', 'a', 'x', '?']);
+
+// These would be unsound:
+// let chunks: &[[_; 5]] = slice.as_chunks_unchecked_mut() // The slice length is not a multiple of 5
+// let chunks: &[[_; 0]] = slice.as_chunks_unchecked_mut() // Zero-length chunks are never allowed
1.88.0 · Source

pub fn as_chunks_mut<const N: usize>(&mut self) -> (&mut [[T; N]], &mut [T])

Splits the slice into a slice of N-element arrays, +starting at the beginning of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (chunks, remainder) = slice.as_chunks_mut(), then:

+
    +
  • chunks.len() equals slice.len() / N,
  • +
  • remainder.len() equals slice.len() % N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened_mut.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+let (chunks, remainder) = v.as_chunks_mut();
+remainder[0] = 9;
+for chunk in chunks {
+    *chunk = [count; 2];
+    count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 9]);
1.88.0 · Source

pub fn as_rchunks_mut<const N: usize>(&mut self) -> (&mut [T], &mut [[T; N]])

Splits the slice into a slice of N-element arrays, +starting at the end of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (remainder, chunks) = slice.as_rchunks_mut(), then:

+
    +
  • remainder.len() equals slice.len() % N,
  • +
  • chunks.len() equals slice.len() / N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened_mut.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+let (remainder, chunks) = v.as_rchunks_mut();
+remainder[0] = 9;
+for chunk in chunks {
+    *chunk = [count; 2];
+    count += 1;
+}
+assert_eq!(v, &[9, 1, 1, 2, 2]);
Source

pub fn array_windows<const N: usize>(&self) -> ArrayWindows<'_, T, N>

🔬This is a nightly-only experimental API. (array_windows)

Returns an iterator over overlapping windows of N elements of a slice, +starting at the beginning of the slice.

+

This is the const generic equivalent of windows.

+

If N is greater than the size of the slice, it will return no windows.

+
§Panics
+

Panics if N is zero. This check will most probably get changed to a compile time +error before this method gets stabilized.

+
§Examples
+
#![feature(array_windows)]
+let slice = [0, 1, 2, 3];
+let mut iter = slice.array_windows();
+assert_eq!(iter.next().unwrap(), &[0, 1]);
+assert_eq!(iter.next().unwrap(), &[1, 2]);
+assert_eq!(iter.next().unwrap(), &[2, 3]);
+assert!(iter.next().is_none());
1.31.0 · Source

pub fn rchunks(&self, chunk_size: usize) -> RChunks<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the end +of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last chunk will not have length chunk_size.

+

See rchunks_exact for a variant of this iterator that returns chunks of always exactly +chunk_size elements, and chunks for the same iterator but starting at the beginning +of the slice.

+

If your chunk_size is a constant, consider using as_rchunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.rchunks(2);
+assert_eq!(iter.next().unwrap(), &['e', 'm']);
+assert_eq!(iter.next().unwrap(), &['o', 'r']);
+assert_eq!(iter.next().unwrap(), &['l']);
+assert!(iter.next().is_none());
1.31.0 · Source

pub fn rchunks_mut(&mut self, chunk_size: usize) -> RChunksMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the end +of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last chunk will not have length chunk_size.

+

See rchunks_exact_mut for a variant of this iterator that returns chunks of always +exactly chunk_size elements, and chunks_mut for the same iterator but starting at the +beginning of the slice.

+

If your chunk_size is a constant, consider using as_rchunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.rchunks_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[3, 2, 2, 1, 1]);
1.31.0 · Source

pub fn rchunks_exact(&self, chunk_size: usize) -> RChunksExact<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +end of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last up to chunk_size-1 elements will be omitted and can be retrieved +from the remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of rchunks.

+

See rchunks for a variant of this iterator that also returns the remainder as a smaller +chunk, and chunks_exact for the same iterator but starting at the beginning of the +slice.

+

If your chunk_size is a constant, consider using as_rchunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.rchunks_exact(2);
+assert_eq!(iter.next().unwrap(), &['e', 'm']);
+assert_eq!(iter.next().unwrap(), &['o', 'r']);
+assert!(iter.next().is_none());
+assert_eq!(iter.remainder(), &['l']);
1.31.0 · Source

pub fn rchunks_exact_mut(&mut self, chunk_size: usize) -> RChunksExactMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the end +of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last up to chunk_size-1 elements will be omitted and can be +retrieved from the into_remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of chunks_mut.

+

See rchunks_mut for a variant of this iterator that also returns the remainder as a +smaller chunk, and chunks_exact_mut for the same iterator but starting at the beginning +of the slice.

+

If your chunk_size is a constant, consider using as_rchunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.rchunks_exact_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[0, 2, 2, 1, 1]);
1.77.0 · Source

pub fn chunk_by<F>(&self, pred: F) -> ChunkBy<'_, T, F>
where + F: FnMut(&T, &T) -> bool,

Returns an iterator over the slice producing non-overlapping runs +of elements using the predicate to separate them.

+

The predicate is called for every pair of consecutive elements, +meaning that it is called on slice[0] and slice[1], +followed by slice[1] and slice[2], and so on.

+
§Examples
+
let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
+
+let mut iter = slice.chunk_by(|a, b| a == b);
+
+assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
+assert_eq!(iter.next(), Some(&[3, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
+assert_eq!(iter.next(), None);
+

This method can be used to extract the sorted subslices:

+ +
let slice = &[1, 1, 2, 3, 2, 3, 2, 3, 4];
+
+let mut iter = slice.chunk_by(|a, b| a <= b);
+
+assert_eq!(iter.next(), Some(&[1, 1, 2, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 3, 4][..]));
+assert_eq!(iter.next(), None);
1.77.0 · Source

pub fn chunk_by_mut<F>(&mut self, pred: F) -> ChunkByMut<'_, T, F>
where + F: FnMut(&T, &T) -> bool,

Returns an iterator over the slice producing non-overlapping mutable +runs of elements using the predicate to separate them.

+

The predicate is called for every pair of consecutive elements, +meaning that it is called on slice[0] and slice[1], +followed by slice[1] and slice[2], and so on.

+
§Examples
+
let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2];
+
+let mut iter = slice.chunk_by_mut(|a, b| a == b);
+
+assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
+assert_eq!(iter.next(), Some(&mut [3, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
+assert_eq!(iter.next(), None);
+

This method can be used to extract the sorted subslices:

+ +
let slice = &mut [1, 1, 2, 3, 2, 3, 2, 3, 4];
+
+let mut iter = slice.chunk_by_mut(|a, b| a <= b);
+
+assert_eq!(iter.next(), Some(&mut [1, 1, 2, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 3, 4][..]));
+assert_eq!(iter.next(), None);
1.0.0 · Source

pub fn split_at(&self, mid: usize) -> (&[T], &[T])

Divides one slice into two at an index.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+
§Panics
+

Panics if mid > len. For a non-panicking alternative see +split_at_checked.

+
§Examples
+
let v = ['a', 'b', 'c'];
+
+{
+   let (left, right) = v.split_at(0);
+   assert_eq!(left, []);
+   assert_eq!(right, ['a', 'b', 'c']);
+}
+
+{
+    let (left, right) = v.split_at(2);
+    assert_eq!(left, ['a', 'b']);
+    assert_eq!(right, ['c']);
+}
+
+{
+    let (left, right) = v.split_at(3);
+    assert_eq!(left, ['a', 'b', 'c']);
+    assert_eq!(right, []);
+}
1.0.0 · Source

pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T])

Divides one mutable slice into two at an index.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+
§Panics
+

Panics if mid > len. For a non-panicking alternative see +split_at_mut_checked.

+
§Examples
+
let mut v = [1, 0, 3, 0, 5, 6];
+let (left, right) = v.split_at_mut(2);
+assert_eq!(left, [1, 0]);
+assert_eq!(right, [3, 0, 5, 6]);
+left[1] = 2;
+right[1] = 4;
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
1.79.0 · Source

pub unsafe fn split_at_unchecked(&self, mid: usize) -> (&[T], &[T])

Divides one slice into two at an index, without doing bounds checking.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+

For a safe alternative see split_at.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used. The caller has to ensure that +0 <= mid <= self.len().

+
§Examples
+
let v = ['a', 'b', 'c'];
+
+unsafe {
+   let (left, right) = v.split_at_unchecked(0);
+   assert_eq!(left, []);
+   assert_eq!(right, ['a', 'b', 'c']);
+}
+
+unsafe {
+    let (left, right) = v.split_at_unchecked(2);
+    assert_eq!(left, ['a', 'b']);
+    assert_eq!(right, ['c']);
+}
+
+unsafe {
+    let (left, right) = v.split_at_unchecked(3);
+    assert_eq!(left, ['a', 'b', 'c']);
+    assert_eq!(right, []);
+}
1.79.0 · Source

pub unsafe fn split_at_mut_unchecked( + &mut self, + mid: usize, +) -> (&mut [T], &mut [T])

Divides one mutable slice into two at an index, without doing bounds checking.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+

For a safe alternative see split_at_mut.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used. The caller has to ensure that +0 <= mid <= self.len().

+
§Examples
+
let mut v = [1, 0, 3, 0, 5, 6];
+// scoped to restrict the lifetime of the borrows
+unsafe {
+    let (left, right) = v.split_at_mut_unchecked(2);
+    assert_eq!(left, [1, 0]);
+    assert_eq!(right, [3, 0, 5, 6]);
+    left[1] = 2;
+    right[1] = 4;
+}
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
1.80.0 · Source

pub fn split_at_checked(&self, mid: usize) -> Option<(&[T], &[T])>

Divides one slice into two at an index, returning None if the slice is +too short.

+

If mid ≤ len returns a pair of slices where the first will contain all +indices from [0, mid) (excluding the index mid itself) and the +second will contain all indices from [mid, len) (excluding the index +len itself).

+

Otherwise, if mid > len, returns None.

+
§Examples
+
let v = [1, -2, 3, -4, 5, -6];
+
+{
+   let (left, right) = v.split_at_checked(0).unwrap();
+   assert_eq!(left, []);
+   assert_eq!(right, [1, -2, 3, -4, 5, -6]);
+}
+
+{
+    let (left, right) = v.split_at_checked(2).unwrap();
+    assert_eq!(left, [1, -2]);
+    assert_eq!(right, [3, -4, 5, -6]);
+}
+
+{
+    let (left, right) = v.split_at_checked(6).unwrap();
+    assert_eq!(left, [1, -2, 3, -4, 5, -6]);
+    assert_eq!(right, []);
+}
+
+assert_eq!(None, v.split_at_checked(7));
1.80.0 · Source

pub fn split_at_mut_checked( + &mut self, + mid: usize, +) -> Option<(&mut [T], &mut [T])>

Divides one mutable slice into two at an index, returning None if the +slice is too short.

+

If mid ≤ len returns a pair of slices where the first will contain all +indices from [0, mid) (excluding the index mid itself) and the +second will contain all indices from [mid, len) (excluding the index +len itself).

+

Otherwise, if mid > len, returns None.

+
§Examples
+
let mut v = [1, 0, 3, 0, 5, 6];
+
+if let Some((left, right)) = v.split_at_mut_checked(2) {
+    assert_eq!(left, [1, 0]);
+    assert_eq!(right, [3, 0, 5, 6]);
+    left[1] = 2;
+    right[1] = 4;
+}
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
+
+assert_eq!(None, v.split_at_mut_checked(7));
1.0.0 · Source

pub fn split<F>(&self, pred: F) -> Split<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred. The matched element is not contained in the subslices.

+
§Examples
+
let slice = [10, 40, 33, 20];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
+

If the first element is matched, an empty slice will be the first item +returned by the iterator. Similarly, if the last element in the slice +is matched, an empty slice will be the last item returned by the +iterator:

+ +
let slice = [10, 40, 33];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40]);
+assert_eq!(iter.next().unwrap(), &[]);
+assert!(iter.next().is_none());
+

If two matched elements are directly adjacent, an empty slice will be +present between them:

+ +
let slice = [10, 6, 33, 20];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10]);
+assert_eq!(iter.next().unwrap(), &[]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
1.0.0 · Source

pub fn split_mut<F>(&mut self, pred: F) -> SplitMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that +match pred. The matched element is not contained in the subslices.

+
§Examples
+
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.split_mut(|num| *num % 3 == 0) {
+    group[0] = 1;
+}
+assert_eq!(v, [1, 40, 30, 1, 60, 1]);
1.51.0 · Source

pub fn split_inclusive<F>(&self, pred: F) -> SplitInclusive<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred. The matched element is contained in the end of the previous +subslice as a terminator.

+
§Examples
+
let slice = [10, 40, 33, 20];
+let mut iter = slice.split_inclusive(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40, 33]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
+

If the last element of the slice is matched, +that element will be considered the terminator of the preceding slice. +That slice will be the last item returned by the iterator.

+ +
let slice = [3, 10, 40, 33];
+let mut iter = slice.split_inclusive(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[3]);
+assert_eq!(iter.next().unwrap(), &[10, 40, 33]);
+assert!(iter.next().is_none());
1.51.0 · Source

pub fn split_inclusive_mut<F>(&mut self, pred: F) -> SplitInclusiveMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that +match pred. The matched element is contained in the previous +subslice as a terminator.

+
§Examples
+
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.split_inclusive_mut(|num| *num % 3 == 0) {
+    let terminator_idx = group.len()-1;
+    group[terminator_idx] = 1;
+}
+assert_eq!(v, [10, 40, 1, 20, 1, 1]);
1.27.0 · Source

pub fn rsplit<F>(&self, pred: F) -> RSplit<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred, starting at the end of the slice and working backwards. +The matched element is not contained in the subslices.

+
§Examples
+
let slice = [11, 22, 33, 0, 44, 55];
+let mut iter = slice.rsplit(|num| *num == 0);
+
+assert_eq!(iter.next().unwrap(), &[44, 55]);
+assert_eq!(iter.next().unwrap(), &[11, 22, 33]);
+assert_eq!(iter.next(), None);
+

As with split(), if the first or last element is matched, an empty +slice will be the first (or last) item returned by the iterator.

+ +
let v = &[0, 1, 1, 2, 3, 5, 8];
+let mut it = v.rsplit(|n| *n % 2 == 0);
+assert_eq!(it.next().unwrap(), &[]);
+assert_eq!(it.next().unwrap(), &[3, 5]);
+assert_eq!(it.next().unwrap(), &[1, 1]);
+assert_eq!(it.next().unwrap(), &[]);
+assert_eq!(it.next(), None);
1.27.0 · Source

pub fn rsplit_mut<F>(&mut self, pred: F) -> RSplitMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that +match pred, starting at the end of the slice and working +backwards. The matched element is not contained in the subslices.

+
§Examples
+
let mut v = [100, 400, 300, 200, 600, 500];
+
+let mut count = 0;
+for group in v.rsplit_mut(|num| *num % 3 == 0) {
+    count += 1;
+    group[0] = count;
+}
+assert_eq!(v, [3, 400, 300, 2, 600, 1]);
1.0.0 · Source

pub fn splitn<F>(&self, n: usize, pred: F) -> SplitN<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred, limited to returning at most n items. The matched element is +not contained in the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+

Print the slice split once by numbers divisible by 3 (i.e., [10, 40], +[20, 60, 50]):

+ +
let v = [10, 40, 30, 20, 60, 50];
+
+for group in v.splitn(2, |num| *num % 3 == 0) {
+    println!("{group:?}");
+}
1.0.0 · Source

pub fn splitn_mut<F>(&mut self, n: usize, pred: F) -> SplitNMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that match +pred, limited to returning at most n items. The matched element is +not contained in the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.splitn_mut(2, |num| *num % 3 == 0) {
+    group[0] = 1;
+}
+assert_eq!(v, [1, 40, 30, 1, 60, 50]);
1.0.0 · Source

pub fn rsplitn<F>(&self, n: usize, pred: F) -> RSplitN<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred limited to returning at most n items. This starts at the end of +the slice and works backwards. The matched element is not contained in +the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+

Print the slice split once, starting from the end, by numbers divisible +by 3 (i.e., [50], [10, 40, 30, 20]):

+ +
let v = [10, 40, 30, 20, 60, 50];
+
+for group in v.rsplitn(2, |num| *num % 3 == 0) {
+    println!("{group:?}");
+}
1.0.0 · Source

pub fn rsplitn_mut<F>(&mut self, n: usize, pred: F) -> RSplitNMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred limited to returning at most n items. This starts at the end of +the slice and works backwards. The matched element is not contained in +the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+
let mut s = [10, 40, 30, 20, 60, 50];
+
+for group in s.rsplitn_mut(2, |num| *num % 3 == 0) {
+    group[0] = 1;
+}
+assert_eq!(s, [1, 40, 30, 20, 60, 1]);
Source

pub fn split_once<F>(&self, pred: F) -> Option<(&[T], &[T])>
where + F: FnMut(&T) -> bool,

🔬This is a nightly-only experimental API. (slice_split_once)

Splits the slice on the first element that matches the specified +predicate.

+

If any matching elements are present in the slice, returns the prefix +before the match and suffix after. The matching element itself is not +included. If no elements match, returns None.

+
§Examples
+
#![feature(slice_split_once)]
+let s = [1, 2, 3, 2, 4];
+assert_eq!(s.split_once(|&x| x == 2), Some((
+    &[1][..],
+    &[3, 2, 4][..]
+)));
+assert_eq!(s.split_once(|&x| x == 0), None);
Source

pub fn rsplit_once<F>(&self, pred: F) -> Option<(&[T], &[T])>
where + F: FnMut(&T) -> bool,

🔬This is a nightly-only experimental API. (slice_split_once)

Splits the slice on the last element that matches the specified +predicate.

+

If any matching elements are present in the slice, returns the prefix +before the match and suffix after. The matching element itself is not +included. If no elements match, returns None.

+
§Examples
+
#![feature(slice_split_once)]
+let s = [1, 2, 3, 2, 4];
+assert_eq!(s.rsplit_once(|&x| x == 2), Some((
+    &[1, 2, 3][..],
+    &[4][..]
+)));
+assert_eq!(s.rsplit_once(|&x| x == 0), None);
1.0.0 · Source

pub fn contains(&self, x: &T) -> bool
where + T: PartialEq,

Returns true if the slice contains an element with the given value.

+

This operation is O(n).

+

Note that if you have a sorted slice, binary_search may be faster.

+
§Examples
+
let v = [10, 40, 30];
+assert!(v.contains(&30));
+assert!(!v.contains(&50));
+

If you do not have a &T, but some other value that you can compare +with one (for example, String implements PartialEq<str>), you can +use iter().any:

+ +
let v = [String::from("hello"), String::from("world")]; // slice of `String`
+assert!(v.iter().any(|e| e == "hello")); // search with `&str`
+assert!(!v.iter().any(|e| e == "hi"));
1.0.0 · Source

pub fn starts_with(&self, needle: &[T]) -> bool
where + T: PartialEq,

Returns true if needle is a prefix of the slice or equal to the slice.

+
§Examples
+
let v = [10, 40, 30];
+assert!(v.starts_with(&[10]));
+assert!(v.starts_with(&[10, 40]));
+assert!(v.starts_with(&v));
+assert!(!v.starts_with(&[50]));
+assert!(!v.starts_with(&[10, 50]));
+

Always returns true if needle is an empty slice:

+ +
let v = &[10, 40, 30];
+assert!(v.starts_with(&[]));
+let v: &[u8] = &[];
+assert!(v.starts_with(&[]));
1.0.0 · Source

pub fn ends_with(&self, needle: &[T]) -> bool
where + T: PartialEq,

Returns true if needle is a suffix of the slice or equal to the slice.

+
§Examples
+
let v = [10, 40, 30];
+assert!(v.ends_with(&[30]));
+assert!(v.ends_with(&[40, 30]));
+assert!(v.ends_with(&v));
+assert!(!v.ends_with(&[50]));
+assert!(!v.ends_with(&[50, 30]));
+

Always returns true if needle is an empty slice:

+ +
let v = &[10, 40, 30];
+assert!(v.ends_with(&[]));
+let v: &[u8] = &[];
+assert!(v.ends_with(&[]));
1.51.0 · Source

pub fn strip_prefix<P>(&self, prefix: &P) -> Option<&[T]>
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

Returns a subslice with the prefix removed.

+

If the slice starts with prefix, returns the subslice after the prefix, wrapped in Some. +If prefix is empty, simply returns the original slice. If prefix is equal to the +original slice, returns an empty slice.

+

If the slice does not start with prefix, returns None.

+
§Examples
+
let v = &[10, 40, 30];
+assert_eq!(v.strip_prefix(&[10]), Some(&[40, 30][..]));
+assert_eq!(v.strip_prefix(&[10, 40]), Some(&[30][..]));
+assert_eq!(v.strip_prefix(&[10, 40, 30]), Some(&[][..]));
+assert_eq!(v.strip_prefix(&[50]), None);
+assert_eq!(v.strip_prefix(&[10, 50]), None);
+
+let prefix : &str = "he";
+assert_eq!(b"hello".strip_prefix(prefix.as_bytes()),
+           Some(b"llo".as_ref()));
1.51.0 · Source

pub fn strip_suffix<P>(&self, suffix: &P) -> Option<&[T]>
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

Returns a subslice with the suffix removed.

+

If the slice ends with suffix, returns the subslice before the suffix, wrapped in Some. +If suffix is empty, simply returns the original slice. If suffix is equal to the +original slice, returns an empty slice.

+

If the slice does not end with suffix, returns None.

+
§Examples
+
let v = &[10, 40, 30];
+assert_eq!(v.strip_suffix(&[30]), Some(&[10, 40][..]));
+assert_eq!(v.strip_suffix(&[40, 30]), Some(&[10][..]));
+assert_eq!(v.strip_suffix(&[10, 40, 30]), Some(&[][..]));
+assert_eq!(v.strip_suffix(&[50]), None);
+assert_eq!(v.strip_suffix(&[50, 30]), None);
Source

pub fn trim_prefix<P>(&self, prefix: &P) -> &[T]
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

🔬This is a nightly-only experimental API. (trim_prefix_suffix)

Returns a subslice with the optional prefix removed.

+

If the slice starts with prefix, returns the subslice after the prefix. If prefix +is empty or the slice does not start with prefix, simply returns the original slice. +If prefix is equal to the original slice, returns an empty slice.

+
§Examples
+
#![feature(trim_prefix_suffix)]
+
+let v = &[10, 40, 30];
+
+// Prefix present - removes it
+assert_eq!(v.trim_prefix(&[10]), &[40, 30][..]);
+assert_eq!(v.trim_prefix(&[10, 40]), &[30][..]);
+assert_eq!(v.trim_prefix(&[10, 40, 30]), &[][..]);
+
+// Prefix absent - returns original slice
+assert_eq!(v.trim_prefix(&[50]), &[10, 40, 30][..]);
+assert_eq!(v.trim_prefix(&[10, 50]), &[10, 40, 30][..]);
+
+let prefix : &str = "he";
+assert_eq!(b"hello".trim_prefix(prefix.as_bytes()), b"llo".as_ref());
Source

pub fn trim_suffix<P>(&self, suffix: &P) -> &[T]
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

🔬This is a nightly-only experimental API. (trim_prefix_suffix)

Returns a subslice with the optional suffix removed.

+

If the slice ends with suffix, returns the subslice before the suffix. If suffix +is empty or the slice does not end with suffix, simply returns the original slice. +If suffix is equal to the original slice, returns an empty slice.

+
§Examples
+
#![feature(trim_prefix_suffix)]
+
+let v = &[10, 40, 30];
+
+// Suffix present - removes it
+assert_eq!(v.trim_suffix(&[30]), &[10, 40][..]);
+assert_eq!(v.trim_suffix(&[40, 30]), &[10][..]);
+assert_eq!(v.trim_suffix(&[10, 40, 30]), &[][..]);
+
+// Suffix absent - returns original slice
+assert_eq!(v.trim_suffix(&[50]), &[10, 40, 30][..]);
+assert_eq!(v.trim_suffix(&[50, 30]), &[10, 40, 30][..]);

Binary searches this slice for a given element. +If the slice is not sorted, the returned result is unspecified and +meaningless.

+

If the value is found then Result::Ok is returned, containing the +index of the matching element. If there are multiple matches, then any +one of the matches could be returned. The index is chosen +deterministically, but is subject to change in future versions of Rust. +If the value is not found then Result::Err is returned, containing +the index where a matching element could be inserted while maintaining +sorted order.

+

See also binary_search_by, binary_search_by_key, and partition_point.

+
§Examples
+

Looks up a series of four elements. The first is found, with a +uniquely determined position; the second and third are not +found; the fourth could match any position in [1, 4].

+ +
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+assert_eq!(s.binary_search(&13),  Ok(9));
+assert_eq!(s.binary_search(&4),   Err(7));
+assert_eq!(s.binary_search(&100), Err(13));
+let r = s.binary_search(&1);
+assert!(match r { Ok(1..=4) => true, _ => false, });
+

If you want to find that whole range of matching items, rather than +an arbitrary matching one, that can be done using partition_point:

+ +
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+let low = s.partition_point(|x| x < &1);
+assert_eq!(low, 1);
+let high = s.partition_point(|x| x <= &1);
+assert_eq!(high, 5);
+let r = s.binary_search(&1);
+assert!((low..high).contains(&r.unwrap()));
+
+assert!(s[..low].iter().all(|&x| x < 1));
+assert!(s[low..high].iter().all(|&x| x == 1));
+assert!(s[high..].iter().all(|&x| x > 1));
+
+// For something not found, the "range" of equal items is empty
+assert_eq!(s.partition_point(|x| x < &11), 9);
+assert_eq!(s.partition_point(|x| x <= &11), 9);
+assert_eq!(s.binary_search(&11), Err(9));
+

If you want to insert an item to a sorted vector, while maintaining +sort order, consider using partition_point:

+ +
let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+let num = 42;
+let idx = s.partition_point(|&x| x <= num);
+// If `num` is unique, `s.partition_point(|&x| x < num)` (with `<`) is equivalent to
+// `s.binary_search(&num).unwrap_or_else(|x| x)`, but using `<=` will allow `insert`
+// to shift less elements.
+s.insert(idx, num);
+assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
1.0.0 · Source

pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result<usize, usize>
where + F: FnMut(&'a T) -> Ordering,

Binary searches this slice with a comparator function.

+

The comparator function should return an order code that indicates +whether its argument is Less, Equal or Greater the desired +target. +If the slice is not sorted or if the comparator function does not +implement an order consistent with the sort order of the underlying +slice, the returned result is unspecified and meaningless.

+

If the value is found then Result::Ok is returned, containing the +index of the matching element. If there are multiple matches, then any +one of the matches could be returned. The index is chosen +deterministically, but is subject to change in future versions of Rust. +If the value is not found then Result::Err is returned, containing +the index where a matching element could be inserted while maintaining +sorted order.

+

See also binary_search, binary_search_by_key, and partition_point.

+
§Examples
+

Looks up a series of four elements. The first is found, with a +uniquely determined position; the second and third are not +found; the fourth could match any position in [1, 4].

+ +
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+let seek = 13;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Ok(9));
+let seek = 4;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(7));
+let seek = 100;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(13));
+let seek = 1;
+let r = s.binary_search_by(|probe| probe.cmp(&seek));
+assert!(match r { Ok(1..=4) => true, _ => false, });
1.10.0 · Source

pub fn binary_search_by_key<'a, B, F>( + &'a self, + b: &B, + f: F, +) -> Result<usize, usize>
where + F: FnMut(&'a T) -> B, + B: Ord,

Binary searches this slice with a key extraction function.

+

Assumes that the slice is sorted by the key, for instance with +sort_by_key using the same key extraction function. +If the slice is not sorted by the key, the returned result is +unspecified and meaningless.

+

If the value is found then Result::Ok is returned, containing the +index of the matching element. If there are multiple matches, then any +one of the matches could be returned. The index is chosen +deterministically, but is subject to change in future versions of Rust. +If the value is not found then Result::Err is returned, containing +the index where a matching element could be inserted while maintaining +sorted order.

+

See also binary_search, binary_search_by, and partition_point.

+
§Examples
+

Looks up a series of four elements in a slice of pairs sorted by +their second elements. The first is found, with a uniquely +determined position; the second and third are not found; the +fourth could match any position in [1, 4].

+ +
let s = [(0, 0), (2, 1), (4, 1), (5, 1), (3, 1),
+         (1, 2), (2, 3), (4, 5), (5, 8), (3, 13),
+         (1, 21), (2, 34), (4, 55)];
+
+assert_eq!(s.binary_search_by_key(&13, |&(a, b)| b),  Ok(9));
+assert_eq!(s.binary_search_by_key(&4, |&(a, b)| b),   Err(7));
+assert_eq!(s.binary_search_by_key(&100, |&(a, b)| b), Err(13));
+let r = s.binary_search_by_key(&1, |&(a, b)| b);
+assert!(match r { Ok(1..=4) => true, _ => false, });
1.20.0 · Source

pub fn sort_unstable(&mut self)
where + T: Ord,

Sorts the slice in ascending order without preserving the initial order of equal elements.

+

This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not +allocate), and O(n * log(n)) worst-case.

+

If the implementation of Ord for T does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+

All original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. Same is true if the implementation of Ord for T panics.

+

Sorting types that only implement PartialOrd such as f32 and f64 require +additional precautions. For example, f32::NAN != f32::NAN, which doesn’t fulfill the +reflexivity requirement of Ord. By using an alternative comparison function with +slice::sort_unstable_by such as f32::total_cmp or f64::total_cmp that defines a +total order users can sort slices containing floating-point values. Alternatively, if all +values in the slice are guaranteed to be in a subset for which PartialOrd::partial_cmp +forms a total order, it’s possible to sort the slice with sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()).

+
§Current implementation
+

The current implementation is based on ipnsort by Lukas Bergdoll and Orson Peters, which +combines the fast average case of quicksort with the fast worst case of heapsort, achieving +linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the +expected time to sort the data is O(n * log(k)).

+

It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice is partially sorted.

+
§Panics
+

May panic if the implementation of Ord for T does not implement a total order, or if +the Ord implementation panics.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+
+v.sort_unstable();
+assert_eq!(v, [-5, -3, 1, 2, 4]);
1.20.0 · Source

pub fn sort_unstable_by<F>(&mut self, compare: F)
where + F: FnMut(&T, &T) -> Ordering,

Sorts the slice in ascending order with a comparison function, without preserving the +initial order of equal elements.

+

This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not +allocate), and O(n * log(n)) worst-case.

+

If the comparison function compare does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+

All original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. Same is true if compare panics.

+
§Current implementation
+

The current implementation is based on ipnsort by Lukas Bergdoll and Orson Peters, which +combines the fast average case of quicksort with the fast worst case of heapsort, achieving +linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the +expected time to sort the data is O(n * log(k)).

+

It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice is partially sorted.

+
§Panics
+

May panic if the compare does not implement a total order, or if +the compare itself panics.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+v.sort_unstable_by(|a, b| a.cmp(b));
+assert_eq!(v, [-5, -3, 1, 2, 4]);
+
+// reverse sorting
+v.sort_unstable_by(|a, b| b.cmp(a));
+assert_eq!(v, [4, 2, 1, -3, -5]);
1.20.0 · Source

pub fn sort_unstable_by_key<K, F>(&mut self, f: F)
where + F: FnMut(&T) -> K, + K: Ord,

Sorts the slice in ascending order with a key extraction function, without preserving +the initial order of equal elements.

+

This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not +allocate), and O(n * log(n)) worst-case.

+

If the implementation of Ord for K does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+

All original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. Same is true if the implementation of Ord for K panics.

+
§Current implementation
+

The current implementation is based on ipnsort by Lukas Bergdoll and Orson Peters, which +combines the fast average case of quicksort with the fast worst case of heapsort, achieving +linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the +expected time to sort the data is O(n * log(k)).

+

It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice is partially sorted.

+
§Panics
+

May panic if the implementation of Ord for K does not implement a total order, or if +the Ord implementation panics.

+
§Examples
+
let mut v = [4i32, -5, 1, -3, 2];
+
+v.sort_unstable_by_key(|k| k.abs());
+assert_eq!(v, [1, 2, -3, 4, -5]);
1.49.0 · Source

pub fn select_nth_unstable( + &mut self, + index: usize, +) -> (&mut [T], &mut T, &mut [T])
where + T: Ord,

Reorders the slice such that the element at index is at a sort-order position. All +elements before index will be <= to this value, and all elements after will be >= to +it.

+

This reordering is unstable (i.e. any element that compares equal to the nth element may end +up at that position), in-place (i.e. does not allocate), and runs in O(n) time. This +function is also known as “kth element” in other libraries.

+

Returns a triple that partitions the reordered slice:

+
    +
  • +

    The unsorted subslice before index, whose elements all satisfy x <= self[index].

    +
  • +
  • +

    The element at index.

    +
  • +
  • +

    The unsorted subslice after index, whose elements all satisfy x >= self[index].

    +
  • +
+
§Current implementation
+

The current algorithm is an introselect implementation based on ipnsort by Lukas Bergdoll +and Orson Peters, which is also the basis for sort_unstable. The fallback algorithm is +Median of Medians using Tukey’s Ninther for pivot selection, which guarantees linear runtime +for all inputs.

+
§Panics
+

Panics when index >= len(), and so always panics on empty slices.

+

May panic if the implementation of Ord for T does not implement a total order.

+
§Examples
+
let mut v = [-5i32, 4, 2, -3, 1];
+
+// Find the items `<=` to the median, the median itself, and the items `>=` to it.
+let (lesser, median, greater) = v.select_nth_unstable(2);
+
+assert!(lesser == [-3, -5] || lesser == [-5, -3]);
+assert_eq!(median, &mut 1);
+assert!(greater == [4, 2] || greater == [2, 4]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [-3, -5, 1, 2, 4] ||
+        v == [-5, -3, 1, 2, 4] ||
+        v == [-3, -5, 1, 4, 2] ||
+        v == [-5, -3, 1, 4, 2]);
1.49.0 · Source

pub fn select_nth_unstable_by<F>( + &mut self, + index: usize, + compare: F, +) -> (&mut [T], &mut T, &mut [T])
where + F: FnMut(&T, &T) -> Ordering,

Reorders the slice with a comparator function such that the element at index is at a +sort-order position. All elements before index will be <= to this value, and all +elements after will be >= to it, according to the comparator function.

+

This reordering is unstable (i.e. any element that compares equal to the nth element may end +up at that position), in-place (i.e. does not allocate), and runs in O(n) time. This +function is also known as “kth element” in other libraries.

+

Returns a triple partitioning the reordered slice:

+
    +
  • +

    The unsorted subslice before index, whose elements all satisfy +compare(x, self[index]).is_le().

    +
  • +
  • +

    The element at index.

    +
  • +
  • +

    The unsorted subslice after index, whose elements all satisfy +compare(x, self[index]).is_ge().

    +
  • +
+
§Current implementation
+

The current algorithm is an introselect implementation based on ipnsort by Lukas Bergdoll +and Orson Peters, which is also the basis for sort_unstable. The fallback algorithm is +Median of Medians using Tukey’s Ninther for pivot selection, which guarantees linear runtime +for all inputs.

+
§Panics
+

Panics when index >= len(), and so always panics on empty slices.

+

May panic if compare does not implement a total order.

+
§Examples
+
let mut v = [-5i32, 4, 2, -3, 1];
+
+// Find the items `>=` to the median, the median itself, and the items `<=` to it, by using
+// a reversed comparator.
+let (before, median, after) = v.select_nth_unstable_by(2, |a, b| b.cmp(a));
+
+assert!(before == [4, 2] || before == [2, 4]);
+assert_eq!(median, &mut 1);
+assert!(after == [-3, -5] || after == [-5, -3]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [2, 4, 1, -5, -3] ||
+        v == [2, 4, 1, -3, -5] ||
+        v == [4, 2, 1, -5, -3] ||
+        v == [4, 2, 1, -3, -5]);
1.49.0 · Source

pub fn select_nth_unstable_by_key<K, F>( + &mut self, + index: usize, + f: F, +) -> (&mut [T], &mut T, &mut [T])
where + F: FnMut(&T) -> K, + K: Ord,

Reorders the slice with a key extraction function such that the element at index is at a +sort-order position. All elements before index will have keys <= to the key at index, +and all elements after will have keys >= to it.

+

This reordering is unstable (i.e. any element that compares equal to the nth element may end +up at that position), in-place (i.e. does not allocate), and runs in O(n) time. This +function is also known as “kth element” in other libraries.

+

Returns a triple partitioning the reordered slice:

+
    +
  • +

    The unsorted subslice before index, whose elements all satisfy f(x) <= f(self[index]).

    +
  • +
  • +

    The element at index.

    +
  • +
  • +

    The unsorted subslice after index, whose elements all satisfy f(x) >= f(self[index]).

    +
  • +
+
§Current implementation
+

The current algorithm is an introselect implementation based on ipnsort by Lukas Bergdoll +and Orson Peters, which is also the basis for sort_unstable. The fallback algorithm is +Median of Medians using Tukey’s Ninther for pivot selection, which guarantees linear runtime +for all inputs.

+
§Panics
+

Panics when index >= len(), meaning it always panics on empty slices.

+

May panic if K: Ord does not implement a total order.

+
§Examples
+
let mut v = [-5i32, 4, 1, -3, 2];
+
+// Find the items `<=` to the absolute median, the absolute median itself, and the items
+// `>=` to it.
+let (lesser, median, greater) = v.select_nth_unstable_by_key(2, |a| a.abs());
+
+assert!(lesser == [1, 2] || lesser == [2, 1]);
+assert_eq!(median, &mut -3);
+assert!(greater == [4, -5] || greater == [-5, 4]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [1, 2, -3, 4, -5] ||
+        v == [1, 2, -3, -5, 4] ||
+        v == [2, 1, -3, 4, -5] ||
+        v == [2, 1, -3, -5, 4]);
Source

pub fn partition_dedup(&mut self) -> (&mut [T], &mut [T])
where + T: PartialEq,

🔬This is a nightly-only experimental API. (slice_partition_dedup)

Moves all consecutive repeated elements to the end of the slice according to the +PartialEq trait implementation.

+

Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.

+

If the slice is sorted, the first returned slice contains no duplicates.

+
§Examples
+
#![feature(slice_partition_dedup)]
+
+let mut slice = [1, 2, 2, 3, 3, 2, 1, 1];
+
+let (dedup, duplicates) = slice.partition_dedup();
+
+assert_eq!(dedup, [1, 2, 3, 2, 1]);
+assert_eq!(duplicates, [2, 3, 1]);
Source

pub fn partition_dedup_by<F>(&mut self, same_bucket: F) -> (&mut [T], &mut [T])
where + F: FnMut(&mut T, &mut T) -> bool,

🔬This is a nightly-only experimental API. (slice_partition_dedup)

Moves all but the first of consecutive elements to the end of the slice satisfying +a given equality relation.

+

Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.

+

The same_bucket function is passed references to two elements from the slice and +must determine if the elements compare equal. The elements are passed in opposite order +from their order in the slice, so if same_bucket(a, b) returns true, a is moved +at the end of the slice.

+

If the slice is sorted, the first returned slice contains no duplicates.

+
§Examples
+
#![feature(slice_partition_dedup)]
+
+let mut slice = ["foo", "Foo", "BAZ", "Bar", "bar", "baz", "BAZ"];
+
+let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.eq_ignore_ascii_case(b));
+
+assert_eq!(dedup, ["foo", "BAZ", "Bar", "baz"]);
+assert_eq!(duplicates, ["bar", "Foo", "BAZ"]);
Source

pub fn partition_dedup_by_key<K, F>(&mut self, key: F) -> (&mut [T], &mut [T])
where + F: FnMut(&mut T) -> K, + K: PartialEq,

🔬This is a nightly-only experimental API. (slice_partition_dedup)

Moves all but the first of consecutive elements to the end of the slice that resolve +to the same key.

+

Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.

+

If the slice is sorted, the first returned slice contains no duplicates.

+
§Examples
+
#![feature(slice_partition_dedup)]
+
+let mut slice = [10, 20, 21, 30, 30, 20, 11, 13];
+
+let (dedup, duplicates) = slice.partition_dedup_by_key(|i| *i / 10);
+
+assert_eq!(dedup, [10, 20, 30, 20, 11]);
+assert_eq!(duplicates, [21, 30, 13]);
1.26.0 · Source

pub fn rotate_left(&mut self, mid: usize)

Rotates the slice in-place such that the first mid elements of the +slice move to the end while the last self.len() - mid elements move to +the front.

+

After calling rotate_left, the element previously at index mid will +become the first element in the slice.

+
§Panics
+

This function will panic if mid is greater than the length of the +slice. Note that mid == self.len() does not panic and is a no-op +rotation.

+
§Complexity
+

Takes linear (in self.len()) time.

+
§Examples
+
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a.rotate_left(2);
+assert_eq!(a, ['c', 'd', 'e', 'f', 'a', 'b']);
+

Rotating a subslice:

+ +
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a[1..5].rotate_left(1);
+assert_eq!(a, ['a', 'c', 'd', 'e', 'b', 'f']);
1.26.0 · Source

pub fn rotate_right(&mut self, k: usize)

Rotates the slice in-place such that the first self.len() - k +elements of the slice move to the end while the last k elements move +to the front.

+

After calling rotate_right, the element previously at index +self.len() - k will become the first element in the slice.

+
§Panics
+

This function will panic if k is greater than the length of the +slice. Note that k == self.len() does not panic and is a no-op +rotation.

+
§Complexity
+

Takes linear (in self.len()) time.

+
§Examples
+
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a.rotate_right(2);
+assert_eq!(a, ['e', 'f', 'a', 'b', 'c', 'd']);
+

Rotating a subslice:

+ +
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a[1..5].rotate_right(1);
+assert_eq!(a, ['a', 'e', 'b', 'c', 'd', 'f']);
1.50.0 · Source

pub fn fill(&mut self, value: T)
where + T: Clone,

Fills self with elements by cloning value.

+
§Examples
+
let mut buf = vec![0; 10];
+buf.fill(1);
+assert_eq!(buf, vec![1; 10]);
1.51.0 · Source

pub fn fill_with<F>(&mut self, f: F)
where + F: FnMut() -> T,

Fills self with elements returned by calling a closure repeatedly.

+

This method uses a closure to create new values. If you’d rather +Clone a given value, use fill. If you want to use the Default +trait to generate values, you can pass Default::default as the +argument.

+
§Examples
+
let mut buf = vec![1; 10];
+buf.fill_with(Default::default);
+assert_eq!(buf, vec![0; 10]);
1.7.0 · Source

pub fn clone_from_slice(&mut self, src: &[T])
where + T: Clone,

Copies the elements from src into self.

+

The length of src must be the same as self.

+
§Panics
+

This function will panic if the two slices have different lengths.

+
§Examples
+

Cloning two elements from a slice into another:

+ +
let src = [1, 2, 3, 4];
+let mut dst = [0, 0];
+
+// Because the slices have to be the same length,
+// we slice the source slice from four elements
+// to two. It will panic if we don't do this.
+dst.clone_from_slice(&src[2..]);
+
+assert_eq!(src, [1, 2, 3, 4]);
+assert_eq!(dst, [3, 4]);
+

Rust enforces that there can only be one mutable reference with no +immutable references to a particular piece of data in a particular +scope. Because of this, attempting to use clone_from_slice on a +single slice will result in a compile failure:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+slice[..2].clone_from_slice(&slice[3..]); // compile fail!
+

To work around this, we can use split_at_mut to create two distinct +sub-slices from a slice:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+{
+    let (left, right) = slice.split_at_mut(2);
+    left.clone_from_slice(&right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 4, 5]);
1.9.0 · Source

pub fn copy_from_slice(&mut self, src: &[T])
where + T: Copy,

Copies all elements from src into self, using a memcpy.

+

The length of src must be the same as self.

+

If T does not implement Copy, use clone_from_slice.

+
§Panics
+

This function will panic if the two slices have different lengths.

+
§Examples
+

Copying two elements from a slice into another:

+ +
let src = [1, 2, 3, 4];
+let mut dst = [0, 0];
+
+// Because the slices have to be the same length,
+// we slice the source slice from four elements
+// to two. It will panic if we don't do this.
+dst.copy_from_slice(&src[2..]);
+
+assert_eq!(src, [1, 2, 3, 4]);
+assert_eq!(dst, [3, 4]);
+

Rust enforces that there can only be one mutable reference with no +immutable references to a particular piece of data in a particular +scope. Because of this, attempting to use copy_from_slice on a +single slice will result in a compile failure:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+slice[..2].copy_from_slice(&slice[3..]); // compile fail!
+

To work around this, we can use split_at_mut to create two distinct +sub-slices from a slice:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+{
+    let (left, right) = slice.split_at_mut(2);
+    left.copy_from_slice(&right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 4, 5]);
1.37.0 · Source

pub fn copy_within<R>(&mut self, src: R, dest: usize)
where + R: RangeBounds<usize>, + T: Copy,

Copies elements from one part of the slice to another part of itself, +using a memmove.

+

src is the range within self to copy from. dest is the starting +index of the range within self to copy to, which will have the same +length as src. The two ranges may overlap. The ends of the two ranges +must be less than or equal to self.len().

+
§Panics
+

This function will panic if either range exceeds the end of the slice, +or if the end of src is before the start.

+
§Examples
+

Copying four bytes within a slice:

+ +
let mut bytes = *b"Hello, World!";
+
+bytes.copy_within(1..5, 8);
+
+assert_eq!(&bytes, b"Hello, Wello!");
1.27.0 · Source

pub fn swap_with_slice(&mut self, other: &mut [T])

Swaps all elements in self with those in other.

+

The length of other must be the same as self.

+
§Panics
+

This function will panic if the two slices have different lengths.

+
§Example
+

Swapping two elements across slices:

+ +
let mut slice1 = [0, 0];
+let mut slice2 = [1, 2, 3, 4];
+
+slice1.swap_with_slice(&mut slice2[2..]);
+
+assert_eq!(slice1, [3, 4]);
+assert_eq!(slice2, [1, 2, 0, 0]);
+

Rust enforces that there can only be one mutable reference to a +particular piece of data in a particular scope. Because of this, +attempting to use swap_with_slice on a single slice will result in +a compile failure:

+ +
let mut slice = [1, 2, 3, 4, 5];
+slice[..2].swap_with_slice(&mut slice[3..]); // compile fail!
+

To work around this, we can use split_at_mut to create two distinct +mutable sub-slices from a slice:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+{
+    let (left, right) = slice.split_at_mut(2);
+    left.swap_with_slice(&mut right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 1, 2]);
1.30.0 · Source

pub unsafe fn align_to<U>(&self) -> (&[T], &[U], &[T])

Transmutes the slice to a slice of another type, ensuring alignment of the types is +maintained.

+

This method splits the slice into three distinct slices: prefix, correctly aligned middle +slice of a new type, and the suffix slice. The middle part will be as big as possible under +the given alignment constraint and element size.

+

This method has no purpose when either input element T or output element U are +zero-sized and will return the original slice without splitting anything.

+
§Safety
+

This method is essentially a transmute with respect to the elements in the returned +middle slice, so all the usual caveats pertaining to transmute::<T, U> also apply here.

+
§Examples
+

Basic usage:

+ +
unsafe {
+    let bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
+    let (prefix, shorts, suffix) = bytes.align_to::<u16>();
+    // less_efficient_algorithm_for_bytes(prefix);
+    // more_efficient_algorithm_for_aligned_shorts(shorts);
+    // less_efficient_algorithm_for_bytes(suffix);
+}
1.30.0 · Source

pub unsafe fn align_to_mut<U>(&mut self) -> (&mut [T], &mut [U], &mut [T])

Transmutes the mutable slice to a mutable slice of another type, ensuring alignment of the +types is maintained.

+

This method splits the slice into three distinct slices: prefix, correctly aligned middle +slice of a new type, and the suffix slice. The middle part will be as big as possible under +the given alignment constraint and element size.

+

This method has no purpose when either input element T or output element U are +zero-sized and will return the original slice without splitting anything.

+
§Safety
+

This method is essentially a transmute with respect to the elements in the returned +middle slice, so all the usual caveats pertaining to transmute::<T, U> also apply here.

+
§Examples
+

Basic usage:

+ +
unsafe {
+    let mut bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
+    let (prefix, shorts, suffix) = bytes.align_to_mut::<u16>();
+    // less_efficient_algorithm_for_bytes(prefix);
+    // more_efficient_algorithm_for_aligned_shorts(shorts);
+    // less_efficient_algorithm_for_bytes(suffix);
+}
Source

pub fn as_simd<const LANES: usize>(&self) -> (&[T], &[Simd<T, LANES>], &[T])
where + Simd<T, LANES>: AsRef<[T; LANES]>, + T: SimdElement, + LaneCount<LANES>: SupportedLaneCount,

🔬This is a nightly-only experimental API. (portable_simd)

Splits a slice into a prefix, a middle of aligned SIMD types, and a suffix.

+

This is a safe wrapper around slice::align_to, so inherits the same +guarantees as that method.

+
§Panics
+

This will panic if the size of the SIMD type is different from +LANES times that of the scalar.

+

At the time of writing, the trait restrictions on Simd<T, LANES> keeps +that from ever happening, as only power-of-two numbers of lanes are +supported. It’s possible that, in the future, those restrictions might +be lifted in a way that would make it possible to see panics from this +method for something like LANES == 3.

+
§Examples
+
#![feature(portable_simd)]
+use core::simd::prelude::*;
+
+let short = &[1, 2, 3];
+let (prefix, middle, suffix) = short.as_simd::<4>();
+assert_eq!(middle, []); // Not enough elements for anything in the middle
+
+// They might be split in any possible way between prefix and suffix
+let it = prefix.iter().chain(suffix).copied();
+assert_eq!(it.collect::<Vec<_>>(), vec![1, 2, 3]);
+
+fn basic_simd_sum(x: &[f32]) -> f32 {
+    use std::ops::Add;
+    let (prefix, middle, suffix) = x.as_simd();
+    let sums = f32x4::from_array([
+        prefix.iter().copied().sum(),
+        0.0,
+        0.0,
+        suffix.iter().copied().sum(),
+    ]);
+    let sums = middle.iter().copied().fold(sums, f32x4::add);
+    sums.reduce_sum()
+}
+
+let numbers: Vec<f32> = (1..101).map(|x| x as _).collect();
+assert_eq!(basic_simd_sum(&numbers[1..99]), 4949.0);
Source

pub fn as_simd_mut<const LANES: usize>( + &mut self, +) -> (&mut [T], &mut [Simd<T, LANES>], &mut [T])
where + Simd<T, LANES>: AsMut<[T; LANES]>, + T: SimdElement, + LaneCount<LANES>: SupportedLaneCount,

🔬This is a nightly-only experimental API. (portable_simd)

Splits a mutable slice into a mutable prefix, a middle of aligned SIMD types, +and a mutable suffix.

+

This is a safe wrapper around slice::align_to_mut, so inherits the same +guarantees as that method.

+

This is the mutable version of slice::as_simd; see that for examples.

+
§Panics
+

This will panic if the size of the SIMD type is different from +LANES times that of the scalar.

+

At the time of writing, the trait restrictions on Simd<T, LANES> keeps +that from ever happening, as only power-of-two numbers of lanes are +supported. It’s possible that, in the future, those restrictions might +be lifted in a way that would make it possible to see panics from this +method for something like LANES == 3.

+
1.82.0 · Source

pub fn is_sorted(&self) -> bool
where + T: PartialOrd,

Checks if the elements of this slice are sorted.

+

That is, for each element a and its following element b, a <= b must hold. If the +slice yields exactly zero or one element, true is returned.

+

Note that if Self::Item is only PartialOrd, but not Ord, the above definition +implies that this function returns false if any two consecutive items are not +comparable.

+
§Examples
+
let empty: [i32; 0] = [];
+
+assert!([1, 2, 2, 9].is_sorted());
+assert!(![1, 3, 2, 4].is_sorted());
+assert!([0].is_sorted());
+assert!(empty.is_sorted());
+assert!(![0.0, 1.0, f32::NAN].is_sorted());
1.82.0 · Source

pub fn is_sorted_by<'a, F>(&'a self, compare: F) -> bool
where + F: FnMut(&'a T, &'a T) -> bool,

Checks if the elements of this slice are sorted using the given comparator function.

+

Instead of using PartialOrd::partial_cmp, this function uses the given compare +function to determine whether two elements are to be considered in sorted order.

+
§Examples
+
assert!([1, 2, 2, 9].is_sorted_by(|a, b| a <= b));
+assert!(![1, 2, 2, 9].is_sorted_by(|a, b| a < b));
+
+assert!([0].is_sorted_by(|a, b| true));
+assert!([0].is_sorted_by(|a, b| false));
+
+let empty: [i32; 0] = [];
+assert!(empty.is_sorted_by(|a, b| false));
+assert!(empty.is_sorted_by(|a, b| true));
1.82.0 · Source

pub fn is_sorted_by_key<'a, F, K>(&'a self, f: F) -> bool
where + F: FnMut(&'a T) -> K, + K: PartialOrd,

Checks if the elements of this slice are sorted using the given key extraction function.

+

Instead of comparing the slice’s elements directly, this function compares the keys of the +elements, as determined by f. Apart from that, it’s equivalent to is_sorted; see its +documentation for more information.

+
§Examples
+
assert!(["c", "bb", "aaa"].is_sorted_by_key(|s| s.len()));
+assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs()));
1.52.0 · Source

pub fn partition_point<P>(&self, pred: P) -> usize
where + P: FnMut(&T) -> bool,

Returns the index of the partition point according to the given predicate +(the index of the first element of the second partition).

+

The slice is assumed to be partitioned according to the given predicate. +This means that all elements for which the predicate returns true are at the start of the slice +and all elements for which the predicate returns false are at the end. +For example, [7, 15, 3, 5, 4, 12, 6] is partitioned under the predicate x % 2 != 0 +(all odd numbers are at the start, all even at the end).

+

If this slice is not partitioned, the returned result is unspecified and meaningless, +as this method performs a kind of binary search.

+

See also binary_search, binary_search_by, and binary_search_by_key.

+
§Examples
+
let v = [1, 2, 3, 3, 5, 6, 7];
+let i = v.partition_point(|&x| x < 5);
+
+assert_eq!(i, 4);
+assert!(v[..i].iter().all(|&x| x < 5));
+assert!(v[i..].iter().all(|&x| !(x < 5)));
+

If all elements of the slice match the predicate, including if the slice +is empty, then the length of the slice will be returned:

+ +
let a = [2, 4, 8];
+assert_eq!(a.partition_point(|x| x < &100), a.len());
+let a: [i32; 0] = [];
+assert_eq!(a.partition_point(|x| x < &100), 0);
+

If you want to insert an item to a sorted vector, while maintaining +sort order:

+ +
let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+let num = 42;
+let idx = s.partition_point(|&x| x <= num);
+s.insert(idx, num);
+assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
1.87.0 · Source

pub fn split_off<'a, R>(self: &mut &'a [T], range: R) -> Option<&'a [T]>
where + R: OneSidedRange<usize>,

Removes the subslice corresponding to the given range +and returns a reference to it.

+

Returns None and does not modify the slice if the given +range is out of bounds.

+

Note that this method only accepts one-sided ranges such as +2.. or ..6, but not 2..6.

+
§Examples
+

Splitting off the first three elements of a slice:

+ +
let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+let mut first_three = slice.split_off(..3).unwrap();
+
+assert_eq!(slice, &['d']);
+assert_eq!(first_three, &['a', 'b', 'c']);
+

Splitting off a slice starting with the third element:

+ +
let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+let mut tail = slice.split_off(2..).unwrap();
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(tail, &['c', 'd']);
+

Getting None when range is out of bounds:

+ +
let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+
+assert_eq!(None, slice.split_off(5..));
+assert_eq!(None, slice.split_off(..5));
+assert_eq!(None, slice.split_off(..=4));
+let expected: &[char] = &['a', 'b', 'c', 'd'];
+assert_eq!(Some(expected), slice.split_off(..4));
1.87.0 · Source

pub fn split_off_mut<'a, R>( + self: &mut &'a mut [T], + range: R, +) -> Option<&'a mut [T]>
where + R: OneSidedRange<usize>,

Removes the subslice corresponding to the given range +and returns a mutable reference to it.

+

Returns None and does not modify the slice if the given +range is out of bounds.

+

Note that this method only accepts one-sided ranges such as +2.. or ..6, but not 2..6.

+
§Examples
+

Splitting off the first three elements of a slice:

+ +
let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+let mut first_three = slice.split_off_mut(..3).unwrap();
+
+assert_eq!(slice, &mut ['d']);
+assert_eq!(first_three, &mut ['a', 'b', 'c']);
+

Splitting off a slice starting with the third element:

+ +
let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+let mut tail = slice.split_off_mut(2..).unwrap();
+
+assert_eq!(slice, &mut ['a', 'b']);
+assert_eq!(tail, &mut ['c', 'd']);
+

Getting None when range is out of bounds:

+ +
let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+
+assert_eq!(None, slice.split_off_mut(5..));
+assert_eq!(None, slice.split_off_mut(..5));
+assert_eq!(None, slice.split_off_mut(..=4));
+let expected: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+assert_eq!(Some(expected), slice.split_off_mut(..4));
1.87.0 · Source

pub fn split_off_first<'a>(self: &mut &'a [T]) -> Option<&'a T>

Removes the first element of the slice and returns a reference +to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &[_] = &['a', 'b', 'c'];
+let first = slice.split_off_first().unwrap();
+
+assert_eq!(slice, &['b', 'c']);
+assert_eq!(first, &'a');
1.87.0 · Source

pub fn split_off_first_mut<'a>(self: &mut &'a mut [T]) -> Option<&'a mut T>

Removes the first element of the slice and returns a mutable +reference to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
+let first = slice.split_off_first_mut().unwrap();
+*first = 'd';
+
+assert_eq!(slice, &['b', 'c']);
+assert_eq!(first, &'d');
1.87.0 · Source

pub fn split_off_last<'a>(self: &mut &'a [T]) -> Option<&'a T>

Removes the last element of the slice and returns a reference +to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &[_] = &['a', 'b', 'c'];
+let last = slice.split_off_last().unwrap();
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(last, &'c');
1.87.0 · Source

pub fn split_off_last_mut<'a>(self: &mut &'a mut [T]) -> Option<&'a mut T>

Removes the last element of the slice and returns a mutable +reference to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
+let last = slice.split_off_last_mut().unwrap();
+*last = 'd';
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(last, &'d');
1.86.0 · Source

pub unsafe fn get_disjoint_unchecked_mut<I, const N: usize>( + &mut self, + indices: [I; N], +) -> [&mut <I as SliceIndex<[T]>>::Output; N]

Returns mutable references to many indices at once, without doing any checks.

+

An index can be either a usize, a Range or a RangeInclusive. Note +that this method takes an array, so all indices must be of the same type. +If passed an array of usizes this method gives back an array of mutable references +to single elements, while if passed an array of ranges it gives back an array of +mutable references to slices.

+

For a safe alternative see get_disjoint_mut.

+
§Safety
+

Calling this method with overlapping or out-of-bounds indices is undefined behavior +even if the resulting references are not used.

+
§Examples
+
let x = &mut [1, 2, 4];
+
+unsafe {
+    let [a, b] = x.get_disjoint_unchecked_mut([0, 2]);
+    *a *= 10;
+    *b *= 100;
+}
+assert_eq!(x, &[10, 2, 400]);
+
+unsafe {
+    let [a, b] = x.get_disjoint_unchecked_mut([0..1, 1..3]);
+    a[0] = 8;
+    b[0] = 88;
+    b[1] = 888;
+}
+assert_eq!(x, &[8, 88, 888]);
+
+unsafe {
+    let [a, b] = x.get_disjoint_unchecked_mut([1..=2, 0..=0]);
+    a[0] = 11;
+    a[1] = 111;
+    b[0] = 1;
+}
+assert_eq!(x, &[1, 11, 111]);
1.86.0 · Source

pub fn get_disjoint_mut<I, const N: usize>( + &mut self, + indices: [I; N], +) -> Result<[&mut <I as SliceIndex<[T]>>::Output; N], GetDisjointMutError>

Returns mutable references to many indices at once.

+

An index can be either a usize, a Range or a RangeInclusive. Note +that this method takes an array, so all indices must be of the same type. +If passed an array of usizes this method gives back an array of mutable references +to single elements, while if passed an array of ranges it gives back an array of +mutable references to slices.

+

Returns an error if any index is out-of-bounds, or if there are overlapping indices. +An empty range is not considered to overlap if it is located at the beginning or at +the end of another range, but is considered to overlap if it is located in the middle.

+

This method does a O(n^2) check to check that there are no overlapping indices, so be careful +when passing many indices.

+
§Examples
+
let v = &mut [1, 2, 3];
+if let Ok([a, b]) = v.get_disjoint_mut([0, 2]) {
+    *a = 413;
+    *b = 612;
+}
+assert_eq!(v, &[413, 2, 612]);
+
+if let Ok([a, b]) = v.get_disjoint_mut([0..1, 1..3]) {
+    a[0] = 8;
+    b[0] = 88;
+    b[1] = 888;
+}
+assert_eq!(v, &[8, 88, 888]);
+
+if let Ok([a, b]) = v.get_disjoint_mut([1..=2, 0..=0]) {
+    a[0] = 11;
+    a[1] = 111;
+    b[0] = 1;
+}
+assert_eq!(v, &[1, 11, 111]);
Source

pub fn element_offset(&self, element: &T) -> Option<usize>

🔬This is a nightly-only experimental API. (substr_range)

Returns the index that an element reference points to.

+

Returns None if element does not point to the start of an element within the slice.

+

This method is useful for extending slice iterators like slice::split.

+

Note that this uses pointer arithmetic and does not compare elements. +To find the index of an element via comparison, use +.iter().position() instead.

+
§Panics
+

Panics if T is zero-sized.

+
§Examples
+

Basic usage:

+ +
#![feature(substr_range)]
+
+let nums: &[u32] = &[1, 7, 1, 1];
+let num = &nums[2];
+
+assert_eq!(num, &1);
+assert_eq!(nums.element_offset(num), Some(2));
+

Returning None with an unaligned element:

+ +
#![feature(substr_range)]
+
+let arr: &[[u32; 2]] = &[[0, 1], [2, 3]];
+let flat_arr: &[u32] = arr.as_flattened();
+
+let ok_elm: &[u32; 2] = flat_arr[0..2].try_into().unwrap();
+let weird_elm: &[u32; 2] = flat_arr[1..3].try_into().unwrap();
+
+assert_eq!(ok_elm, &[0, 1]);
+assert_eq!(weird_elm, &[1, 2]);
+
+assert_eq!(arr.element_offset(ok_elm), Some(0)); // Points to element 0
+assert_eq!(arr.element_offset(weird_elm), None); // Points between element 0 and 1
Source

pub fn subslice_range(&self, subslice: &[T]) -> Option<Range<usize>>

🔬This is a nightly-only experimental API. (substr_range)

Returns the range of indices that a subslice points to.

+

Returns None if subslice does not point within the slice or if it is not aligned with the +elements in the slice.

+

This method does not compare elements. Instead, this method finds the location in the slice that +subslice was obtained from. To find the index of a subslice via comparison, instead use +.windows().position().

+

This method is useful for extending slice iterators like slice::split.

+

Note that this may return a false positive (either Some(0..0) or Some(self.len()..self.len())) +if subslice has a length of zero and points to the beginning or end of another, separate, slice.

+
§Panics
+

Panics if T is zero-sized.

+
§Examples
+

Basic usage:

+ +
#![feature(substr_range)]
+
+let nums = &[0, 5, 10, 0, 0, 5];
+
+let mut iter = nums
+    .split(|t| *t == 0)
+    .map(|n| nums.subslice_range(n).unwrap());
+
+assert_eq!(iter.next(), Some(0..0));
+assert_eq!(iter.next(), Some(1..3));
+assert_eq!(iter.next(), Some(4..4));
+assert_eq!(iter.next(), Some(5..6));
1.79.0 · Source

pub fn utf8_chunks(&self) -> Utf8Chunks<'_>

Creates an iterator over the contiguous valid UTF-8 ranges of this +slice, and the non-UTF-8 fragments in between.

+

See the Utf8Chunk type for documentation of the items yielded by this iterator.

+
§Examples
+

This function formats arbitrary but mostly-UTF-8 bytes into Rust source +code in the form of a C-string literal (c"...").

+ +
use std::fmt::Write as _;
+
+pub fn cstr_literal(bytes: &[u8]) -> String {
+    let mut repr = String::new();
+    repr.push_str("c\"");
+    for chunk in bytes.utf8_chunks() {
+        for ch in chunk.valid().chars() {
+            // Escapes \0, \t, \r, \n, \\, \', \", and uses \u{...} for non-printable characters.
+            write!(repr, "{}", ch.escape_debug()).unwrap();
+        }
+        for byte in chunk.invalid() {
+            write!(repr, "\\x{:02X}", byte).unwrap();
+        }
+    }
+    repr.push('"');
+    repr
+}
+
+fn main() {
+    let lit = cstr_literal(b"\xferris the \xf0\x9f\xa6\x80\x07");
+    let expected = stringify!(c"\xFErris the 🦀\u{7}");
+    assert_eq!(lit, expected);
+}
1.0.0 · Source

pub fn sort(&mut self)
where + T: Ord,

Sorts the slice in ascending order, preserving initial order of equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(n * log(n)) +worst-case.

+

If the implementation of Ord for T does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

When applicable, unstable sorting is preferred because it is generally faster than stable +sorting and it doesn’t allocate auxiliary memory. See +sort_unstable. The exception are partially sorted slices, which +may be better served with slice::sort.

+

Sorting types that only implement PartialOrd such as f32 and f64 require +additional precautions. For example, f32::NAN != f32::NAN, which doesn’t fulfill the +reflexivity requirement of Ord. By using an alternative comparison function with +slice::sort_by such as f32::total_cmp or f64::total_cmp that defines a total +order users can sort slices containing floating-point values. Alternatively, if all values +in the slice are guaranteed to be in a subset for which PartialOrd::partial_cmp forms a +total order, it’s possible to sort the slice with sort_by(|a, b| a.partial_cmp(b).unwrap()).

+
§Current implementation
+

The current implementation is based on driftsort by Orson Peters and Lukas Bergdoll, which +combines the fast average case of quicksort with the fast worst case and partial run +detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs +with k distinct elements, the expected time to sort the data is O(n * log(k)).

+

The auxiliary memory allocation behavior depends on the input length. Short slices are +handled without allocation, medium sized slices allocate self.len() and beyond that it +clamps at self.len() / 2.

+
§Panics
+

May panic if the implementation of Ord for T does not implement a total order, or if +the Ord implementation itself panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+
+v.sort();
+assert_eq!(v, [-5, -3, 1, 2, 4]);
1.0.0 · Source

pub fn sort_by<F>(&mut self, compare: F)
where + F: FnMut(&T, &T) -> Ordering,

Sorts the slice in ascending order with a comparison function, preserving initial order of +equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(n * log(n)) +worst-case.

+

If the comparison function compare does not implement a total order, the function may +panic; even if the function exits normally, the resulting order of elements in the slice is +unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+
§Current implementation
+

The current implementation is based on driftsort by Orson Peters and Lukas Bergdoll, which +combines the fast average case of quicksort with the fast worst case and partial run +detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs +with k distinct elements, the expected time to sort the data is O(n * log(k)).

+

The auxiliary memory allocation behavior depends on the input length. Short slices are +handled without allocation, medium sized slices allocate self.len() and beyond that it +clamps at self.len() / 2.

+
§Panics
+

May panic if compare does not implement a total order, or if compare itself panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+v.sort_by(|a, b| a.cmp(b));
+assert_eq!(v, [-5, -3, 1, 2, 4]);
+
+// reverse sorting
+v.sort_by(|a, b| b.cmp(a));
+assert_eq!(v, [4, 2, 1, -3, -5]);
1.7.0 · Source

pub fn sort_by_key<K, F>(&mut self, f: F)
where + F: FnMut(&T) -> K, + K: Ord,

Sorts the slice in ascending order with a key extraction function, preserving initial order +of equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(m * n * log(n)) +worst-case, where the key function is O(m).

+

If the implementation of Ord for K does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+
§Current implementation
+

The current implementation is based on driftsort by Orson Peters and Lukas Bergdoll, which +combines the fast average case of quicksort with the fast worst case and partial run +detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs +with k distinct elements, the expected time to sort the data is O(n * log(k)).

+

The auxiliary memory allocation behavior depends on the input length. Short slices are +handled without allocation, medium sized slices allocate self.len() and beyond that it +clamps at self.len() / 2.

+
§Panics
+

May panic if the implementation of Ord for K does not implement a total order, or if +the Ord implementation or the key-function f panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4i32, -5, 1, -3, 2];
+
+v.sort_by_key(|k| k.abs());
+assert_eq!(v, [1, 2, -3, 4, -5]);
1.34.0 · Source

pub fn sort_by_cached_key<K, F>(&mut self, f: F)
where + F: FnMut(&T) -> K, + K: Ord,

Sorts the slice in ascending order with a key extraction function, preserving initial order +of equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(m * n + n * +log(n)) worst-case, where the key function is O(m).

+

During sorting, the key function is called at most once per element, by using temporary +storage to remember the results of key evaluation. The order of calls to the key function is +unspecified and may change in future versions of the standard library.

+

If the implementation of Ord for K does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For simple key functions (e.g., functions that are property accesses or basic operations), +sort_by_key is likely to be faster.

+
§Current implementation
+

The current implementation is based on instruction-parallel-network sort by Lukas +Bergdoll, which combines the fast average case of randomized quicksort with the fast worst +case of heapsort, while achieving linear time on fully sorted and reversed inputs. And +O(k * log(n)) where k is the number of distinct elements in the input. It leverages +superscalar out-of-order execution capabilities commonly found in CPUs, to efficiently +perform the operation.

+

In the worst case, the algorithm allocates temporary storage in a Vec<(K, usize)> the +length of the slice.

+
§Panics
+

May panic if the implementation of Ord for K does not implement a total order, or if +the Ord implementation panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4i32, -5, 1, -3, 2, 10];
+
+// Strings are sorted by lexicographical order.
+v.sort_by_cached_key(|k| k.to_string());
+assert_eq!(v, [-3, -5, 1, 10, 2, 4]);
1.0.0 · Source

pub fn to_vec(&self) -> Vec<T>
where + T: Clone,

Copies self into a new Vec.

+
§Examples
+
let s = [10, 40, 30];
+let x = s.to_vec();
+// Here, `s` and `x` can be modified independently.
Source

pub fn to_vec_in<A>(&self, alloc: A) -> Vec<T, A>
where + A: Allocator, + T: Clone,

🔬This is a nightly-only experimental API. (allocator_api)

Copies self into a new Vec with an allocator.

+
§Examples
+
#![feature(allocator_api)]
+
+use std::alloc::System;
+
+let s = [10, 40, 30];
+let x = s.to_vec_in(System);
+// Here, `s` and `x` can be modified independently.
1.40.0 · Source

pub fn repeat(&self, n: usize) -> Vec<T>
where + T: Copy,

Creates a vector by copying a slice n times.

+
§Panics
+

This function will panic if the capacity would overflow.

+
§Examples
+
assert_eq!([1, 2].repeat(3), vec![1, 2, 1, 2, 1, 2]);
+

A panic upon overflow:

+ +
// this will panic at runtime
+b"0123456789abcdef".repeat(usize::MAX);
1.0.0 · Source

pub fn concat<Item>(&self) -> <[T] as Concat<Item>>::Output
where + [T]: Concat<Item>, + Item: ?Sized,

Flattens a slice of T into a single value Self::Output.

+
§Examples
+
assert_eq!(["hello", "world"].concat(), "helloworld");
+assert_eq!([[1, 2], [3, 4]].concat(), [1, 2, 3, 4]);
1.3.0 · Source

pub fn join<Separator>( + &self, + sep: Separator, +) -> <[T] as Join<Separator>>::Output
where + [T]: Join<Separator>,

Flattens a slice of T into a single value Self::Output, placing a +given separator between each.

+
§Examples
+
assert_eq!(["hello", "world"].join(" "), "hello world");
+assert_eq!([[1, 2], [3, 4]].join(&0), [1, 2, 0, 3, 4]);
+assert_eq!([[1, 2], [3, 4]].join(&[0, 0][..]), [1, 2, 0, 0, 3, 4]);
1.0.0 · Source

pub fn connect<Separator>( + &self, + sep: Separator, +) -> <[T] as Join<Separator>>::Output
where + [T]: Join<Separator>,

👎Deprecated since 1.3.0: renamed to join

Flattens a slice of T into a single value Self::Output, placing a +given separator between each.

+
§Examples
+
assert_eq!(["hello", "world"].connect(" "), "hello world");
+assert_eq!([[1, 2], [3, 4]].connect(&0), [1, 2, 0, 3, 4]);
1.23.0 · Source

pub fn to_ascii_uppercase(&self) -> Vec<u8>

Returns a vector containing a copy of this slice where each byte +is mapped to its ASCII upper case equivalent.

+

ASCII letters ‘a’ to ‘z’ are mapped to ‘A’ to ‘Z’, +but non-ASCII letters are unchanged.

+

To uppercase the value in-place, use make_ascii_uppercase.

+
1.23.0 · Source

pub fn to_ascii_lowercase(&self) -> Vec<u8>

Returns a vector containing a copy of this slice where each byte +is mapped to its ASCII lower case equivalent.

+

ASCII letters ‘A’ to ‘Z’ are mapped to ‘a’ to ‘z’, +but non-ASCII letters are unchanged.

+

To lowercase the value in-place, use make_ascii_lowercase.

+

Trait Implementations§

Source§

impl Clone for ConnectionId

Source§

fn clone(&self) -> ConnectionId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Deref for ConnectionId

Source§

type Target = [u8]

The resulting type after dereferencing.
Source§

fn deref(&self) -> &[u8]

Dereferences the value.
Source§

impl DerefMut for ConnectionId

Source§

fn deref_mut(&mut self) -> &mut [u8]

Mutably dereferences the value.
Source§

impl Display for ConnectionId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Hash for ConnectionId

Source§

fn hash<__H>(&self, state: &mut __H)
where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for ConnectionId

Source§

fn cmp(&self, other: &ConnectionId) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for ConnectionId

Source§

fn eq(&self, other: &ConnectionId) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for ConnectionId

Source§

fn partial_cmp(&self, other: &ConnectionId) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for ConnectionId

Source§

impl Eq for ConnectionId

Source§

impl StructuralPartialEq for ConnectionId

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<P, T> Receiver for P
where + P: Deref<Target = T> + ?Sized, + T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.ConnectionStats.html b/pr/395/docs/iroh_quinn/struct.ConnectionStats.html new file mode 100644 index 000000000..5d8c9f040 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.ConnectionStats.html @@ -0,0 +1,36 @@ +ConnectionStats in iroh_quinn - Rust

ConnectionStats

Struct ConnectionStats 

Source
#[non_exhaustive]
pub struct ConnectionStats { + pub udp_tx: UdpStats, + pub udp_rx: UdpStats, + pub frame_tx: FrameStats, + pub frame_rx: FrameStats, +}
Expand description

Connection statistics

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§udp_tx: UdpStats

Statistics about UDP datagrams transmitted on a connection

+
§udp_rx: UdpStats

Statistics about UDP datagrams received on a connection

+
§frame_tx: FrameStats

Statistics about frames transmitted on a connection

+
§frame_rx: FrameStats

Statistics about frames received on a connection

+

Trait Implementations§

Source§

impl Clone for ConnectionStats

Source§

fn clone(&self) -> ConnectionStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for ConnectionStats

Source§

fn default() -> ConnectionStats

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.Endpoint.html b/pr/395/docs/iroh_quinn/struct.Endpoint.html new file mode 100644 index 000000000..403216a87 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.Endpoint.html @@ -0,0 +1,103 @@ +Endpoint in iroh_quinn - Rust

Endpoint

Struct Endpoint 

Source
pub struct Endpoint { /* private fields */ }
Expand description

A QUIC endpoint.

+

An endpoint corresponds to a single UDP socket, may host many connections, and may act as both +client and server for different connections.

+

May be cloned to obtain another handle to the same endpoint.

+

Implementations§

Source§

impl Endpoint

Source

pub fn client(addr: SocketAddr) -> Result<Self>

Helper to construct an endpoint for use with outgoing connections only

+

Note that addr is the local address to bind to, which should usually be a wildcard +address like 0.0.0.0:0 or [::]:0, which allow communication with any reachable IPv4 or +IPv6 address respectively from an OS-assigned port.

+

If an IPv6 address is provided, attempts to make the socket dual-stack so as to allow +communication with both IPv4 and IPv6 addresses. As such, calling Endpoint::client with +the address [::]:0 is a reasonable default to maximize the ability to connect to other +address. For example:

+ +
iroh_quinn::Endpoint::client((std::net::Ipv6Addr::UNSPECIFIED, 0).into());
+

Some environments may not allow creation of dual-stack sockets, in which case an IPv6 +client will only be able to connect to IPv6 servers. An IPv4 client is never dual-stack.

+
Source

pub fn stats(&self) -> EndpointStats

Returns relevant stats from this Endpoint

+
Source

pub fn server(config: ServerConfig, addr: SocketAddr) -> Result<Self>

Helper to construct an endpoint for use with both incoming and outgoing connections

+

Platform defaults for dual-stack sockets vary. For example, any socket bound to a wildcard +IPv6 address on Windows will not by default be able to communicate with IPv4 +addresses. Portable applications should bind an address that matches the family they wish to +communicate within.

+
Source

pub fn new( + config: EndpointConfig, + server_config: Option<ServerConfig>, + socket: UdpSocket, + runtime: Arc<dyn Runtime>, +) -> Result<Self>

Construct an endpoint with arbitrary configuration and socket

+
Source

pub fn new_with_abstract_socket( + config: EndpointConfig, + server_config: Option<ServerConfig>, + socket: Box<dyn AsyncUdpSocket>, + runtime: Arc<dyn Runtime>, +) -> Result<Self>

Construct an endpoint with arbitrary configuration and pre-constructed abstract socket

+

Useful when socket has additional state (e.g. sidechannels) attached for which shared +ownership is needed.

+
Source

pub fn accept(&self) -> Accept<'_>

Get the next incoming connection attempt from a client

+

Yields Incomings, or None if the endpoint is closed. Incoming +can be awaited to obtain the final Connection, or used to e.g. +filter connection attempts or force address validation, or converted into an intermediate +Connecting future which can be used to e.g. send 0.5-RTT data.

+
Source

pub fn set_default_client_config(&self, config: ClientConfig)

Set the client configuration used by connect

+
Source

pub fn connect( + &self, + addr: SocketAddr, + server_name: &str, +) -> Result<Connecting, ConnectError>

Connect to a remote endpoint

+

server_name must be covered by the certificate presented by the server. This prevents a +connection from being intercepted by an attacker with a valid certificate for some other +server.

+

May fail immediately due to configuration errors, or in the future if the connection could +not be established.

+
Source

pub fn connect_with( + &self, + config: ClientConfig, + addr: SocketAddr, + server_name: &str, +) -> Result<Connecting, ConnectError>

Connect to a remote endpoint using a custom configuration.

+

See connect() for details.

+
Source

pub fn rebind(&self, socket: UdpSocket) -> Result<()>

Switch to a new UDP socket

+

See Endpoint::rebind_abstract() for details.

+
Source

pub fn rebind_abstract(&self, socket: Box<dyn AsyncUdpSocket>) -> Result<()>

Switch to a new UDP socket

+

Allows the endpoint’s address to be updated live, affecting all active connections. Incoming +connections and connections to servers unreachable from the new address will be lost.

+

On error, the old UDP socket is retained.

+
Source

pub fn set_server_config(&self, server_config: Option<ServerConfig>)

Replace the server configuration, affecting new incoming connections only

+

Useful for e.g. refreshing TLS certificates without disrupting existing connections.

+
Source

pub fn local_addr(&self) -> Result<SocketAddr>

Get the local SocketAddr the underlying socket is bound to

+
Source

pub fn open_connections(&self) -> usize

Get the number of connections that are currently open

+
Source

pub fn close(&self, error_code: VarInt, reason: &[u8])

Close all of this endpoint’s connections immediately and cease accepting new connections.

+

See Connection::close() for details.

+
Source

pub async fn wait_idle(&self)

Wait for all connections on the endpoint to be cleanly shut down

+

Waiting for this condition before exiting ensures that a good-faith effort is made to notify +peers of recent connection closes, whereas exiting immediately could force them to wait out +the idle timeout period.

+

Does not proactively close existing connections or cause incoming connections to be +rejected. Consider calling close() if that is desired.

+

Trait Implementations§

Source§

impl Clone for Endpoint

Source§

fn clone(&self) -> Endpoint

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Endpoint

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.EndpointConfig.html b/pr/395/docs/iroh_quinn/struct.EndpointConfig.html new file mode 100644 index 000000000..a79e13637 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.EndpointConfig.html @@ -0,0 +1,67 @@ +EndpointConfig in iroh_quinn - Rust

EndpointConfig

Struct EndpointConfig 

Source
pub struct EndpointConfig { /* private fields */ }
Expand description

Global configuration for the endpoint, affecting all connections

+

Default values should be suitable for most internet applications.

+

Implementations§

Source§

impl EndpointConfig

Source

pub fn new(reset_key: Arc<dyn HmacKey>) -> EndpointConfig

Create a default config with a particular reset_key

+
Source

pub fn cid_generator<F>(&mut self, factory: F) -> &mut EndpointConfig
where + F: Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync + 'static,

Supply a custom connection ID generator factory

+

Called once by each Endpoint constructed from this configuration to obtain the CID +generator which will be used to generate the CIDs used for incoming packets on all +connections involving that Endpoint. A custom CID generator allows applications to embed +information in local connection IDs, e.g. to support stateless packet-level load balancers.

+

Defaults to HashedConnectionIdGenerator.

+
Source

pub fn reset_key(&mut self, key: Arc<dyn HmacKey>) -> &mut EndpointConfig

Private key used to send authenticated connection resets to peers who were +communicating with a previous instance of this endpoint.

+
Source

pub fn max_udp_payload_size( + &mut self, + value: u16, +) -> Result<&mut EndpointConfig, ConfigError>

Maximum UDP payload size accepted from peers (excluding UDP and IP overhead).

+

Must be greater or equal than 1200.

+

Defaults to 1472, which is the largest UDP payload that can be transmitted in the typical +1500 byte Ethernet MTU. Deployments on links with larger MTUs (e.g. loopback or Ethernet +with jumbo frames) can raise this to improve performance at the cost of a linear increase in +datagram receive buffer size.

+
Source

pub fn get_max_udp_payload_size(&self) -> u64

Get the current value of max_udp_payload_size

+
Source

pub fn supported_versions( + &mut self, + supported_versions: Vec<u32>, +) -> &mut EndpointConfig

Override supported QUIC versions

+
Source

pub fn grease_quic_bit(&mut self, value: bool) -> &mut EndpointConfig

Whether to accept QUIC packets containing any value for the fixed bit

+

Enabled by default. Helps protect against protocol ossification and makes traffic less +identifiable to observers. Disable if helping observers identify this traffic as QUIC is +desired.

+
Source

pub fn min_reset_interval(&mut self, value: Duration) -> &mut EndpointConfig

Minimum interval between outgoing stateless reset packets

+

Defaults to 20ms. Limits the impact of attacks which flood an endpoint with garbage packets, +e.g. ISAKMP/IKE amplification. Larger values provide a stronger defense, but may delay +detection of some error conditions by clients. Using a ConnectionIdGenerator with a low +rate of false positives in validate reduces the risk +incurred by a small minimum reset interval.

+
Source

pub fn rng_seed(&mut self, seed: Option<[u8; 32]>) -> &mut EndpointConfig

Optional seed to be used internally for random number generation

+

By default, quinn will initialize an endpoint’s rng using a platform entropy source. +However, you can seed the rng yourself through this method (e.g. if you need to run quinn +deterministically or if you are using quinn in an environment that doesn’t have a source of +entropy available).

+

Trait Implementations§

Source§

impl Clone for EndpointConfig

Source§

fn clone(&self) -> EndpointConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EndpointConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for EndpointConfig

Source§

fn default() -> EndpointConfig

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.EndpointStats.html b/pr/395/docs/iroh_quinn/struct.EndpointStats.html new file mode 100644 index 000000000..19310ee19 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.EndpointStats.html @@ -0,0 +1,36 @@ +EndpointStats in iroh_quinn - Rust

EndpointStats

Struct EndpointStats 

Source
#[non_exhaustive]
pub struct EndpointStats { + pub accepted_handshakes: u64, + pub outgoing_handshakes: u64, + pub refused_handshakes: u64, + pub ignored_handshakes: u64, +}
Expand description

Statistics on Endpoint activity

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§accepted_handshakes: u64

Cumulative number of Quic handshakes accepted by this Endpoint

+
§outgoing_handshakes: u64

Cumulative number of Quic handshakes sent from this Endpoint

+
§refused_handshakes: u64

Cumulative number of Quic handshakes refused on this Endpoint

+
§ignored_handshakes: u64

Cumulative number of Quic handshakes ignored on this Endpoint

+

Trait Implementations§

Source§

impl Clone for EndpointStats

Source§

fn clone(&self) -> EndpointStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EndpointStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for EndpointStats

Source§

fn default() -> EndpointStats

Returns the “default value” for a type. Read more
Source§

impl Copy for EndpointStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.FrameStats.html b/pr/395/docs/iroh_quinn/struct.FrameStats.html new file mode 100644 index 000000000..a2980fad6 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.FrameStats.html @@ -0,0 +1,65 @@ +FrameStats in iroh_quinn - Rust

FrameStats

Struct FrameStats 

Source
#[non_exhaustive]
pub struct FrameStats {
Show 37 fields + pub acks: u64, + pub path_acks: u64, + pub ack_frequency: u64, + pub crypto: u64, + pub connection_close: u64, + pub data_blocked: u64, + pub datagram: u64, + pub handshake_done: u8, + pub immediate_ack: u64, + pub max_data: u64, + pub max_stream_data: u64, + pub max_streams_bidi: u64, + pub max_streams_uni: u64, + pub new_connection_id: u64, + pub path_new_connection_id: u64, + pub new_token: u64, + pub path_challenge: u64, + pub path_response: u64, + pub ping: u64, + pub reset_stream: u64, + pub retire_connection_id: u64, + pub path_retire_connection_id: u64, + pub stream_data_blocked: u64, + pub streams_blocked_bidi: u64, + pub streams_blocked_uni: u64, + pub stop_sending: u64, + pub stream: u64, + pub observed_addr: u64, + pub path_abandon: u64, + pub path_status_available: u64, + pub path_status_backup: u64, + pub max_path_id: u64, + pub paths_blocked: u64, + pub path_cids_blocked: u64, + pub add_address: u64, + pub reach_out: u64, + pub remove_address: u64, +
}
Expand description

Number of frames transmitted or received of each frame type

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§acks: u64§path_acks: u64§ack_frequency: u64§crypto: u64§connection_close: u64§data_blocked: u64§datagram: u64§handshake_done: u8§immediate_ack: u64§max_data: u64§max_stream_data: u64§max_streams_bidi: u64§max_streams_uni: u64§new_connection_id: u64§path_new_connection_id: u64§new_token: u64§path_challenge: u64§path_response: u64§ping: u64§reset_stream: u64§retire_connection_id: u64§path_retire_connection_id: u64§stream_data_blocked: u64§streams_blocked_bidi: u64§streams_blocked_uni: u64§stop_sending: u64§stream: u64§observed_addr: u64§path_abandon: u64§path_status_available: u64§path_status_backup: u64§max_path_id: u64§paths_blocked: u64§path_cids_blocked: u64§add_address: u64§reach_out: u64§remove_address: u64

Trait Implementations§

Source§

impl Clone for FrameStats

Source§

fn clone(&self) -> FrameStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FrameStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for FrameStats

Source§

fn default() -> FrameStats

Returns the “default value” for a type. Read more
Source§

impl Copy for FrameStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.IdleTimeout.html b/pr/395/docs/iroh_quinn/struct.IdleTimeout.html new file mode 100644 index 000000000..3036a6878 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.IdleTimeout.html @@ -0,0 +1,52 @@ +IdleTimeout in iroh_quinn - Rust

IdleTimeout

Struct IdleTimeout 

Source
pub struct IdleTimeout(/* private fields */);
Expand description

Maximum duration of inactivity to accept before timing out the connection

+

This wraps an underlying VarInt, representing the duration in milliseconds. Values can be +constructed by converting directly from VarInt, or using TryFrom<Duration>.

+ +
// A `VarInt`-encoded value in milliseconds
+let timeout = IdleTimeout::from(VarInt::from_u32(10_000));
+
+// Try to convert a `Duration` into a `VarInt`-encoded timeout
+let timeout = IdleTimeout::try_from(Duration::from_secs(10))?;

Trait Implementations§

Source§

impl Clone for IdleTimeout

Source§

fn clone(&self) -> IdleTimeout

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for IdleTimeout

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for IdleTimeout

Source§

fn default() -> IdleTimeout

Returns the “default value” for a type. Read more
Source§

impl From<VarInt> for IdleTimeout

Source§

fn from(inner: VarInt) -> IdleTimeout

Converts to this type from the input type.
Source§

impl Hash for IdleTimeout

Source§

fn hash<__H>(&self, state: &mut __H)
where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for IdleTimeout

Source§

fn cmp(&self, other: &IdleTimeout) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for IdleTimeout

Source§

fn eq(&self, other: &IdleTimeout) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for IdleTimeout

Source§

fn partial_cmp(&self, other: &IdleTimeout) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl TryFrom<Duration> for IdleTimeout

Source§

type Error = VarIntBoundsExceeded

The type returned in the event of a conversion error.
Source§

fn try_from( + timeout: Duration, +) -> Result<IdleTimeout, <IdleTimeout as TryFrom<Duration>>::Error>

Performs the conversion.
Source§

impl Copy for IdleTimeout

Source§

impl Eq for IdleTimeout

Source§

impl StructuralPartialEq for IdleTimeout

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.Incoming.html b/pr/395/docs/iroh_quinn/struct.Incoming.html new file mode 100644 index 000000000..2d7afbe0b --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.Incoming.html @@ -0,0 +1,46 @@ +Incoming in iroh_quinn - Rust

Incoming

Struct Incoming 

Source
pub struct Incoming(/* private fields */);
Expand description

An incoming connection for which the server has not yet begun its part of the handshake

+

Implementations§

Source§

impl Incoming

Source

pub fn accept(self) -> Result<Connecting, ConnectionError>

Attempt to accept this incoming connection (an error may still occur)

+
Source

pub fn accept_with( + self, + server_config: Arc<ServerConfig>, +) -> Result<Connecting, ConnectionError>

Accept this incoming connection using a custom configuration

+

See accept() for more details.

+
Source

pub fn refuse(self)

Reject this incoming connection attempt

+
Source

pub fn retry(self) -> Result<(), RetryError>

Respond with a retry packet, requiring the client to retry with address validation

+

Errors if may_retry() is false.

+
Source

pub fn ignore(self)

Ignore this incoming connection attempt, not sending any packet in response

+
Source

pub fn local_ip(&self) -> Option<IpAddr>

The local IP address which was used when the peer established the connection

+
Source

pub fn remote_address(&self) -> SocketAddr

The peer’s UDP address

+
Source

pub fn remote_address_validated(&self) -> bool

Whether the socket address that is initiating this connection has been validated

+

This means that the sender of the initial packet has proved that they can receive traffic +sent to self.remote_address().

+

If self.remote_address_validated() is false, self.may_retry() is guaranteed to be true. +The inverse is not guaranteed.

+
Source

pub fn may_retry(&self) -> bool

Whether it is legal to respond with a retry packet

+

If self.remote_address_validated() is false, self.may_retry() is guaranteed to be true. +The inverse is not guaranteed.

+
Source

pub fn orig_dst_cid(&self) -> ConnectionId

The original destination CID when initiating the connection

+

Trait Implementations§

Source§

impl Debug for Incoming

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for Incoming

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl IntoFuture for Incoming

Source§

type Output = Result<Connection, ConnectionError>

The output that the future will produce on completion.
Source§

type IntoFuture = IncomingFuture

Which kind of future are we turning this into?
Source§

fn into_future(self) -> Self::IntoFuture

Creates a future from a value. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.IncomingFuture.html b/pr/395/docs/iroh_quinn/struct.IncomingFuture.html new file mode 100644 index 000000000..e607068ec --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.IncomingFuture.html @@ -0,0 +1,53 @@ +IncomingFuture in iroh_quinn - Rust

IncomingFuture

Struct IncomingFuture 

Source
pub struct IncomingFuture(/* private fields */);
Expand description

Basic adapter to let Incoming be await-ed like a Connecting

+

Trait Implementations§

Source§

impl Debug for IncomingFuture

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Future for IncomingFuture

Source§

type Output = Result<Connection, ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.InvalidCid.html b/pr/395/docs/iroh_quinn/struct.InvalidCid.html new file mode 100644 index 000000000..214b681a3 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.InvalidCid.html @@ -0,0 +1,27 @@ +InvalidCid in iroh_quinn - Rust

InvalidCid

Struct InvalidCid 

Source
pub struct InvalidCid;
Expand description

The connection ID was not recognized by the ConnectionIdGenerator

+

Trait Implementations§

Source§

impl Clone for InvalidCid

Source§

fn clone(&self) -> InvalidCid

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for InvalidCid

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Copy for InvalidCid

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.MtuDiscoveryConfig.html b/pr/395/docs/iroh_quinn/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..0ff38acf8 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.MtuDiscoveryConfig.html @@ -0,0 +1,88 @@ +MtuDiscoveryConfig in iroh_quinn - Rust

MtuDiscoveryConfig

Struct MtuDiscoveryConfig 

Source
pub struct MtuDiscoveryConfig { /* private fields */ }
Expand description

Parameters governing MTU discovery.

+

§The why of MTU discovery

+

By design, QUIC ensures during the handshake that the network path between the client and the +server is able to transmit unfragmented UDP packets with a body of 1200 bytes. In other words, +once the connection is established, we know that the network path’s maximum transmission unit +(MTU) is of at least 1200 bytes (plus IP and UDP headers). Because of this, a QUIC endpoint can +split outgoing data in packets of 1200 bytes, with confidence that the network will be able to +deliver them (if the endpoint were to send bigger packets, they could prove too big and end up +being dropped).

+

There is, however, a significant overhead associated to sending a packet. If the same +information can be sent in fewer packets, that results in higher throughput. The amount of +packets that need to be sent is inversely proportional to the MTU: the higher the MTU, the +bigger the packets that can be sent, and the fewer packets that are needed to transmit a given +amount of bytes.

+

Most networks have an MTU higher than 1200. Through MTU discovery, endpoints can detect the +path’s MTU and, if it turns out to be higher, start sending bigger packets.

+

§MTU discovery internals

+

Quinn implements MTU discovery through DPLPMTUD (Datagram Packetization Layer Path MTU +Discovery), described in section 14.3 of RFC +9000. This method consists of sending +QUIC packets padded to a particular size (called PMTU probes), and waiting to see if the remote +peer responds with an ACK. If an ACK is received, that means the probe arrived at the remote +peer, which in turn means that the network path’s MTU is of at least the packet’s size. If the +probe is lost, it is sent another 2 times before concluding that the MTU is lower than the +packet’s size.

+

MTU discovery runs on a schedule (e.g. every 600 seconds) specified through +MtuDiscoveryConfig::interval. The first run happens right after the handshake, and +subsequent discoveries are scheduled to run when the interval has elapsed, starting from the +last time when MTU discovery completed.

+

Since the search space for MTUs is quite big (the smallest possible MTU is 1200, and the highest +is 65527), Quinn performs a binary search to keep the number of probes as low as possible. The +lower bound of the search is equal to TransportConfig::initial_mtu in the +initial MTU discovery run, and is equal to the currently discovered MTU in subsequent runs. The +upper bound is determined by the minimum of MtuDiscoveryConfig::upper_bound and the +max_udp_payload_size transport parameter received from the peer during the handshake.

+

§Black hole detection

+

If, at some point, the network path no longer accepts packets of the detected size, packet loss +will eventually trigger black hole detection and reset the detected MTU to 1200. In that case, +MTU discovery will be triggered after MtuDiscoveryConfig::black_hole_cooldown (ignoring the +timer that was set based on MtuDiscoveryConfig::interval).

+

§Interaction between peers

+

There is no guarantee that the MTU on the path between A and B is the same as the MTU of the +path between B and A. Therefore, each peer in the connection needs to run MTU discovery +independently in order to discover the path’s MTU.

+

Implementations§

Source§

impl MtuDiscoveryConfig

Source

pub fn interval(&mut self, value: Duration) -> &mut MtuDiscoveryConfig

Specifies the time to wait after completing MTU discovery before starting a new MTU +discovery run.

+

Defaults to 600 seconds, as recommended by RFC +8899.

+
Source

pub fn upper_bound(&mut self, value: u16) -> &mut MtuDiscoveryConfig

Specifies the upper bound to the max UDP payload size that MTU discovery will search for.

+

Defaults to 1452, to stay within Ethernet’s MTU when using IPv4 and IPv6. The highest +allowed value is 65527, which corresponds to the maximum permitted UDP payload on IPv6.

+

It is safe to use an arbitrarily high upper bound, regardless of the network path’s MTU. The +only drawback is that MTU discovery might take more time to finish.

+
Source

pub fn black_hole_cooldown( + &mut self, + value: Duration, +) -> &mut MtuDiscoveryConfig

Specifies the amount of time that MTU discovery should wait after a black hole was detected +before running again. Defaults to one minute.

+

Black hole detection can be spuriously triggered in case of congestion, so it makes sense to +try MTU discovery again after a short period of time.

+
Source

pub fn minimum_change(&mut self, value: u16) -> &mut MtuDiscoveryConfig

Specifies the minimum MTU change to stop the MTU discovery phase. +Defaults to 20.

+

Trait Implementations§

Source§

impl Clone for MtuDiscoveryConfig

Source§

fn clone(&self) -> MtuDiscoveryConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MtuDiscoveryConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for MtuDiscoveryConfig

Source§

fn default() -> MtuDiscoveryConfig

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.NoneTokenLog.html b/pr/395/docs/iroh_quinn/struct.NoneTokenLog.html new file mode 100644 index 000000000..aa526d6a8 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.NoneTokenLog.html @@ -0,0 +1,31 @@ +NoneTokenLog in iroh_quinn - Rust

NoneTokenLog

Struct NoneTokenLog 

Source
pub struct NoneTokenLog;
Expand description

Null implementation of TokenLog, which never accepts tokens

+

Trait Implementations§

Source§

impl TokenLog for NoneTokenLog

Source§

fn check_and_insert( + &self, + _: u128, + _: SystemTime, + _: Duration, +) -> Result<(), TokenReuseError>

Record that the token was used and, ideally, return a token reuse error if the token may +have been already used previously Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.NoneTokenStore.html b/pr/395/docs/iroh_quinn/struct.NoneTokenStore.html new file mode 100644 index 000000000..450968c06 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.NoneTokenStore.html @@ -0,0 +1,25 @@ +NoneTokenStore in iroh_quinn - Rust

NoneTokenStore

Struct NoneTokenStore 

Source
pub struct NoneTokenStore;
Expand description

Null implementation of TokenStore, which does not store any tokens

+

Trait Implementations§

Source§

impl TokenStore for NoneTokenStore

Source§

fn insert(&self, _: &str, _: Bytes)

Potentially store a token for later one-time use Read more
Source§

fn take(&self, _: &str) -> Option<Bytes>

Try to find and take a token that was stored with the given server name Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.OnClosed.html b/pr/395/docs/iroh_quinn/struct.OnClosed.html new file mode 100644 index 000000000..7ac10f873 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.OnClosed.html @@ -0,0 +1,50 @@ +OnClosed in iroh_quinn - Rust

OnClosed

Struct OnClosed 

Source
pub struct OnClosed { /* private fields */ }
Expand description

Future returned by Connection::on_closed

+

Resolves to a tuple of (ConnectionError, ConnectionStats).

+

Trait Implementations§

Source§

impl Drop for OnClosed

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl Future for OnClosed

Source§

type Output = (ConnectionError, ConnectionStats)

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.OpenBi.html b/pr/395/docs/iroh_quinn/struct.OpenBi.html new file mode 100644 index 000000000..02274d2c3 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.OpenBi.html @@ -0,0 +1,54 @@ +OpenBi in iroh_quinn - Rust

OpenBi

Struct OpenBi 

Source
pub struct OpenBi<'a> { /* private fields */ }
Expand description

Future produced by Connection::open_bi

+

Trait Implementations§

Source§

impl Future for OpenBi<'_>

Source§

type Output = Result<(SendStream, RecvStream), ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for OpenBi<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for OpenBi<'a>

§

impl<'a> !RefUnwindSafe for OpenBi<'a>

§

impl<'a> Send for OpenBi<'a>

§

impl<'a> Sync for OpenBi<'a>

§

impl<'a> !UnwindSafe for OpenBi<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.OpenPath.html b/pr/395/docs/iroh_quinn/struct.OpenPath.html new file mode 100644 index 000000000..a60407291 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.OpenPath.html @@ -0,0 +1,57 @@ +OpenPath in iroh_quinn - Rust

OpenPath

Struct OpenPath 

Source
pub struct OpenPath(/* private fields */);
Expand description

Future produced by crate::Connection::open_path

+

Implementations§

Source§

impl OpenPath

Source

pub fn path_id(&self) -> Option<PathId>

Returns the path ID of the new path being opened.

+

If an error occurred before a path ID was allocated, None is returned. In this +case the future is ready and polling it will immediately yield the error.

+

The returned value remains the same for the entire lifetime of this future.

+

Trait Implementations§

Source§

impl Future for OpenPath

Source§

type Output = Result<Path, PathError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.OpenUni.html b/pr/395/docs/iroh_quinn/struct.OpenUni.html new file mode 100644 index 000000000..a13245ede --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.OpenUni.html @@ -0,0 +1,54 @@ +OpenUni in iroh_quinn - Rust

OpenUni

Struct OpenUni 

Source
pub struct OpenUni<'a> { /* private fields */ }
Expand description

Future produced by Connection::open_uni

+

Trait Implementations§

Source§

impl Future for OpenUni<'_>

Source§

type Output = Result<SendStream, ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for OpenUni<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for OpenUni<'a>

§

impl<'a> !RefUnwindSafe for OpenUni<'a>

§

impl<'a> Send for OpenUni<'a>

§

impl<'a> Sync for OpenUni<'a>

§

impl<'a> !UnwindSafe for OpenUni<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.Path.html b/pr/395/docs/iroh_quinn/struct.Path.html new file mode 100644 index 000000000..24b4f477d --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.Path.html @@ -0,0 +1,48 @@ +Path in iroh_quinn - Rust

Path

Struct Path 

Source
pub struct Path { /* private fields */ }
Expand description

An open (Multi)Path

+

Implementations§

Source§

impl Path

Source

pub fn id(&self) -> PathId

The PathId of this path.

+
Source

pub fn status(&self) -> Result<PathStatus, ClosedPath>

The current local PathStatus of this path.

+
Source

pub fn set_status(&self, status: PathStatus) -> Result<(), SetPathStatusError>

Sets the PathStatus of this path.

+
Source

pub fn close(&self) -> Result<ClosePath, ClosePathError>

Closes this path

+

The future will resolve when all the path state is dropped. This only happens after +the remote has confirmed the path as closed and after an additional timeout to +give any in-flight packets the time to arrive.

+
Source

pub fn set_max_idle_timeout( + &self, + timeout: Option<Duration>, +) -> Result<Option<Duration>, ClosedPath>

Sets the keep_alive_interval for a specific path

+

See TransportConfig::default_path_keep_alive_interval for details.

+

Returns the previous value of the setting.

+
Source

pub fn set_keep_alive_interval( + &self, + interval: Option<Duration>, +) -> Result<Option<Duration>, ClosedPath>

Sets the keep_alive_interval for a specific path

+

See TransportConfig::default_path_keep_alive_interval for details.

+

Returns the previous value of the setting.

+
Source

pub fn observed_external_addr(&self) -> Result<AddressDiscovery, ClosedPath>

Track changes on our external address as reported by the peer.

+

If the address-discovery extension is not negotiated, the stream will never return.

+
Source

pub fn remote_address(&self) -> Result<SocketAddr, ClosedPath>

The peer’s UDP address for this path.

+
Source

pub fn ping(&self) -> Result<(), ClosedPath>

Ping the remote endpoint over this path.

+

Trait Implementations§

Source§

impl Debug for Path

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Path

§

impl RefUnwindSafe for Path

§

impl Send for Path

§

impl Sync for Path

§

impl Unpin for Path

§

impl UnwindSafe for Path

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.PathId.html b/pr/395/docs/iroh_quinn/struct.PathId.html new file mode 100644 index 000000000..2759c307d --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.PathId.html @@ -0,0 +1,53 @@ +PathId in iroh_quinn - Rust

PathId

Struct PathId 

Source
pub struct PathId(/* private fields */);
Expand description

Id representing different paths when using multipath extension

+

Implementations§

Source§

impl PathId

Source

pub const MAX: PathId

The maximum path ID allowed.

+
Source

pub const ZERO: PathId

The 0 path id.

+
Source

pub fn saturating_add(self, rhs: impl Into<PathId>) -> PathId

Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead +of overflowing.

+
Source

pub fn saturating_sub(self, rhs: impl Into<PathId>) -> PathId

Saturating integer subtraction. Computes self - rhs, saturating at the numeric bounds +instead of overflowing.

+

Trait Implementations§

Source§

impl Clone for PathId

Source§

fn clone(&self) -> PathId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Decodable for PathId

Source§

fn decode<B>(r: &mut B) -> Result<PathId, UnexpectedEnd>
where + B: Buf,

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Default for PathId

Source§

fn default() -> PathId

Returns the “default value” for a type. Read more
Source§

impl Display for PathId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Encodable for PathId

Source§

fn encode<B>(&self, w: &mut B)
where + B: BufMut,

Append the encoding of self to the provided buffer.
Source§

impl<T> From<T> for PathId
where + T: Into<u32>,

Source§

fn from(source: T) -> PathId

Converts to this type from the input type.
Source§

impl Hash for PathId

Source§

fn hash<H>(&self, state: &mut H)
where + H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for PathId

Source§

fn cmp(&self, other: &PathId) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for PathId

Source§

fn eq(&self, other: &PathId) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for PathId

Source§

fn partial_cmp(&self, other: &PathId) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for PathId

Source§

impl Eq for PathId

Source§

impl IdentityHashable for PathId

Source§

impl StructuralPartialEq for PathId

Auto Trait Implementations§

§

impl Freeze for PathId

§

impl RefUnwindSafe for PathId

§

impl Send for PathId

§

impl Sync for PathId

§

impl Unpin for PathId

§

impl UnwindSafe for PathId

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.PathStats.html b/pr/395/docs/iroh_quinn/struct.PathStats.html new file mode 100644 index 000000000..4b2bc0b01 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.PathStats.html @@ -0,0 +1,56 @@ +PathStats in iroh_quinn - Rust

PathStats

Struct PathStats 

Source
#[non_exhaustive]
pub struct PathStats { + pub rtt: Duration, + pub udp_tx: UdpStats, + pub udp_rx: UdpStats, + pub cwnd: u64, + pub congestion_events: u64, + pub lost_packets: u64, + pub lost_bytes: u64, + pub sent_plpmtud_probes: u64, + pub lost_plpmtud_probes: u64, + pub black_holes_detected: u64, + pub current_mtu: u16, +}
Expand description

Statistics related to a transmission path

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§rtt: Duration

Current best estimate of this connection’s latency (round-trip-time)

+
§udp_tx: UdpStats

Statistics about datagrams and bytes sent on this path

+
§udp_rx: UdpStats

Statistics about datagrams and bytes received on this path

+
§cwnd: u64

Current congestion window of the connection

+
§congestion_events: u64

Congestion events on the connection

+
§lost_packets: u64

The amount of packets lost on this path

+
§lost_bytes: u64

The amount of bytes lost on this path

+
§sent_plpmtud_probes: u64

The amount of PLPMTUD probe packets sent on this path (also counted by udp_tx.datagrams)

+
§lost_plpmtud_probes: u64

The amount of PLPMTUD probe packets lost on this path (ignored by lost_packets and +lost_bytes)

+
§black_holes_detected: u64

The number of times a black hole was detected in the path

+
§current_mtu: u16

Largest UDP payload size the path currently supports

+

Trait Implementations§

Source§

impl Clone for PathStats

Source§

fn clone(&self) -> PathStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for PathStats

Source§

fn default() -> PathStats

Returns the “default value” for a type. Read more
Source§

impl PartialEq for PathStats

Source§

fn eq(&self, other: &PathStats) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathStats

Source§

impl Eq for PathStats

Source§

impl StructuralPartialEq for PathStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.QlogConfig.html b/pr/395/docs/iroh_quinn/struct.QlogConfig.html new file mode 100644 index 000000000..bef1e4dcf --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.QlogConfig.html @@ -0,0 +1,34 @@ +QlogConfig in iroh_quinn - Rust

QlogConfig

Struct QlogConfig 

Source
pub struct QlogConfig { /* private fields */ }
Expand description

Configuration for qlog trace logging.

+

This struct is returned from QlogFactory::for_connection if qlog logging should +be enabled for a connection. It allows to set metadata for the qlog trace.

+

The trace will be written to the provided writer in the [JSON-SEQ format] defined in the qlog spec.

+

JSON-SEQ format

+

Implementations§

Source§

impl QlogConfig

Source

pub fn new(writer: Box<dyn Write + Send + Sync>) -> QlogConfig

Creates a new QlogConfig that writes a qlog trace to the specified writer.

+
Source

pub fn title(&mut self, title: Option<String>) -> &mut QlogConfig

Title to record in the qlog capture

+
Source

pub fn description(&mut self, description: Option<String>) -> &mut QlogConfig

Description to record in the qlog capture

+
Source

pub fn start_time(&mut self, start_time: Instant) -> &mut QlogConfig

Epoch qlog event times are recorded relative to

+

If unset, the start of the connection is used.

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.QlogFileFactory.html b/pr/395/docs/iroh_quinn/struct.QlogFileFactory.html new file mode 100644 index 000000000..ec814cda1 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.QlogFileFactory.html @@ -0,0 +1,39 @@ +QlogFileFactory in iroh_quinn - Rust

QlogFileFactory

Struct QlogFileFactory 

Source
pub struct QlogFileFactory { /* private fields */ }
Expand description

Enables writing qlog traces to a directory.

+

Implementations§

Source§

impl QlogFileFactory

Source

pub fn new(dir: PathBuf) -> QlogFileFactory

Creates a new qlog factory that writes files into the specified directory.

+
Source

pub fn from_env() -> QlogFileFactory

Creates a new qlog factory that writes files into QLOGDIR, if set.

+

If the environment variable QLOGDIR is set, qlog traces for all connections handled +by this endpoint will be written into that directory. +If the directory doesn’t exist it will be created.

+
Source

pub fn with_prefix(self, prefix: impl ToString) -> QlogFileFactory

Sets a prefix to the filename of the generated files.

+
Source

pub fn with_start_instant(self, start: Instant) -> QlogFileFactory

Override the instant relative to which all events are recorded.

+

If not set, events will be recorded relative to the start of the connection.

+

Trait Implementations§

Source§

impl Debug for QlogFileFactory

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl QlogFactory for QlogFileFactory

Source§

fn for_connection( + &self, + side: Side, + _remote: SocketAddr, + initial_dst_cid: ConnectionId, + now: Instant, +) -> Option<QlogConfig>

Returns a QlogConfig for a connection, if logging should be enabled. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.ReadDatagram.html b/pr/395/docs/iroh_quinn/struct.ReadDatagram.html new file mode 100644 index 000000000..fc318ee0c --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.ReadDatagram.html @@ -0,0 +1,54 @@ +ReadDatagram in iroh_quinn - Rust

ReadDatagram

Struct ReadDatagram 

Source
pub struct ReadDatagram<'a> { /* private fields */ }
Expand description

Future produced by Connection::read_datagram

+

Trait Implementations§

Source§

impl Future for ReadDatagram<'_>

Source§

type Output = Result<Bytes, ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for ReadDatagram<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for ReadDatagram<'a>

§

impl<'a> !RefUnwindSafe for ReadDatagram<'a>

§

impl<'a> Send for ReadDatagram<'a>

§

impl<'a> Sync for ReadDatagram<'a>

§

impl<'a> !UnwindSafe for ReadDatagram<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.RecvStream.html b/pr/395/docs/iroh_quinn/struct.RecvStream.html new file mode 100644 index 000000000..11810c6e9 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.RecvStream.html @@ -0,0 +1,210 @@ +RecvStream in iroh_quinn - Rust

RecvStream

Struct RecvStream 

Source
pub struct RecvStream { /* private fields */ }
Expand description

A stream that can only be used to receive data

+

stop(0) is implicitly called on drop unless:

+
    +
  • A variant of ReadError has been yielded by a read call
  • +
  • stop() was called explicitly
  • +
+

§Cancellation

+

A read method is said to be cancel-safe when dropping its future before the future becomes +ready cannot lead to loss of stream data. This is true of methods which succeed immediately when +any progress is made, and is not true of methods which might need to perform multiple reads +internally before succeeding. Each read method documents whether it is cancel-safe.

+

§Common issues

§Data never received on a locally-opened stream

+

Peers are not notified of streams until they or a later-numbered stream are used to send +data. If a bidirectional stream is locally opened but never used to send, then the peer may +never see it. Application protocols should always arrange for the endpoint which will first +transmit on a stream to be the endpoint responsible for opening it.

+

§Data never received on a remotely-opened stream

+

Verify that the stream you are receiving is the same one that the server is sending on, e.g. by +logging the id of each. Streams are always accepted in the same order as they are created, +i.e. ascending order by StreamId. For example, even if a sender first transmits on +bidirectional stream 1, the first stream yielded by Connection::accept_bi on the receiver +will be bidirectional stream 0.

+

Implementations§

Source§

impl RecvStream

Source

pub async fn read(&mut self, buf: &mut [u8]) -> Result<Option<usize>, ReadError>

Read data contiguously from the stream.

+

Yields the number of bytes read into buf on success, or None if the stream was finished.

+

This operation is cancel-safe.

+
Source

pub async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError>

Read an exact number of bytes contiguously from the stream.

+

See read() for details. This operation is not cancel-safe.

+
Source

pub fn poll_read( + &mut self, + cx: &mut Context<'_>, + buf: &mut [u8], +) -> Poll<Result<usize, ReadError>>

Attempts to read from the stream into the provided buffer

+

On success, returns Poll::Ready(Ok(num_bytes_read)) and places data into buf. If this +returns zero bytes read (and buf has a non-zero length), that indicates that the remote +side has finished the stream and the local side has already read all bytes.

+

If no data is available for reading, this returns Poll::Pending and arranges for the +current task (via cx.waker()) to be notified when the stream becomes readable or is +closed.

+
Source

pub fn poll_read_buf( + &mut self, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, +) -> Poll<Result<(), ReadError>>

Attempts to read from the stream into the provided buffer, which may be uninitialized

+

On success, returns Poll::Ready(Ok(())) and places data into the unfilled portion of +buf. If this does not write any bytes to buf (and buf.remaining() is non-zero), that +indicates that the remote side has finished the stream and the local side has already +read all bytes.

+

If no data is available for reading, this returns Poll::Pending and arranges for the +current task (via cx.waker()) to be notified when the stream becomes readable or is +closed.

+
Source

pub async fn read_chunk( + &mut self, + max_length: usize, +) -> Result<Option<Chunk>, ReadError>

Read the next segment of data

+

Yields None if the stream was finished. Otherwise, yields a segment of data and its +offset in the stream. The chunk’s offset will be immediately after +the last data yielded by RecvStream::read or RecvStream::read_chunk.

+

For unordered reads, convert the stream into an unordered stream using Self::into_unordered.

+

Slightly more efficient than RecvStream::read due to not copying. Chunk boundaries do not correspond +to peer writes, and hence cannot be used as framing.

+

This operation is cancel-safe.

+
Source

pub async fn read_chunks( + &mut self, + bufs: &mut [Bytes], +) -> Result<Option<usize>, ReadError>

Read the next segments of data

+

Fills bufs with the segments of data beginning immediately after the +last data yielded by read or read_chunk, or None if the stream was +finished.

+

Slightly more efficient than read due to not copying. Chunk boundaries +do not correspond to peer writes, and hence cannot be used as framing.

+

This operation is cancel-safe.

+
Source

pub async fn read_to_end( + &mut self, + size_limit: usize, +) -> Result<Vec<u8>, ReadToEndError>

Convenience method to read all remaining data into a buffer

+

Fails with ReadToEndError::TooLong on reading more than size_limit bytes, discarding +all data read. Uses unordered reads to be more efficient than using AsyncRead would +allow. size_limit should be set to limit worst-case memory use.

+

If unordered reads have already been made, the resulting buffer may have gaps containing +arbitrary data.

+

This operation is not cancel-safe.

+
Source

pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream>

Stop accepting data

+

Discards unread data and notifies the peer to stop transmitting. Once stopped, further +attempts to operate on a stream will yield ClosedStream errors.

+
Source

pub fn is_0rtt(&self) -> bool

Check if this stream has been opened during 0-RTT.

+

In which case any non-idempotent request should be considered dangerous at the application +level. Because read data is subject to replay attacks.

+
Source

pub fn id(&self) -> StreamId

Get the identity of this stream

+
Source

pub async fn received_reset(&mut self) -> Result<Option<VarInt>, ResetError>

Completes when the stream has been reset by the peer or otherwise closed

+

Yields Some with the reset error code when the stream is reset by the peer. Yields None +when the stream was previously stop()ed, or when the stream was +finish()ed by the peer and all data has been received, after +which it is no longer meaningful for the stream to be reset.

+

This operation is cancel-safe.

+
Source

pub fn into_unordered(self) -> UnorderedRecvStream

Converts this stream into an unordered stream.

+

Trait Implementations§

Source§

impl AsyncRead for RecvStream

Available on crate feature futures-io only.
Source§

fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], +) -> Poll<Result<usize>>

Attempt to read from the AsyncRead into buf. Read more
§

fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], +) -> Poll<Result<usize, Error>>

Attempt to read from the AsyncRead into bufs using vectored +IO operations. Read more
Source§

impl AsyncRead for RecvStream

Source§

fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, +) -> Poll<Result<()>>

Attempts to read from the AsyncRead into buf. Read more
Source§

impl Debug for RecvStream

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for RecvStream

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<R> AsyncReadExt for R
where + R: AsyncRead + ?Sized,

§

fn chain<R>(self, next: R) -> Chain<Self, R>
where + Self: Sized, + R: AsyncRead,

Creates a new AsyncRead instance that chains this stream with +next. Read more
§

fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self>
where + Self: Unpin,

Pulls some bytes from this source into the specified buffer, +returning how many bytes were read. Read more
§

fn read_buf<'a, B>(&'a mut self, buf: &'a mut B) -> ReadBuf<'a, Self, B>
where + Self: Unpin, + B: BufMut + ?Sized,

Pulls some bytes from this source into the specified buffer, +advancing the buffer’s internal cursor. Read more
§

fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self>
where + Self: Unpin,

Reads the exact number of bytes required to fill buf. Read more
§

fn read_u8(&mut self) -> ReadU8<&mut Self>
where + Self: Unpin,

Reads an unsigned 8 bit integer from the underlying reader. Read more
§

fn read_i8(&mut self) -> ReadI8<&mut Self>
where + Self: Unpin,

Reads a signed 8 bit integer from the underlying reader. Read more
§

fn read_u16(&mut self) -> ReadU16<&mut Self>
where + Self: Unpin,

Reads an unsigned 16-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_i16(&mut self) -> ReadI16<&mut Self>
where + Self: Unpin,

Reads a signed 16-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_u32(&mut self) -> ReadU32<&mut Self>
where + Self: Unpin,

Reads an unsigned 32-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_i32(&mut self) -> ReadI32<&mut Self>
where + Self: Unpin,

Reads a signed 32-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_u64(&mut self) -> ReadU64<&mut Self>
where + Self: Unpin,

Reads an unsigned 64-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_i64(&mut self) -> ReadI64<&mut Self>
where + Self: Unpin,

Reads an signed 64-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_u128(&mut self) -> ReadU128<&mut Self>
where + Self: Unpin,

Reads an unsigned 128-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_i128(&mut self) -> ReadI128<&mut Self>
where + Self: Unpin,

Reads an signed 128-bit integer in big-endian order from the +underlying reader. Read more
§

fn read_f32(&mut self) -> ReadF32<&mut Self>
where + Self: Unpin,

Reads an 32-bit floating point type in big-endian order from the +underlying reader. Read more
§

fn read_f64(&mut self) -> ReadF64<&mut Self>
where + Self: Unpin,

Reads an 64-bit floating point type in big-endian order from the +underlying reader. Read more
§

fn read_u16_le(&mut self) -> ReadU16Le<&mut Self>
where + Self: Unpin,

Reads an unsigned 16-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_i16_le(&mut self) -> ReadI16Le<&mut Self>
where + Self: Unpin,

Reads a signed 16-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_u32_le(&mut self) -> ReadU32Le<&mut Self>
where + Self: Unpin,

Reads an unsigned 32-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_i32_le(&mut self) -> ReadI32Le<&mut Self>
where + Self: Unpin,

Reads a signed 32-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_u64_le(&mut self) -> ReadU64Le<&mut Self>
where + Self: Unpin,

Reads an unsigned 64-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_i64_le(&mut self) -> ReadI64Le<&mut Self>
where + Self: Unpin,

Reads an signed 64-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_u128_le(&mut self) -> ReadU128Le<&mut Self>
where + Self: Unpin,

Reads an unsigned 128-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_i128_le(&mut self) -> ReadI128Le<&mut Self>
where + Self: Unpin,

Reads an signed 128-bit integer in little-endian order from the +underlying reader. Read more
§

fn read_f32_le(&mut self) -> ReadF32Le<&mut Self>
where + Self: Unpin,

Reads an 32-bit floating point type in little-endian order from the +underlying reader. Read more
§

fn read_f64_le(&mut self) -> ReadF64Le<&mut Self>
where + Self: Unpin,

Reads an 64-bit floating point type in little-endian order from the +underlying reader. Read more
§

fn read_to_end<'a>(&'a mut self, buf: &'a mut Vec<u8>) -> ReadToEnd<'a, Self>
where + Self: Unpin,

Reads all bytes until EOF in this source, placing them into buf. Read more
§

fn read_to_string<'a>( + &'a mut self, + dst: &'a mut String, +) -> ReadToString<'a, Self>
where + Self: Unpin,

Reads all bytes until EOF in this source, appending them to buf. Read more
§

fn take(self, limit: u64) -> Take<Self>
where + Self: Sized,

Creates an adaptor which reads at most limit bytes from it. Read more
§

impl<R> AsyncReadExt for R
where + R: AsyncRead + ?Sized,

§

fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadFuture<'a, Self>
where + Self: Unpin,

Reads some bytes from the byte stream. Read more
§

fn read_vectored<'a>( + &'a mut self, + bufs: &'a mut [IoSliceMut<'a>], +) -> ReadVectoredFuture<'a, Self>
where + Self: Unpin,

Like [read()][AsyncReadExt::read()], except it reads into a slice of buffers. Read more
§

fn read_to_end<'a>( + &'a mut self, + buf: &'a mut Vec<u8>, +) -> ReadToEndFuture<'a, Self>
where + Self: Unpin,

Reads the entire contents and appends them to a Vec. Read more
§

fn read_to_string<'a>( + &'a mut self, + buf: &'a mut String, +) -> ReadToStringFuture<'a, Self>
where + Self: Unpin,

Reads the entire contents and appends them to a String. Read more
§

fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExactFuture<'a, Self>
where + Self: Unpin,

Reads the exact number of bytes required to fill buf. Read more
§

fn take(self, limit: u64) -> Take<Self>
where + Self: Sized,

Creates an adapter which will read at most limit bytes from it. Read more
§

fn bytes(self) -> Bytes<Self>
where + Self: Sized,

Converts this [AsyncRead] into a [Stream] of bytes. Read more
§

fn chain<R>(self, next: R) -> Chain<Self, R>
where + R: AsyncRead, + Self: Sized,

Creates an adapter which will chain this stream with another. Read more
§

fn boxed_reader<'a>(self) -> Pin<Box<dyn AsyncRead + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the reader and changes its type to dyn AsyncRead + Send + 'a. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.RetryError.html b/pr/395/docs/iroh_quinn/struct.RetryError.html new file mode 100644 index 000000000..e1f9daf29 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.RetryError.html @@ -0,0 +1,27 @@ +RetryError in iroh_quinn - Rust

RetryError

Struct RetryError 

Source
pub struct RetryError(/* private fields */);
Expand description

Error for attempting to retry an Incoming which already bears a token from a previous retry

+

Implementations§

Trait Implementations§

Source§

impl Debug for RetryError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for RetryError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for RetryError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.SendDatagram.html b/pr/395/docs/iroh_quinn/struct.SendDatagram.html new file mode 100644 index 000000000..856168e13 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.SendDatagram.html @@ -0,0 +1,54 @@ +SendDatagram in iroh_quinn - Rust

SendDatagram

Struct SendDatagram 

Source
pub struct SendDatagram<'a> { /* private fields */ }
Expand description

Future produced by Connection::send_datagram_wait

+

Trait Implementations§

Source§

impl Future for SendDatagram<'_>

Source§

type Output = Result<(), SendDatagramError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for SendDatagram<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for SendDatagram<'a>

§

impl<'a> !RefUnwindSafe for SendDatagram<'a>

§

impl<'a> Send for SendDatagram<'a>

§

impl<'a> Sync for SendDatagram<'a>

§

impl<'a> !UnwindSafe for SendDatagram<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.SendStream.html b/pr/395/docs/iroh_quinn/struct.SendStream.html new file mode 100644 index 000000000..e98922d24 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.SendStream.html @@ -0,0 +1,222 @@ +SendStream in iroh_quinn - Rust

SendStream

Struct SendStream 

Source
pub struct SendStream { /* private fields */ }
Expand description

A stream that can only be used to send data

+

If dropped, streams that haven’t been explicitly reset() will be implicitly finish()ed, +continuing to (re)transmit previously written data until it has been fully acknowledged or the +connection is closed.

+

§Cancellation

+

A write method is said to be cancel-safe when dropping its future before the future becomes +ready will always result in no data being written to the stream. This is true of methods which +succeed immediately when any progress is made, and is not true of methods which might need to +perform multiple writes internally before succeeding. Each write method documents whether it is +cancel-safe.

+

Implementations§

Source§

impl SendStream

Source

pub async fn write(&mut self, buf: &[u8]) -> Result<usize, WriteError>

Write a buffer into this stream, returning how many bytes were written

+

Unless this method errors, it waits until some amount of buf can be written into this +stream, and then writes as much as it can without waiting again. Due to congestion and flow +control, this may be shorter than buf.len(). On success this yields the length of the +prefix that was written.

+
§Cancel safety
+

This method is cancellation safe. If this does not resolve, no bytes were written.

+
Source

pub async fn write_all(&mut self, buf: &[u8]) -> Result<(), WriteError>

Write a buffer into this stream in its entirety

+

This method repeatedly calls write until all bytes are written, or an +error occurs.

+
§Cancel safety
+

This method is not cancellation safe. Even if this does not resolve, some prefix of buf +may have been written when previously polled.

+
Source

pub async fn write_chunks( + &mut self, + bufs: &mut [Bytes], +) -> Result<Written, WriteError>

Write a slice of [Bytes] into this stream, returning how much was written

+

Bytes to try to write are provided to this method as an array of cheaply cloneable chunks. +Unless this method errors, it waits until some amount of those bytes can be written into +this stream, and then writes as much as it can without waiting again. Due to congestion and +flow control, this may be less than the total number of bytes.

+

On success, this method both mutates bufs and yields an informative Written struct +indicating how much was written:

+
    +
  • [Bytes] chunks that were fully written are mutated to be empty.
  • +
  • If a [Bytes] chunk was partially written, it is split to contain +only the suffix of bytes that were not written.
  • +
  • The yielded Written struct indicates how many chunks were fully written as well as +how many bytes were written.
  • +
+
§Cancel safety
+

This method is cancellation safe. If this does not resolve, no bytes were written.

+
Source

pub async fn write_chunk(&mut self, buf: Bytes) -> Result<(), WriteError>

Write a single [Bytes] into this stream in its entirety

+

Bytes to write are provided to this method as an single cheaply cloneable chunk. This +method repeatedly calls write_chunks until all bytes are written, +or an error occurs.

+
§Cancel safety
+

This method is not cancellation safe. Even if this does not resolve, some bytes may have +been written when previously polled.

+
Source

pub async fn write_all_chunks( + &mut self, + bufs: &mut [Bytes], +) -> Result<(), WriteError>

Write a slice of [Bytes] into this stream in its entirety

+

Bytes to write are provided to this method as an array of cheaply cloneable chunks. This +method repeatedly calls write_chunks until all bytes are written, +or an error occurs. This method mutates bufs by mutating all chunks to be +empty.

+
§Cancel safety
+

This method is not cancellation safe. Even if this does not resolve, some bytes may have +been written when previously polled.

+
Source

pub fn finish(&mut self) -> Result<(), ClosedStream>

Notify the peer that no more data will ever be written to this stream

+

It is an error to write to a SendStream after finish()ing it. reset() +may still be called after finish to abandon transmission of any stream data that might +still be buffered.

+

To wait for the peer to receive all buffered stream data, see stopped().

+

May fail if finish() or reset() was previously +called. This error is harmless and serves only to indicate that the caller may have +incorrect assumptions about the stream’s state.

+
Source

pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream>

Close the send stream immediately.

+

No new data can be written after calling this method. Locally buffered data is dropped, and +previously transmitted data will no longer be retransmitted if lost. If an attempt has +already been made to finish the stream, the peer may still receive all written data.

+

May fail if finish() or reset() was previously +called. This error is harmless and serves only to indicate that the caller may have +incorrect assumptions about the stream’s state.

+
Source

pub fn set_priority(&self, priority: i32) -> Result<(), ClosedStream>

Set the priority of the send stream

+

Every send stream has an initial priority of 0. Locally buffered data from streams with +higher priority will be transmitted before data from streams with lower priority. Changing +the priority of a stream with pending data may only take effect after that data has been +transmitted. Using many different priority levels per connection may have a negative +impact on performance.

+
Source

pub fn priority(&self) -> Result<i32, ClosedStream>

Get the priority of the send stream

+
Source

pub fn stopped( + &self, +) -> impl Future<Output = Result<Option<VarInt>, StoppedError>> + Send + Sync + 'static

Completes when the peer stops the stream or reads the stream to completion

+

Yields Some with the stop error code if the peer stops the stream. Yields None if the +local side finish()es the stream and then the peer acknowledges receipt +of all stream data (although not necessarily the processing of it), after which the peer +closing the stream is no longer meaningful.

+

For a variety of reasons, the peer may not send acknowledgements immediately upon receiving +data. As such, relying on stopped to know when the peer has read a stream to completion +may introduce more latency than using an application-level response of some sort.

+
Source

pub fn id(&self) -> StreamId

Get the identity of this stream

+
Source

pub fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], +) -> Poll<Result<usize, WriteError>>

Attempt to write bytes from buf into the stream.

+

On success, returns Poll::Ready(Ok(num_bytes_written)).

+

If the stream is not ready for writing, the method returns Poll::Pending and arranges +for the current task (via cx.waker().wake_by_ref()) to receive a notification when the +stream becomes writable or is closed.

+

Trait Implementations§

Source§

impl AsyncWrite for SendStream

Available on crate feature futures-io only.
Source§

fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], +) -> Poll<Result<usize>>

Attempt to write bytes from buf into the object. Read more
Source§

fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>>

Attempt to flush the object, ensuring that any buffered data reach +their destination. Read more
Source§

fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>>

Attempt to close the object. Read more
§

fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], +) -> Poll<Result<usize, Error>>

Attempt to write bytes from bufs into the object using vectored +IO operations. Read more
Source§

impl AsyncWrite for SendStream

Source§

fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], +) -> Poll<Result<usize>>

Attempt to write bytes from buf into the object. Read more
Source§

fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>>

Attempts to flush the object, ensuring that any buffered data reach +their destination. Read more
Source§

fn poll_shutdown( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, +) -> Poll<Result<()>>

Initiates or attempts to shut down this writer, returning success when +the I/O connection has completely shut down. Read more
§

fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], +) -> Poll<Result<usize, Error>>

Like poll_write, except that it writes from a slice of buffers. Read more
§

fn is_write_vectored(&self) -> bool

Determines if this writer has an efficient poll_write_vectored +implementation. Read more
Source§

impl Debug for SendStream

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for SendStream

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<W> AsyncWriteExt for W
where + W: AsyncWrite + ?Sized,

§

fn write<'a>(&'a mut self, src: &'a [u8]) -> Write<'a, Self>
where + Self: Unpin,

Writes a buffer into this writer, returning how many bytes were +written. Read more
§

fn write_vectored<'a, 'b>( + &'a mut self, + bufs: &'a [IoSlice<'b>], +) -> WriteVectored<'a, 'b, Self>
where + Self: Unpin,

Like write, except that it writes from a slice of buffers. Read more
§

fn write_buf<'a, B>(&'a mut self, src: &'a mut B) -> WriteBuf<'a, Self, B>
where + Self: Sized + Unpin, + B: Buf,

Writes a buffer into this writer, advancing the buffer’s internal +cursor. Read more
§

fn write_all_buf<'a, B>( + &'a mut self, + src: &'a mut B, +) -> WriteAllBuf<'a, Self, B>
where + Self: Sized + Unpin, + B: Buf,

Attempts to write an entire buffer into this writer. Read more
§

fn write_all<'a>(&'a mut self, src: &'a [u8]) -> WriteAll<'a, Self>
where + Self: Unpin,

Attempts to write an entire buffer into this writer. Read more
§

fn write_u8(&mut self, n: u8) -> WriteU8<&mut Self>
where + Self: Unpin,

Writes an unsigned 8-bit integer to the underlying writer. Read more
§

fn write_i8(&mut self, n: i8) -> WriteI8<&mut Self>
where + Self: Unpin,

Writes a signed 8-bit integer to the underlying writer. Read more
§

fn write_u16(&mut self, n: u16) -> WriteU16<&mut Self>
where + Self: Unpin,

Writes an unsigned 16-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_i16(&mut self, n: i16) -> WriteI16<&mut Self>
where + Self: Unpin,

Writes a signed 16-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_u32(&mut self, n: u32) -> WriteU32<&mut Self>
where + Self: Unpin,

Writes an unsigned 32-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_i32(&mut self, n: i32) -> WriteI32<&mut Self>
where + Self: Unpin,

Writes a signed 32-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_u64(&mut self, n: u64) -> WriteU64<&mut Self>
where + Self: Unpin,

Writes an unsigned 64-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_i64(&mut self, n: i64) -> WriteI64<&mut Self>
where + Self: Unpin,

Writes an signed 64-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_u128(&mut self, n: u128) -> WriteU128<&mut Self>
where + Self: Unpin,

Writes an unsigned 128-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_i128(&mut self, n: i128) -> WriteI128<&mut Self>
where + Self: Unpin,

Writes an signed 128-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_f32(&mut self, n: f32) -> WriteF32<&mut Self>
where + Self: Unpin,

Writes an 32-bit floating point type in big-endian order to the +underlying writer. Read more
§

fn write_f64(&mut self, n: f64) -> WriteF64<&mut Self>
where + Self: Unpin,

Writes an 64-bit floating point type in big-endian order to the +underlying writer. Read more
§

fn write_u16_le(&mut self, n: u16) -> WriteU16Le<&mut Self>
where + Self: Unpin,

Writes an unsigned 16-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_i16_le(&mut self, n: i16) -> WriteI16Le<&mut Self>
where + Self: Unpin,

Writes a signed 16-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_u32_le(&mut self, n: u32) -> WriteU32Le<&mut Self>
where + Self: Unpin,

Writes an unsigned 32-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_i32_le(&mut self, n: i32) -> WriteI32Le<&mut Self>
where + Self: Unpin,

Writes a signed 32-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_u64_le(&mut self, n: u64) -> WriteU64Le<&mut Self>
where + Self: Unpin,

Writes an unsigned 64-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_i64_le(&mut self, n: i64) -> WriteI64Le<&mut Self>
where + Self: Unpin,

Writes an signed 64-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_u128_le(&mut self, n: u128) -> WriteU128Le<&mut Self>
where + Self: Unpin,

Writes an unsigned 128-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_i128_le(&mut self, n: i128) -> WriteI128Le<&mut Self>
where + Self: Unpin,

Writes an signed 128-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_f32_le(&mut self, n: f32) -> WriteF32Le<&mut Self>
where + Self: Unpin,

Writes an 32-bit floating point type in little-endian order to the +underlying writer. Read more
§

fn write_f64_le(&mut self, n: f64) -> WriteF64Le<&mut Self>
where + Self: Unpin,

Writes an 64-bit floating point type in little-endian order to the +underlying writer. Read more
§

fn flush(&mut self) -> Flush<'_, Self>
where + Self: Unpin,

Flushes this output stream, ensuring that all intermediately buffered +contents reach their destination. Read more
§

fn shutdown(&mut self) -> Shutdown<'_, Self>
where + Self: Unpin,

Shuts down the output stream, ensuring that the value can be dropped +cleanly. Read more
§

impl<W> AsyncWriteExt for W
where + W: AsyncWrite + ?Sized,

§

fn write<'a>(&'a mut self, buf: &'a [u8]) -> WriteFuture<'a, Self>
where + Self: Unpin,

Writes some bytes into the byte stream. Read more
§

fn write_vectored<'a>( + &'a mut self, + bufs: &'a [IoSlice<'a>], +) -> WriteVectoredFuture<'a, Self>
where + Self: Unpin,

Like [write()][AsyncWriteExt::write()], except that it writes a slice of buffers. Read more
§

fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAllFuture<'a, Self>
where + Self: Unpin,

Writes an entire buffer into the byte stream. Read more
§

fn flush(&mut self) -> FlushFuture<'_, Self>
where + Self: Unpin,

Flushes the stream to ensure that all buffered contents reach their destination. Read more
§

fn close(&mut self) -> CloseFuture<'_, Self>
where + Self: Unpin,

Closes the writer. Read more
§

fn boxed_writer<'a>(self) -> Pin<Box<dyn AsyncWrite + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the writer and changes its type to dyn AsyncWrite + Send + 'a. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.ServerConfig.html b/pr/395/docs/iroh_quinn/struct.ServerConfig.html new file mode 100644 index 000000000..065923e6e --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.ServerConfig.html @@ -0,0 +1,114 @@ +ServerConfig in iroh_quinn - Rust

ServerConfig

Struct ServerConfig 

Source
pub struct ServerConfig {
+    pub transport: Arc<TransportConfig>,
+    pub crypto: Arc<dyn ServerConfig>,
+    pub validation_token: ValidationTokenConfig,
+    /* private fields */
+}
Expand description

Parameters governing incoming connections

+

Default values should be suitable for most internet applications.

+

Fields§

§transport: Arc<TransportConfig>

Transport configuration to use for incoming connections

+
§crypto: Arc<dyn ServerConfig>

TLS configuration used for incoming connections

+

Must be set to use TLS 1.3 only.

+
§validation_token: ValidationTokenConfig

Configuration for sending and handling validation tokens

+

Implementations§

Source§

impl ServerConfig

Source

pub fn new( + crypto: Arc<dyn ServerConfig>, + token_key: Arc<dyn HandshakeTokenKey>, +) -> ServerConfig

Create a default config with a particular handshake token key

+
Source

pub fn transport_config( + &mut self, + transport: Arc<TransportConfig>, +) -> &mut ServerConfig

Set a custom TransportConfig

+
Source

pub fn validation_token_config( + &mut self, + validation_token: ValidationTokenConfig, +) -> &mut ServerConfig

Set a custom ValidationTokenConfig

+
Source

pub fn token_key( + &mut self, + value: Arc<dyn HandshakeTokenKey>, +) -> &mut ServerConfig

Private key used to authenticate data included in handshake tokens

+
Source

pub fn retry_token_lifetime(&mut self, value: Duration) -> &mut ServerConfig

Duration after a retry token was issued for which it’s considered valid

+

Defaults to 15 seconds.

+
Source

pub fn migration(&mut self, value: bool) -> &mut ServerConfig

Whether to allow clients to migrate to new addresses

+

Improves behavior for clients that move between different internet connections or suffer NAT +rebinding. Enabled by default.

+
Source

pub fn preferred_address_v4( + &mut self, + address: Option<SocketAddrV4>, +) -> &mut ServerConfig

The preferred IPv4 address that will be communicated to clients during handshaking

+

If the client is able to reach this address, it will switch to it.

+
Source

pub fn preferred_address_v6( + &mut self, + address: Option<SocketAddrV6>, +) -> &mut ServerConfig

The preferred IPv6 address that will be communicated to clients during handshaking

+

If the client is able to reach this address, it will switch to it.

+
Source

pub fn max_incoming(&mut self, max_incoming: usize) -> &mut ServerConfig

Maximum number of Incoming to allow to exist at a time

+

An Incoming comes into existence when an incoming connection attempt +is received and stops existing when the application either accepts it or otherwise disposes +of it. While this limit is reached, new incoming connection attempts are immediately +refused. Larger values have greater worst-case memory consumption, but accommodate greater +application latency in handling incoming connection attempts.

+

The default value is set to 65536. With a typical Ethernet MTU of 1500 bytes, this limits +memory consumption from this to under 100 MiB–a generous amount that still prevents memory +exhaustion in most contexts.

+
Source

pub fn incoming_buffer_size( + &mut self, + incoming_buffer_size: u64, +) -> &mut ServerConfig

Maximum number of received bytes to buffer for each Incoming

+

An Incoming comes into existence when an incoming connection attempt +is received and stops existing when the application either accepts it or otherwise disposes +of it. This limit governs only packets received within that period, and does not include +the first packet. Packets received in excess of this limit are dropped, which may cause +0-RTT or handshake data to have to be retransmitted.

+

The default value is set to 10 MiB–an amount such that in most situations a client would +not transmit that much 0-RTT data faster than the server handles the corresponding +Incoming.

+
Source

pub fn incoming_buffer_size_total( + &mut self, + incoming_buffer_size_total: u64, +) -> &mut ServerConfig

Maximum number of received bytes to buffer for all Incoming +collectively

+

An Incoming comes into existence when an incoming connection attempt +is received and stops existing when the application either accepts it or otherwise disposes +of it. This limit governs only packets received within that period, and does not include +the first packet. Packets received in excess of this limit are dropped, which may cause +0-RTT or handshake data to have to be retransmitted.

+

The default value is set to 100 MiB–a generous amount that still prevents memory +exhaustion in most contexts.

+
Source

pub fn time_source( + &mut self, + time_source: Arc<dyn TimeSource>, +) -> &mut ServerConfig

Object to get current SystemTime

+

This exists to allow system time to be mocked in tests, or wherever else desired.

+

Defaults to StdSystemTime, which simply calls SystemTime::now().

+
Source§

impl ServerConfig

Source

pub fn with_single_cert( + cert_chain: Vec<CertificateDer<'static>>, + key: PrivateKeyDer<'static>, +) -> Result<ServerConfig, Error>

Create a server config with the given certificate chain to be presented to clients

+

Uses a randomized handshake token key.

+
Source§

impl ServerConfig

Source

pub fn with_crypto(crypto: Arc<dyn ServerConfig>) -> ServerConfig

Create a server config with the given crypto::ServerConfig

+

Uses a randomized handshake token key.

+

Trait Implementations§

Source§

impl Clone for ServerConfig

Source§

fn clone(&self) -> ServerConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ServerConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.SmolRuntime.html b/pr/395/docs/iroh_quinn/struct.SmolRuntime.html new file mode 100644 index 000000000..147179d7f --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.SmolRuntime.html @@ -0,0 +1,25 @@ +SmolRuntime in iroh_quinn - Rust

SmolRuntime

Struct SmolRuntime 

Source
pub struct SmolRuntime;
Expand description

A Quinn runtime for smol

+

Trait Implementations§

Source§

impl Debug for SmolRuntime

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Runtime for SmolRuntime

Source§

fn new_timer(&self, t: Instant) -> Pin<Box<dyn AsyncTimer>>

Construct a timer that will expire at i
Source§

fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>)

Drive future to completion in the background
Source§

fn wrap_udp_socket(&self, sock: UdpSocket) -> Result<Box<dyn AsyncUdpSocket>>

Convert t into the socket type used by this runtime
Source§

fn now(&self) -> Instant

Look up the current time Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.StdSystemTime.html b/pr/395/docs/iroh_quinn/struct.StdSystemTime.html new file mode 100644 index 000000000..cb413dc52 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.StdSystemTime.html @@ -0,0 +1,26 @@ +StdSystemTime in iroh_quinn - Rust

StdSystemTime

Struct StdSystemTime 

Source
pub struct StdSystemTime;
Expand description

Default implementation of TimeSource

+

Implements now by calling SystemTime::now().

+

Trait Implementations§

Source§

impl TimeSource for StdSystemTime

Source§

fn now(&self) -> SystemTime

Get SystemTime::now() or the mocked equivalent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.StreamId.html b/pr/395/docs/iroh_quinn/struct.StreamId.html new file mode 100644 index 000000000..5f748d203 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.StreamId.html @@ -0,0 +1,55 @@ +StreamId in iroh_quinn - Rust

StreamId

Struct StreamId 

Source
pub struct StreamId(/* private fields */);
Expand description

Identifier for a stream within a particular connection

+

Implementations§

Source§

impl StreamId

Source

pub fn new(initiator: Side, dir: Dir, index: u64) -> StreamId

Create a new StreamId

+
Source

pub fn initiator(self) -> Side

Which side of a connection initiated the stream

+
Source

pub fn dir(self) -> Dir

Which directions data flows in

+
Source

pub fn index(self) -> u64

Distinguishes streams of the same initiator and directionality

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for StreamId

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<StreamId, Error>

Generate an arbitrary value of Self from the given unstructured data. Read more
§

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self, Error>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for StreamId

Source§

fn clone(&self) -> StreamId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for StreamId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Decodable for StreamId

Source§

fn decode<B>(buf: &mut B) -> Result<StreamId, UnexpectedEnd>
where + B: Buf,

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for StreamId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Encodable for StreamId

Source§

fn encode<B>(&self, buf: &mut B)
where + B: BufMut,

Append the encoding of self to the provided buffer.
Source§

impl From<StreamId> for VarInt

Source§

fn from(x: StreamId) -> VarInt

Converts to this type from the input type.
Source§

impl From<VarInt> for StreamId

Source§

fn from(v: VarInt) -> StreamId

Converts to this type from the input type.
Source§

impl Hash for StreamId

Source§

fn hash<__H>(&self, state: &mut __H)
where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for StreamId

Source§

fn cmp(&self, other: &StreamId) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for StreamId

Source§

fn eq(&self, other: &StreamId) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for StreamId

Source§

fn partial_cmp(&self, other: &StreamId) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for StreamId

Source§

impl Eq for StreamId

Source§

impl StructuralPartialEq for StreamId

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.TokenMemoryCache.html b/pr/395/docs/iroh_quinn/struct.TokenMemoryCache.html new file mode 100644 index 000000000..6a181a1d7 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.TokenMemoryCache.html @@ -0,0 +1,31 @@ +TokenMemoryCache in iroh_quinn - Rust

TokenMemoryCache

Struct TokenMemoryCache 

Source
pub struct TokenMemoryCache(/* private fields */);
Expand description

TokenStore implementation that stores up to N tokens per server name for up to a +limited number of server names, in-memory

+

Implementations§

Source§

impl TokenMemoryCache

Source

pub fn new( + max_server_names: u32, + max_tokens_per_server: usize, +) -> TokenMemoryCache

Construct empty

+

Trait Implementations§

Source§

impl Debug for TokenMemoryCache

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for TokenMemoryCache

Defaults to a maximum of 256 servers and 2 tokens per server

+
Source§

fn default() -> TokenMemoryCache

Returns the “default value” for a type. Read more
Source§

impl TokenStore for TokenMemoryCache

Source§

fn insert(&self, server_name: &str, token: Bytes)

Potentially store a token for later one-time use Read more
Source§

fn take(&self, server_name: &str) -> Option<Bytes>

Try to find and take a token that was stored with the given server name Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.TokenReuseError.html b/pr/395/docs/iroh_quinn/struct.TokenReuseError.html new file mode 100644 index 000000000..248825b81 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.TokenReuseError.html @@ -0,0 +1,25 @@ +TokenReuseError in iroh_quinn - Rust

TokenReuseError

Struct TokenReuseError 

Source
pub struct TokenReuseError;
Expand description

Error for when a validation token may have been reused

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.TokioRuntime.html b/pr/395/docs/iroh_quinn/struct.TokioRuntime.html new file mode 100644 index 000000000..f68f0fcfd --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.TokioRuntime.html @@ -0,0 +1,25 @@ +TokioRuntime in iroh_quinn - Rust

TokioRuntime

Struct TokioRuntime 

Source
pub struct TokioRuntime;
Expand description

A Quinn runtime for Tokio

+

Trait Implementations§

Source§

impl Debug for TokioRuntime

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Runtime for TokioRuntime

Source§

fn new_timer(&self, t: Instant) -> Pin<Box<dyn AsyncTimer>>

Construct a timer that will expire at i
Source§

fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>)

Drive future to completion in the background
Source§

fn wrap_udp_socket(&self, sock: UdpSocket) -> Result<Box<dyn AsyncUdpSocket>>

Convert t into the socket type used by this runtime
Source§

fn now(&self) -> Instant

Look up the current time Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.Transmit.html b/pr/395/docs/iroh_quinn/struct.Transmit.html new file mode 100644 index 000000000..aa5fafe62 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.Transmit.html @@ -0,0 +1,37 @@ +Transmit in iroh_quinn - Rust

Transmit

Struct Transmit 

Source
pub struct Transmit {
+    pub destination: SocketAddr,
+    pub ecn: Option<EcnCodepoint>,
+    pub size: usize,
+    pub segment_size: Option<usize>,
+    pub src_ip: Option<IpAddr>,
+}
Expand description

An outgoing packet

+

Fields§

§destination: SocketAddr

The socket this datagram should be sent to

+
§ecn: Option<EcnCodepoint>

Explicit congestion notification bits to set on the packet

+
§size: usize

Amount of data written to the caller-supplied buffer

+
§segment_size: Option<usize>

The segment size if this transmission contains multiple datagrams. +This is None if the transmit only contains a single datagram

+
§src_ip: Option<IpAddr>

Optional source IP address for the datagram

+

Trait Implementations§

Source§

impl Debug for Transmit

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.TransportConfig.html b/pr/395/docs/iroh_quinn/struct.TransportConfig.html new file mode 100644 index 000000000..5ffbc1999 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.TransportConfig.html @@ -0,0 +1,252 @@ +TransportConfig in iroh_quinn - Rust

TransportConfig

Struct TransportConfig 

Source
pub struct TransportConfig { /* private fields */ }
Expand description

Parameters governing the core QUIC state machine

+

Default values should be suitable for most internet applications. Applications protocols which +forbid remotely-initiated streams should set max_concurrent_bidi_streams and +max_concurrent_uni_streams to zero.

+

In some cases, performance or resource requirements can be improved by tuning these values to +suit a particular application and/or network connection. In particular, data window sizes can be +tuned for a particular expected round trip time, link capacity, and memory availability. Tuning +for higher bandwidths and latencies increases worst-case memory consumption, but does not impair +performance at lower bandwidths and latencies. The default configuration is tuned for a 100Mbps +link with a 100ms round trip time.

+

Implementations§

Source§

impl TransportConfig

Source

pub fn max_concurrent_bidi_streams( + &mut self, + value: VarInt, +) -> &mut TransportConfig

Maximum number of incoming bidirectional streams that may be open concurrently

+

Must be nonzero for the peer to open any bidirectional streams.

+

Worst-case memory use is directly proportional to max_concurrent_bidi_streams * stream_receive_window, with an upper bound proportional to receive_window.

+
Source

pub fn max_concurrent_uni_streams( + &mut self, + value: VarInt, +) -> &mut TransportConfig

Variant of max_concurrent_bidi_streams affecting unidirectional streams

+
Source

pub fn max_idle_timeout( + &mut self, + value: Option<IdleTimeout>, +) -> &mut TransportConfig

Maximum duration of inactivity to accept before timing out the connection.

+

The true idle timeout is the minimum of this and the peer’s own max idle timeout. None +represents an infinite timeout. Defaults to 30 seconds.

+

WARNING: If a peer or its network path malfunctions or acts maliciously, an infinite +idle timeout can result in permanently hung futures!

+ +
let mut config = TransportConfig::default();
+
+// Set the idle timeout as `VarInt`-encoded milliseconds
+config.max_idle_timeout(Some(VarInt::from_u32(10_000).into()));
+
+// Set the idle timeout as a `Duration`
+config.max_idle_timeout(Some(Duration::from_secs(10).try_into()?));
Source

pub fn stream_receive_window(&mut self, value: VarInt) -> &mut TransportConfig

Maximum number of bytes the peer may transmit without acknowledgement on any one stream +before becoming blocked.

+

This should be set to at least the expected connection latency multiplied by the maximum +desired throughput. Setting this smaller than receive_window helps ensure that a single +stream doesn’t monopolize receive buffers, which may otherwise occur if the application +chooses not to read from a large stream for a time while still requiring data on other +streams.

+
Source

pub fn receive_window(&mut self, value: VarInt) -> &mut TransportConfig

Maximum number of bytes the peer may transmit across all streams of a connection before +becoming blocked.

+

This should be set to at least the expected connection latency multiplied by the maximum +desired throughput. Larger values can be useful to allow maximum throughput within a +stream while another is blocked.

+
Source

pub fn send_window(&mut self, value: u64) -> &mut TransportConfig

Maximum number of bytes to transmit to a peer without acknowledgment

+

Provides an upper bound on memory when communicating with peers that issue large amounts of +flow control credit. Endpoints that wish to handle large numbers of connections robustly +should take care to set this low enough to guarantee memory exhaustion does not occur if +every connection uses the entire window.

+
Source

pub fn send_fairness(&mut self, value: bool) -> &mut TransportConfig

Whether to implement fair queuing for send streams having the same priority.

+

When enabled, connections schedule data from outgoing streams having the same priority in a +round-robin fashion. When disabled, streams are scheduled in the order they are written to.

+

Note that this only affects streams with the same priority. Higher priority streams always +take precedence over lower priority streams.

+

Disabling fairness can reduce fragmentation and protocol overhead for workloads that use +many small streams.

+
Source

pub fn packet_threshold(&mut self, value: u32) -> &mut TransportConfig

Maximum reordering in packet number space before FACK style loss detection considers a +packet lost. Should not be less than 3, per RFC5681.

+
Source

pub fn time_threshold(&mut self, value: f32) -> &mut TransportConfig

Maximum reordering in time space before time based loss detection considers a packet lost, +as a factor of RTT

+
Source

pub fn initial_rtt(&mut self, value: Duration) -> &mut TransportConfig

The RTT used before an RTT sample is taken

+
Source

pub fn initial_mtu(&mut self, value: u16) -> &mut TransportConfig

The initial value to be used as the maximum UDP payload size before running MTU discovery +(see TransportConfig::mtu_discovery_config).

+

Must be at least 1200, which is the default, and known to be safe for typical internet +applications. Larger values are more efficient, but increase the risk of packet loss due to +exceeding the network path’s IP MTU. If the provided value is higher than what the network +path actually supports, packet loss will eventually trigger black hole detection and bring +it down to TransportConfig::min_mtu.

+
Source

pub fn min_mtu(&mut self, value: u16) -> &mut TransportConfig

The maximum UDP payload size guaranteed to be supported by the network.

+

Must be at least 1200, which is the default, and lower than or equal to +TransportConfig::initial_mtu.

+

Real-world MTUs can vary according to ISP, VPN, and properties of intermediate network links +outside of either endpoint’s control. Extreme care should be used when raising this value +outside of private networks where these factors are fully controlled. If the provided value +is higher than what the network path actually supports, the result will be unpredictable and +catastrophic packet loss, without a possibility of repair. Prefer +TransportConfig::initial_mtu together with +TransportConfig::mtu_discovery_config to set a maximum UDP payload size that robustly +adapts to the network.

+
Source

pub fn mtu_discovery_config( + &mut self, + value: Option<MtuDiscoveryConfig>, +) -> &mut TransportConfig

Specifies the MTU discovery config (see MtuDiscoveryConfig for details).

+

Enabled by default.

+
Source

pub fn pad_to_mtu(&mut self, value: bool) -> &mut TransportConfig

Pad UDP datagrams carrying application data to current maximum UDP payload size

+

Disabled by default. UDP datagrams containing loss probes are exempt from padding.

+

Enabling this helps mitigate traffic analysis by network observers, but it increases +bandwidth usage. Without this mitigation precise plain text size of application datagrams as +well as the total size of stream write bursts can be inferred by observers under certain +conditions. This analysis requires either an uncongested connection or application datagrams +too large to be coalesced.

+
Source

pub fn ack_frequency_config( + &mut self, + value: Option<AckFrequencyConfig>, +) -> &mut TransportConfig

Specifies the ACK frequency config (see AckFrequencyConfig for details)

+

The provided configuration will be ignored if the peer does not support the acknowledgement +frequency QUIC extension.

+

Defaults to None, which disables controlling the peer’s acknowledgement frequency. Even +if set to None, the local side still supports the acknowledgement frequency QUIC +extension and may use it in other ways.

+
Source

pub fn persistent_congestion_threshold( + &mut self, + value: u32, +) -> &mut TransportConfig

Number of consecutive PTOs after which network is considered to be experiencing persistent congestion.

+
Source

pub fn keep_alive_interval( + &mut self, + value: Option<Duration>, +) -> &mut TransportConfig

Period of inactivity before sending a keep-alive packet

+

Keep-alive packets prevent an inactive but otherwise healthy connection from timing out.

+

None to disable, which is the default. Only one side of any given connection needs keep-alive +enabled for the connection to be preserved. Must be set lower than the idle_timeout of both +peers to be effective.

+
Source

pub fn crypto_buffer_size(&mut self, value: usize) -> &mut TransportConfig

Maximum quantity of out-of-order crypto layer data to buffer

+
Source

pub fn allow_spin(&mut self, value: bool) -> &mut TransportConfig

Whether the implementation is permitted to set the spin bit on this connection

+

This allows passive observers to easily judge the round trip time of a connection, which can +be useful for network administration but sacrifices a small amount of privacy.

+
Source

pub fn datagram_receive_buffer_size( + &mut self, + value: Option<usize>, +) -> &mut TransportConfig

Maximum number of incoming application datagram bytes to buffer, or None to disable +incoming datagrams

+

The peer is forbidden to send single datagrams larger than this size. If the aggregate size +of all datagrams that have been received from the peer but not consumed by the application +exceeds this value, old datagrams are dropped until it is no longer exceeded.

+
Source

pub fn datagram_send_buffer_size( + &mut self, + value: usize, +) -> &mut TransportConfig

Maximum number of outgoing application datagram bytes to buffer

+

While datagrams are sent ASAP, it is possible for an application to generate data faster +than the link, or even the underlying hardware, can transmit them. This limits the amount of +memory that may be consumed in that case. When the send buffer is full and a new datagram is +sent, older datagrams are dropped until sufficient space is available.

+
Source

pub fn congestion_controller_factory( + &mut self, + factory: Arc<dyn ControllerFactory + Send + Sync>, +) -> &mut TransportConfig

How to construct new congestion::Controllers

+

Typically the refcounted configuration of a congestion::Controller, +e.g. a congestion::NewRenoConfig.

+
§Example
+
let mut config = TransportConfig::default();
+config.congestion_controller_factory(Arc::new(congestion::NewRenoConfig::default()));
Source

pub fn enable_segmentation_offload( + &mut self, + enabled: bool, +) -> &mut TransportConfig

Whether to use “Generic Segmentation Offload” to accelerate transmits, when supported by the +environment

+

Defaults to true.

+

GSO dramatically reduces CPU consumption when sending large numbers of packets with the same +headers, such as when transmitting bulk data on a connection. However, it is not supported +by all network interface drivers or packet inspection tools. quinn-udp will attempt to +disable GSO automatically when unavailable, but this can lead to spurious packet loss at +startup, temporarily degrading performance.

+
Source

pub fn send_observed_address_reports( + &mut self, + enabled: bool, +) -> &mut TransportConfig

Whether to send observed address reports to peers.

+

This will aid peers in inferring their reachable address, which in most NATd networks +will not be easily available to them.

+
Source

pub fn receive_observed_address_reports( + &mut self, + enabled: bool, +) -> &mut TransportConfig

Whether to receive observed address reports from other peers.

+

Peers with the address discovery extension enabled that are willing to provide observed +address reports will do so if this transport parameter is set. In general, observed address +reports cannot be trusted. This, however, can aid the current endpoint in inferring its +reachable address, which in most NATd networks will not be easily available.

+
Source

pub fn max_concurrent_multipath_paths( + &mut self, + max_concurrent: u32, +) -> &mut TransportConfig

Enables the Multipath Extension for QUIC.

+

Setting this to any nonzero value will enable the Multipath Extension for QUIC, +https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/.

+

The value provided specifies the number maximum number of paths this endpoint may open +concurrently when multipath is negotiated. For any path to be opened, the remote must +enable multipath as well.

+
Source

pub fn default_path_max_idle_timeout( + &mut self, + timeout: Option<Duration>, +) -> &mut TransportConfig

Sets a default per-path maximum idle timeout

+

If the path is idle for this long the path will be abandoned. Bear in mind this will +interact with the TransportConfig::max_idle_timeout, if the last path is +abandoned the entire connection will be closed.

+

You can also change this using Connection::set_path_max_idle_timeout for +existing paths.

+
Source

pub fn default_path_keep_alive_interval( + &mut self, + interval: Option<Duration>, +) -> &mut TransportConfig

Sets a default per-path keep alive interval

+

Note that this does not interact with the connection-wide +TransportConfig::keep_alive_interval. This setting will keep this path active, +TransportConfig::keep_alive_interval will keep the connection active, with no +control over which path is used for this.

+

You can also change this using Connection::set_path_keep_alive_interval for +existing path.

+
Source

pub fn set_max_remote_nat_traversal_addresses( + &mut self, + max_addresses: u8, +) -> &mut TransportConfig

Sets the maximum number of nat traversal addresses this endpoint allows the remote to +advertise

+

Setting this to any nonzero value will enable Iroh’s holepunching, loosely based in the Nat +Traversal Extension for QUIC, see +https://www.ietf.org/archive/id/draft-seemann-quic-nat-traversal-02.html

+

This implementation expects the multipath extension to be enabled as well. if not yet +enabled via Self::max_concurrent_multipath_paths, a default value of +[DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED] will be used.

+
Source

pub fn qlog_factory( + &mut self, + factory: Arc<dyn QlogFactory>, +) -> &mut TransportConfig

Configures qlog capturing by setting a QlogFactory.

+

This assigns a QlogFactory that produces qlog capture configurations for +individual connections.

+
Source

pub fn qlog_from_env(&mut self, prefix: &str) -> &mut TransportConfig

Configures qlog capturing through the QLOGDIR environment variable.

+

This uses QlogFileFactory::from_env to create a factory to write qlog traces +into the directory set through the QLOGDIR environment variable.

+

If QLOGDIR is not set, no traces will be written. If QLOGDIR is set to a path +that does not exist, it will be created.

+

The files will be prefixed with prefix.

+
Source

pub fn qlog_from_path( + &mut self, + path: impl AsRef<Path>, + prefix: &str, +) -> &mut TransportConfig

Configures qlog capturing into a directory.

+

This uses QlogFileFactory to create a factory to write qlog traces into +the specified directory. The files will be prefixed with prefix.

+

Trait Implementations§

Source§

impl Clone for TransportConfig

Source§

fn clone(&self) -> TransportConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TransportConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for TransportConfig

Source§

fn default() -> TransportConfig

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.TransportErrorCode.html b/pr/395/docs/iroh_quinn/struct.TransportErrorCode.html new file mode 100644 index 000000000..e2c9df35b --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.TransportErrorCode.html @@ -0,0 +1,57 @@ +TransportErrorCode in iroh_quinn - Rust

TransportErrorCode

Struct TransportErrorCode 

Source
pub struct TransportErrorCode(/* private fields */);
Expand description

Transport-level error code

+

Implementations§

Source§

impl Code

Source

pub fn crypto(code: u8) -> Code

Create QUIC error code from TLS alert code

+
Source§

impl Code

Source

pub const NO_ERROR: Code

the connection is being closed abruptly in the absence of any error

+
Source

pub const INTERNAL_ERROR: Code

the endpoint encountered an internal error and cannot continue with the connection

+
Source

pub const CONNECTION_REFUSED: Code

the server refused to accept a new connection

+
Source

pub const FLOW_CONTROL_ERROR: Code

received more data than permitted in advertised data limits

+
Source

pub const STREAM_LIMIT_ERROR: Code

received a frame for a stream identifier that exceeded advertised the stream limit for the corresponding stream type

+
Source

pub const STREAM_STATE_ERROR: Code

received a frame for a stream that was not in a state that permitted that frame

+
Source

pub const FINAL_SIZE_ERROR: Code

received a STREAM frame or a RESET_STREAM frame containing a different final size to the one already established

+
Source

pub const FRAME_ENCODING_ERROR: Code

received a frame that was badly formatted

+
Source

pub const TRANSPORT_PARAMETER_ERROR: Code

received transport parameters that were badly formatted, included an invalid value, was absent even though it is mandatory, was present though it is forbidden, or is otherwise in error

+
Source

pub const CONNECTION_ID_LIMIT_ERROR: Code

the number of connection IDs provided by the peer exceeds the advertised active_connection_id_limit

+
Source

pub const PROTOCOL_VIOLATION: Code

detected an error with protocol compliance that was not covered by more specific error codes

+
Source

pub const INVALID_TOKEN: Code

received an invalid Retry Token in a client Initial

+
Source

pub const APPLICATION_ERROR: Code

the application or application protocol caused the connection to be closed during the handshake

+
Source

pub const CRYPTO_BUFFER_EXCEEDED: Code

received more data in CRYPTO frames than can be buffered

+
Source

pub const KEY_UPDATE_ERROR: Code

key update error

+
Source

pub const AEAD_LIMIT_REACHED: Code

the endpoint has reached the confidentiality or integrity limit for the AEAD algorithm

+
Source

pub const NO_VIABLE_PATH: Code

no viable network path exists

+
Source

pub const APPLICATION_ABANDON_PATH: Code

Path abandoned at the application’s request

+
Source

pub const PATH_RESOURCE_LIMIT_REACHED: Code

Path abandoned due to resource limitations in the transport

+
Source

pub const PATH_UNSTABLE_OR_POOR: Code

Path abandoned due to unstable interfaces

+
Source

pub const NO_CID_AVAILABLE_FOR_PATH: Code

Path abandoned due to no available connection IDs for the path

+

Trait Implementations§

Source§

impl Clone for Code

Source§

fn clone(&self) -> Code

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Code

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Decodable for Code

Source§

fn decode<B>(buf: &mut B) -> Result<Code, UnexpectedEnd>
where + B: Buf,

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for Code

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Encodable for Code

Source§

fn encode<B>(&self, buf: &mut B)
where + B: BufMut,

Append the encoding of self to the provided buffer.
Source§

impl From<Code> for VarInt

Source§

fn from(value: Code) -> VarInt

Converts to this type from the input type.
Source§

impl From<VarInt> for Code

Source§

fn from(value: VarInt) -> Code

Converts to this type from the input type.
Source§

impl PartialEq for Code

Source§

fn eq(&self, other: &Code) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for Code

Source§

impl Eq for Code

Source§

impl StructuralPartialEq for Code

Auto Trait Implementations§

§

impl Freeze for Code

§

impl RefUnwindSafe for Code

§

impl Send for Code

§

impl Sync for Code

§

impl Unpin for Code

§

impl UnwindSafe for Code

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.UdpStats.html b/pr/395/docs/iroh_quinn/struct.UdpStats.html new file mode 100644 index 000000000..5382935fc --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.UdpStats.html @@ -0,0 +1,41 @@ +UdpStats in iroh_quinn - Rust

UdpStats

Struct UdpStats 

Source
#[non_exhaustive]
pub struct UdpStats { + pub datagrams: u64, + pub bytes: u64, + pub ios: u64, +}
Expand description

Statistics about UDP datagrams transmitted or received on a connection

+

All QUIC packets are carried by UDP datagrams. Hence, these statistics cover all traffic on a connection.

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§datagrams: u64

The amount of UDP datagrams observed

+
§bytes: u64

The total amount of bytes which have been transferred inside UDP datagrams

+
§ios: u64

The amount of I/O operations executed

+

Can be less than datagrams when GSO, GRO, and/or batched system calls are in use.

+

Trait Implementations§

Source§

impl Clone for UdpStats

Source§

fn clone(&self) -> UdpStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for UdpStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for UdpStats

Source§

fn default() -> UdpStats

Returns the “default value” for a type. Read more
Source§

impl PartialEq for UdpStats

Source§

fn eq(&self, other: &UdpStats) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for UdpStats

Source§

impl Eq for UdpStats

Source§

impl StructuralPartialEq for UdpStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.UnorderedRecvStream.html b/pr/395/docs/iroh_quinn/struct.UnorderedRecvStream.html new file mode 100644 index 000000000..28f9ef0c8 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.UnorderedRecvStream.html @@ -0,0 +1,54 @@ +UnorderedRecvStream in iroh_quinn - Rust

UnorderedRecvStream

Struct UnorderedRecvStream 

Source
pub struct UnorderedRecvStream { /* private fields */ }
Expand description

A stream that can be used to receive data out-of-order.

+

Obtained by converting a RecvStream via RecvStream::into_unordered.

+

This variant of RecvStream allows reading chunks of data exclusively +out of order. Once you have done an unordered read, ordered reads are no +longer possible since data may have been consumed out of order.

+

The stream state related fns like Self::id, Self::is_0rtt, Self::stop, and +Self::received_reset behave exactly as on RecvStream.

+

Implementations§

Source§

impl UnorderedRecvStream

Source

pub async fn read_chunk( + &mut self, + max_length: usize, +) -> Result<Option<Chunk>, ReadError>

Reads the next segment of data.

+

Yields None if the stream was finished. Otherwise, yields a segment of data and its +offset in the stream. Segments may be received in any order, and the Chunk’s offset +field can be used to determine ordering in the caller. Unordered reads are less prone +to head-of-line blocking within a stream, but require the application to manage +reassembling the original data.

+

This operation is cancel-safe.

+
Source

pub fn id(&self) -> StreamId

Get the identity of this stream

+
Source

pub fn is_0rtt(&self) -> bool

Check if this stream has been opened during 0-RTT.

+

In which case any non-idempotent request should be considered dangerous at the application +level. Because read data is subject to replay attacks.

+
Source

pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream>

Stop accepting data

+

Discards unread data and notifies the peer to stop transmitting. Once stopped, further +attempts to operate on a stream will yield ClosedStream errors.

+
Source

pub async fn received_reset(&mut self) -> Result<Option<VarInt>, ResetError>

Completes when the stream has been reset by the peer or otherwise closed

+

Yields Some with the reset error code when the stream is reset by the peer. Yields None +when the stream was previously stop()ed, or when the stream was +finish()ed by the peer and all data has been received, after +which it is no longer meaningful for the stream to be reset.

+

This operation is cancel-safe.

+

Trait Implementations§

Source§

impl Debug for UnorderedRecvStream

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.ValidationTokenConfig.html b/pr/395/docs/iroh_quinn/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..f5ce4e487 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.ValidationTokenConfig.html @@ -0,0 +1,74 @@ +ValidationTokenConfig in iroh_quinn - Rust

ValidationTokenConfig

Struct ValidationTokenConfig 

Source
pub struct ValidationTokenConfig { /* private fields */ }
Expand description

Configuration for sending and handling validation tokens in incoming connections

+

Default values should be suitable for most internet applications.

+

§QUIC Tokens

+

The QUIC protocol defines a concept of “address validation”. Essentially, one side of a +QUIC connection may appear to be receiving QUIC packets from a particular remote UDP address, +but it will only consider that remote address “validated” once it has convincing evidence that +the address is not being spoofed.

+

Validation is important primarily because of QUIC’s “anti-amplification limit.” This limit +prevents a QUIC server from sending a client more than three times the number of bytes it has +received from the client on a given address until that address is validated. This is designed +to mitigate the ability of attackers to use QUIC-based servers as reflectors in amplification +attacks.

+

A path may become validated in several ways. The server is always considered validated by the +client. The client usually begins in an unvalidated state upon first connecting or migrating, +but then becomes validated through various mechanisms that usually take one network round trip. +However, in some cases, a client which has previously attempted to connect to a server may have +been given a one-time use cryptographically secured “token” that it can send in a subsequent +connection attempt to be validated immediately.

+

There are two ways these tokens can originate:

+
    +
  • If the server responds to an incoming connection with retry, a “retry token” is minted and +sent to the client, which the client immediately uses to attempt to connect again. Retry +tokens operate on short timescales, such as 15 seconds.
  • +
  • If a client’s path within an active connection is validated, the server may send the client +one or more “validation tokens,” which the client may store for use in later connections to +the same server. Validation tokens may be valid for much longer lifetimes than retry token.
  • +
+

The usage of validation tokens is most impactful in situations where 0-RTT data is also being +used–in particular, in situations where the server sends the client more than three times more +0.5-RTT data than it has received 0-RTT data. Since the successful completion of a connection +handshake implicitly causes the client’s address to be validated, transmission of 0.5-RTT data +is the main situation where a server might be sending application data to an address that could +be validated by token usage earlier than it would become validated without token usage.

+

These tokens should not be confused with “stateless reset tokens,” which are similarly named +but entirely unrelated.

+

Implementations§

Source§

impl ValidationTokenConfig

Source

pub fn lifetime(&mut self, value: Duration) -> &mut ValidationTokenConfig

Duration after an address validation token was issued for which it’s considered valid

+

This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.

+

Defaults to 2 weeks.

+
Source

pub fn log(&mut self, log: Arc<dyn TokenLog>) -> &mut ValidationTokenConfig

Set a custom TokenLog

+

If the bloom feature is enabled (which it is by default), defaults to a default +BloomTokenLog, which is suitable for most internet applications.

+

If the bloom feature is disabled, defaults to NoneTokenLog, +which makes the server ignore all address validation tokens (that is, tokens originating +from NEW_TOKEN frames–retry tokens are not affected).

+
Source

pub fn sent(&mut self, value: u32) -> &mut ValidationTokenConfig

Number of address validation tokens sent to a client when its path is validated

+

This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.

+

If the bloom feature is enabled (which it is by default), defaults to 2. Otherwise, +defaults to 0.

+

Trait Implementations§

Source§

impl Clone for ValidationTokenConfig

Source§

fn clone(&self) -> ValidationTokenConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ValidationTokenConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for ValidationTokenConfig

Source§

fn default() -> ValidationTokenConfig

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.VarInt.html b/pr/395/docs/iroh_quinn/struct.VarInt.html new file mode 100644 index 000000000..1511dbd2c --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.VarInt.html @@ -0,0 +1,65 @@ +VarInt in iroh_quinn - Rust

VarInt

Struct VarInt 

Source
pub struct VarInt(/* private fields */);
Expand description

An integer less than 2^62

+

Values of this type are suitable for encoding as QUIC variable-length integer.

+

Implementations§

Source§

impl VarInt

Source

pub const MAX: VarInt

The largest representable value

+
Source

pub const MAX_SIZE: usize = 8usize

The largest encoded value length

+
Source

pub const fn from_u32(x: u32) -> VarInt

Construct a VarInt infallibly

+
Source

pub fn from_u64(x: u64) -> Result<VarInt, VarIntBoundsExceeded>

Succeeds iff x < 2^62

+
Source

pub const unsafe fn from_u64_unchecked(x: u64) -> VarInt

Create a VarInt without ensuring it’s in range

+
§Safety
+

x must be less than 2^62.

+
Source

pub const fn into_inner(self) -> u64

Extract the integer value

+
Source

pub fn saturating_add(self, rhs: impl Into<VarInt>) -> VarInt

Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead +of overflowing.

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for VarInt

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<VarInt, Error>

Generate an arbitrary value of Self from the given unstructured data. Read more
§

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self, Error>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for VarInt

Source§

fn clone(&self) -> VarInt

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for VarInt

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Decodable for VarInt

Source§

fn decode<B>(r: &mut B) -> Result<VarInt, UnexpectedEnd>
where + B: Buf,

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Default for VarInt

Source§

fn default() -> VarInt

Returns the “default value” for a type. Read more
Source§

impl Display for VarInt

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Encodable for VarInt

Source§

fn encode<B>(&self, w: &mut B)
where + B: BufMut,

Append the encoding of self to the provided buffer.
Source§

impl From<StreamId> for VarInt

Source§

fn from(x: StreamId) -> VarInt

Converts to this type from the input type.
Source§

impl From<Code> for VarInt

Source§

fn from(value: Code) -> VarInt

Converts to this type from the input type.
Source§

impl From<VarInt> for IdleTimeout

Source§

fn from(inner: VarInt) -> IdleTimeout

Converts to this type from the input type.
Source§

impl From<VarInt> for StreamId

Source§

fn from(v: VarInt) -> StreamId

Converts to this type from the input type.
Source§

impl From<VarInt> for Code

Source§

fn from(value: VarInt) -> Code

Converts to this type from the input type.
Source§

impl From<u16> for VarInt

Source§

fn from(x: u16) -> VarInt

Converts to this type from the input type.
Source§

impl From<u32> for VarInt

Source§

fn from(x: u32) -> VarInt

Converts to this type from the input type.
Source§

impl From<u8> for VarInt

Source§

fn from(x: u8) -> VarInt

Converts to this type from the input type.
Source§

impl Hash for VarInt

Source§

fn hash<__H>(&self, state: &mut __H)
where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for VarInt

Source§

fn cmp(&self, other: &VarInt) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for VarInt

Source§

fn eq(&self, other: &VarInt) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for VarInt

Source§

fn partial_cmp(&self, other: &VarInt) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl TryFrom<u128> for VarInt

Source§

fn try_from(x: u128) -> Result<VarInt, VarIntBoundsExceeded>

Succeeds iff x < 2^62

+
Source§

type Error = VarIntBoundsExceeded

The type returned in the event of a conversion error.
Source§

impl TryFrom<u64> for VarInt

Source§

fn try_from(x: u64) -> Result<VarInt, VarIntBoundsExceeded>

Succeeds iff x < 2^62

+
Source§

type Error = VarIntBoundsExceeded

The type returned in the event of a conversion error.
Source§

impl TryFrom<usize> for VarInt

Source§

fn try_from(x: usize) -> Result<VarInt, VarIntBoundsExceeded>

Succeeds iff x < 2^62

+
Source§

type Error = VarIntBoundsExceeded

The type returned in the event of a conversion error.
Source§

impl Copy for VarInt

Source§

impl Eq for VarInt

Source§

impl StructuralPartialEq for VarInt

Auto Trait Implementations§

§

impl Freeze for VarInt

§

impl RefUnwindSafe for VarInt

§

impl Send for VarInt

§

impl Sync for VarInt

§

impl Unpin for VarInt

§

impl UnwindSafe for VarInt

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.VarIntBoundsExceeded.html b/pr/395/docs/iroh_quinn/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..5c31c8ad0 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.VarIntBoundsExceeded.html @@ -0,0 +1,33 @@ +VarIntBoundsExceeded in iroh_quinn - Rust

VarIntBoundsExceeded

Struct VarIntBoundsExceeded 

Source
pub struct VarIntBoundsExceeded;
Expand description

Error returned when constructing a VarInt from a value >= 2^62

+

Trait Implementations§

Source§

impl Clone for VarIntBoundsExceeded

Source§

fn clone(&self) -> VarIntBoundsExceeded

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for VarIntBoundsExceeded

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for VarIntBoundsExceeded

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for VarIntBoundsExceeded

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<VarIntBoundsExceeded> for ConfigError

Source§

fn from(_: VarIntBoundsExceeded) -> ConfigError

Converts to this type from the input type.
Source§

impl PartialEq for VarIntBoundsExceeded

Source§

fn eq(&self, other: &VarIntBoundsExceeded) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for VarIntBoundsExceeded

Source§

impl Eq for VarIntBoundsExceeded

Source§

impl StructuralPartialEq for VarIntBoundsExceeded

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.WeakConnectionHandle.html b/pr/395/docs/iroh_quinn/struct.WeakConnectionHandle.html new file mode 100644 index 000000000..6c95c8486 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.WeakConnectionHandle.html @@ -0,0 +1,31 @@ +WeakConnectionHandle in iroh_quinn - Rust

WeakConnectionHandle

Struct WeakConnectionHandle 

Source
pub struct WeakConnectionHandle(/* private fields */);
Expand description

A handle to some connection internals, use with care.

+

This contains a weak reference to the connection so will not itself keep the connection +alive.

+

Implementations§

Source§

impl WeakConnectionHandle

Source

pub fn is_alive(&self) -> bool

Returns true if the Connection associated with this handle is still alive.

+
Source

pub fn upgrade(&self) -> Option<Connection>

Upgrade the handle to a full Connection

+

Trait Implementations§

Source§

impl Clone for WeakConnectionHandle

Source§

fn clone(&self) -> WeakConnectionHandle

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for WeakConnectionHandle

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.Written.html b/pr/395/docs/iroh_quinn/struct.Written.html new file mode 100644 index 000000000..74761fd84 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.Written.html @@ -0,0 +1,38 @@ +Written in iroh_quinn - Rust

Written

Struct Written 

Source
pub struct Written {
+    pub bytes: usize,
+    pub chunks: usize,
+}
Expand description

Indicates how many bytes and chunks had been transferred in a write operation

+

Fields§

§bytes: usize

The amount of bytes which had been written

+
§chunks: usize

The amount of full chunks which had been written

+

If a chunk was only partially written, it will not be counted by this field.

+

Trait Implementations§

Source§

impl Clone for Written

Source§

fn clone(&self) -> Written

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Written

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for Written

Source§

fn default() -> Written

Returns the “default value” for a type. Read more
Source§

impl PartialEq for Written

Source§

fn eq(&self, other: &Written) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for Written

Source§

impl Eq for Written

Source§

impl StructuralPartialEq for Written

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/struct.ZeroRttAccepted.html b/pr/395/docs/iroh_quinn/struct.ZeroRttAccepted.html new file mode 100644 index 000000000..d1b4ac827 --- /dev/null +++ b/pr/395/docs/iroh_quinn/struct.ZeroRttAccepted.html @@ -0,0 +1,51 @@ +ZeroRttAccepted in iroh_quinn - Rust

ZeroRttAccepted

Struct ZeroRttAccepted 

Source
pub struct ZeroRttAccepted(/* private fields */);
Expand description

Future that completes when a connection is fully established

+

For clients, the resulting value indicates if 0-RTT was accepted. For servers, the resulting +value is meaningless.

+

Trait Implementations§

Source§

impl Future for ZeroRttAccepted

Source§

type Output = bool

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/trait.AsyncTimer.html b/pr/395/docs/iroh_quinn/trait.AsyncTimer.html new file mode 100644 index 000000000..360d5345a --- /dev/null +++ b/pr/395/docs/iroh_quinn/trait.AsyncTimer.html @@ -0,0 +1,11 @@ +AsyncTimer in iroh_quinn - Rust

AsyncTimer

Trait AsyncTimer 

Source
pub trait AsyncTimer:
+    Send
+    + Debug
+    + 'static {
+    // Required methods
+    fn reset(self: Pin<&mut Self>, i: Instant);
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()>;
+}
Expand description

Abstract implementation of an async timer for runtime independence

+

Required Methods§

Source

fn reset(self: Pin<&mut Self>, i: Instant)

Update the timer to expire at i

+
Source

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()>

Check whether the timer has expired, and register to be woken if not

+

Implementations on Foreign Types§

Source§

impl AsyncTimer for Sleep

Source§

fn reset(self: Pin<&mut Self>, t: Instant)

Source§

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()>

Source§

impl AsyncTimer for Timer

Source§

fn reset(self: Pin<&mut Self>, t: Instant)

Source§

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()>

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/trait.AsyncUdpSocket.html b/pr/395/docs/iroh_quinn/trait.AsyncUdpSocket.html new file mode 100644 index 000000000..073e35161 --- /dev/null +++ b/pr/395/docs/iroh_quinn/trait.AsyncUdpSocket.html @@ -0,0 +1,38 @@ +AsyncUdpSocket in iroh_quinn - Rust

AsyncUdpSocket

Trait AsyncUdpSocket 

Source
pub trait AsyncUdpSocket:
+    Send
+    + Sync
+    + Debug
+    + 'static {
+    // Required methods
+    fn create_sender(&self) -> Pin<Box<dyn UdpSender>>;
+    fn poll_recv(
+        &mut self,
+        cx: &mut Context<'_>,
+        bufs: &mut [IoSliceMut<'_>],
+        meta: &mut [RecvMeta],
+    ) -> Poll<Result<usize>>;
+    fn local_addr(&self) -> Result<SocketAddr>;
+
+    // Provided methods
+    fn max_receive_segments(&self) -> NonZeroUsize { ... }
+    fn may_fragment(&self) -> bool { ... }
+}
Expand description

Abstract implementation of a UDP socket for runtime independence

+

Required Methods§

Source

fn create_sender(&self) -> Pin<Box<dyn UdpSender>>

Create a UdpSender that can register a single task for write-readiness notifications +and send a transmit, if ready.

+

A poll_send method on a single object can usually store only one Waker at a time, +i.e. allow at most one caller to wait for an event. This method allows any number of +interested tasks to construct their own UdpSender object. They can all then wait for the +same event and be notified concurrently, because each UdpSender can store a separate +Waker.

+
Source

fn poll_recv( + &mut self, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + meta: &mut [RecvMeta], +) -> Poll<Result<usize>>

Receive UDP datagrams, or register to be woken if receiving may succeed in the future

+
Source

fn local_addr(&self) -> Result<SocketAddr>

Look up the local IP address and port used by this socket

+

Provided Methods§

Source

fn max_receive_segments(&self) -> NonZeroUsize

Maximum number of datagrams that might be described by a single RecvMeta

+
Source

fn may_fragment(&self) -> bool

Whether datagrams might get fragmented into multiple parts

+

Sockets should prevent this for best performance. See e.g. the IPV6_DONTFRAG socket +option.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/trait.ConnectionIdGenerator.html b/pr/395/docs/iroh_quinn/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..01537b89e --- /dev/null +++ b/pr/395/docs/iroh_quinn/trait.ConnectionIdGenerator.html @@ -0,0 +1,21 @@ +ConnectionIdGenerator in iroh_quinn - Rust

ConnectionIdGenerator

Trait ConnectionIdGenerator 

Source
pub trait ConnectionIdGenerator: Send + Sync {
+    // Required methods
+    fn generate_cid(&mut self) -> ConnectionId;
+    fn cid_len(&self) -> usize;
+    fn cid_lifetime(&self) -> Option<Duration>;
+
+    // Provided method
+    fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid> { ... }
+}
Expand description

Generates connection IDs for incoming connections

+

Required Methods§

Source

fn generate_cid(&mut self) -> ConnectionId

Generates a new CID

+

Connection IDs MUST NOT contain any information that can be used by +an external observer (that is, one that does not cooperate with the +issuer) to correlate them with other connection IDs for the same +connection. They MUST have high entropy, e.g. due to encrypted data +or cryptographic-grade random data.

+
Source

fn cid_len(&self) -> usize

Returns the length of a CID for connections created by this generator

+
Source

fn cid_lifetime(&self) -> Option<Duration>

Returns the lifetime of generated Connection IDs

+

Connection IDs will be retired after the returned Duration, if any. Assumed to be constant.

+

Provided Methods§

Source

fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid>

Quickly determine whether cid could have been generated by this generator

+

False positives are permitted, but increase the cost of handling invalid packets.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/trait.QlogFactory.html b/pr/395/docs/iroh_quinn/trait.QlogFactory.html new file mode 100644 index 000000000..1ddbd5c9a --- /dev/null +++ b/pr/395/docs/iroh_quinn/trait.QlogFactory.html @@ -0,0 +1,23 @@ +QlogFactory in iroh_quinn - Rust

QlogFactory

Trait QlogFactory 

Source
pub trait QlogFactory:
+    Send
+    + Sync
+    + 'static {
+    // Required method
+    fn for_connection(
+        &self,
+        side: Side,
+        remote: SocketAddr,
+        initial_dst_cid: ConnectionId,
+        now: Instant,
+    ) -> Option<QlogConfig>;
+}
Expand description

Constructs a QlogConfig for individual connections.

+

This is set via TransportConfig::qlog_factory.

+

Required Methods§

Source

fn for_connection( + &self, + side: Side, + remote: SocketAddr, + initial_dst_cid: ConnectionId, + now: Instant, +) -> Option<QlogConfig>

Returns a QlogConfig for a connection, if logging should be enabled.

+

If None is returned, qlog capture is disabled for the connection.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/trait.Runtime.html b/pr/395/docs/iroh_quinn/trait.Runtime.html new file mode 100644 index 000000000..a4edab2af --- /dev/null +++ b/pr/395/docs/iroh_quinn/trait.Runtime.html @@ -0,0 +1,19 @@ +Runtime in iroh_quinn - Rust

Runtime

Trait Runtime 

Source
pub trait Runtime:
+    Send
+    + Sync
+    + Debug
+    + 'static {
+    // Required methods
+    fn new_timer(&self, i: Instant) -> Pin<Box<dyn AsyncTimer>>;
+    fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>);
+    fn wrap_udp_socket(&self, t: UdpSocket) -> Result<Box<dyn AsyncUdpSocket>>;
+
+    // Provided method
+    fn now(&self) -> Instant { ... }
+}
Expand description

Abstracts I/O and timer operations for runtime independence

+

Required Methods§

Source

fn new_timer(&self, i: Instant) -> Pin<Box<dyn AsyncTimer>>

Construct a timer that will expire at i

+
Source

fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>)

Drive future to completion in the background

+
Source

fn wrap_udp_socket(&self, t: UdpSocket) -> Result<Box<dyn AsyncUdpSocket>>

Convert t into the socket type used by this runtime

+

Provided Methods§

Source

fn now(&self) -> Instant

Look up the current time

+

Allows simulating the flow of time for testing.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/trait.TimeSource.html b/pr/395/docs/iroh_quinn/trait.TimeSource.html new file mode 100644 index 000000000..d6f9073ed --- /dev/null +++ b/pr/395/docs/iroh_quinn/trait.TimeSource.html @@ -0,0 +1,7 @@ +TimeSource in iroh_quinn - Rust

TimeSource

Trait TimeSource 

Source
pub trait TimeSource: Send + Sync {
+    // Required method
+    fn now(&self) -> SystemTime;
+}
Expand description

Object to get current SystemTime

+

This exists to allow system time to be mocked in tests, or wherever else desired.

+

Required Methods§

Source

fn now(&self) -> SystemTime

Get SystemTime::now() or the mocked equivalent

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/trait.TokenLog.html b/pr/395/docs/iroh_quinn/trait.TokenLog.html new file mode 100644 index 000000000..47e428543 --- /dev/null +++ b/pr/395/docs/iroh_quinn/trait.TokenLog.html @@ -0,0 +1,45 @@ +TokenLog in iroh_quinn - Rust

TokenLog

Trait TokenLog 

Source
pub trait TokenLog: Send + Sync {
+    // Required method
+    fn check_and_insert(
+        &self,
+        nonce: u128,
+        issued: SystemTime,
+        lifetime: Duration,
+    ) -> Result<(), TokenReuseError>;
+}
Expand description

Responsible for limiting clients’ ability to reuse validation tokens

+

RFC 9000 § 8.1.4:

+
+

Attackers could replay tokens to use servers as amplifiers in DDoS attacks. To protect +against such attacks, servers MUST ensure that replay of tokens is prevented or limited. +Servers SHOULD ensure that tokens sent in Retry packets are only accepted for a short time, +as they are returned immediately by clients. Tokens that are provided in NEW_TOKEN frames +(Section 19.7) need to be valid for longer but SHOULD NOT be accepted multiple times. +Servers are encouraged to allow tokens to be used only once, if possible; tokens MAY include +additional information about clients to further narrow applicability or reuse.

+
+

TokenLog pertains only to tokens provided in NEW_TOKEN frames.

+

Required Methods§

Source

fn check_and_insert( + &self, + nonce: u128, + issued: SystemTime, + lifetime: Duration, +) -> Result<(), TokenReuseError>

Record that the token was used and, ideally, return a token reuse error if the token may +have been already used previously

+

False negatives and false positives are both permissible. Called when a client uses an +address validation token.

+

Parameters:

+
    +
  • nonce: A server-generated random unique value for the token.
  • +
  • issued: The time the server issued the token.
  • +
  • lifetime: The expiration time of address validation tokens sent via NEW_TOKEN frames, +as configured by ServerValidationTokenConfig::lifetime.
  • +
+
§Security & Performance
+

To the extent that it is possible to repeatedly trigger false negatives (returning Ok for +a token which has been reused), an attacker could use the server to perform amplification +attacks. The QUIC specification requires that this be limited, if not prevented fully.

+

A false positive (returning Err for a token which has never been used) is not a security +vulnerability; it is permissible for a TokenLog to always return Err. A false positive +causes the token to be ignored, which may cause the transmission of some 0.5-RTT data to be +delayed until the handshake completes, if a sufficient amount of 0.5-RTT data it sent.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/trait.TokenStore.html b/pr/395/docs/iroh_quinn/trait.TokenStore.html new file mode 100644 index 000000000..f2736bb10 --- /dev/null +++ b/pr/395/docs/iroh_quinn/trait.TokenStore.html @@ -0,0 +1,13 @@ +TokenStore in iroh_quinn - Rust

TokenStore

Trait TokenStore 

Source
pub trait TokenStore: Send + Sync {
+    // Required methods
+    fn insert(&self, server_name: &str, token: Bytes);
+    fn take(&self, server_name: &str) -> Option<Bytes>;
+}
Expand description

Responsible for storing validation tokens received from servers and retrieving them for use in +subsequent connections

+

Required Methods§

Source

fn insert(&self, server_name: &str, token: Bytes)

Potentially store a token for later one-time use

+

Called when a NEW_TOKEN frame is received from the server.

+
Source

fn take(&self, server_name: &str) -> Option<Bytes>

Try to find and take a token that was stored with the given server name

+

The same token must never be returned from take twice, as doing so can be used to +de-anonymize a client’s traffic.

+

Called when trying to connect to a server. It is always ok for this to return None.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn/trait.UdpSender.html b/pr/395/docs/iroh_quinn/trait.UdpSender.html new file mode 100644 index 000000000..898d664d6 --- /dev/null +++ b/pr/395/docs/iroh_quinn/trait.UdpSender.html @@ -0,0 +1,30 @@ +UdpSender in iroh_quinn - Rust

UdpSender

Trait UdpSender 

Source
pub trait UdpSender:
+    Send
+    + Sync
+    + Debug
+    + 'static {
+    // Required method
+    fn poll_send(
+        self: Pin<&mut Self>,
+        transmit: &Transmit<'_>,
+        cx: &mut Context<'_>,
+    ) -> Poll<Result<()>>;
+
+    // Provided method
+    fn max_transmit_segments(&self) -> NonZeroUsize { ... }
+}
Expand description

An object for asynchronously writing to an associated AsyncUdpSocket.

+

Any number of UdpSenders may exist for a single AsyncUdpSocket. Each UdpSender is +responsible for notifying at most one task for send readiness.

+

Required Methods§

Source

fn poll_send( + self: Pin<&mut Self>, + transmit: &Transmit<'_>, + cx: &mut Context<'_>, +) -> Poll<Result<()>>

Send a UDP datagram, or register to be woken if sending may succeed in the future.

+

Usually implementations of this will poll the socket for writability before trying to +write to them, and retry both if writing fails.

+

Quinn will create multiple UdpSenders, one for each task it’s using it from. Thus it’s +important to poll the underlying socket in a way that doesn’t overwrite wakers.

+

A single UdpSender will be reused, even if poll_send returns Poll::Ready once, +unlike Future::poll, so calling it again after readiness should not panic.

+

Provided Methods§

Source

fn max_transmit_segments(&self) -> NonZeroUsize

Maximum number of datagrams that a Transmit may encode.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/all.html b/pr/395/docs/iroh_quinn_proto/all.html new file mode 100644 index 000000000..513460ae8 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

List of all items

Structs

Enums

Traits

Functions

Type Aliases

Constants

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/bench_exports/index.html b/pr/395/docs/iroh_quinn_proto/bench_exports/index.html new file mode 100644 index 000000000..0745feafa --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/bench_exports/index.html @@ -0,0 +1,2 @@ +iroh_quinn_proto::bench_exports - Rust

Module bench_exports

Module bench_exports 

Source
Expand description

Exports for benchmarks

+

Modules§

send_buffer_benches
Bench fns for SendBuffer
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html b/pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html new file mode 100644 index 000000000..e250a60ae --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html @@ -0,0 +1,2 @@ +get_into_many_segments in iroh_quinn_proto::bench_exports::send_buffer_benches - Rust

get_into_many_segments

Function get_into_many_segments 

Source
pub fn get_into_many_segments(criterion: &mut Criterion)
Expand description

Pathological case: many segments, get from end

+
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html b/pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html new file mode 100644 index 000000000..ad8532b20 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html @@ -0,0 +1,2 @@ +get_loop_many_segments in iroh_quinn_proto::bench_exports::send_buffer_benches - Rust

get_loop_many_segments

Function get_loop_many_segments 

Source
pub fn get_loop_many_segments(criterion: &mut Criterion)
Expand description

Get segments in the old way, using a loop of get calls

+
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/index.html b/pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/index.html new file mode 100644 index 000000000..d66f335e3 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/index.html @@ -0,0 +1,4 @@ +iroh_quinn_proto::bench_exports::send_buffer_benches - Rust

Module send_buffer_benches

Module send_buffer_benches 

Source
Expand description

Bench fns for SendBuffer

+

These are defined here and re-exported via bench_exports in lib.rs, +so we can access the private SendBuffer struct.

+

Functions§

get_into_many_segments
Pathological case: many segments, get from end
get_loop_many_segments
Get segments in the old way, using a loop of get calls
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/sidebar-items.js b/pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/sidebar-items.js new file mode 100644 index 000000000..24edbdcf5 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["get_into_many_segments","get_loop_many_segments"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/bench_exports/sidebar-items.js b/pr/395/docs/iroh_quinn_proto/bench_exports/sidebar-items.js new file mode 100644 index 000000000..cb9cfb5fa --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/bench_exports/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["send_buffer_benches"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/bloom_token_log/struct.BloomTokenLog.html b/pr/395/docs/iroh_quinn_proto/bloom_token_log/struct.BloomTokenLog.html new file mode 100644 index 000000000..b31125b10 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/bloom_token_log/struct.BloomTokenLog.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.BloomTokenLog.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/cid_generator/struct.HashedConnectionIdGenerator.html b/pr/395/docs/iroh_quinn_proto/cid_generator/struct.HashedConnectionIdGenerator.html new file mode 100644 index 000000000..cf0d92970 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/cid_generator/struct.HashedConnectionIdGenerator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.HashedConnectionIdGenerator.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/cid_generator/struct.InvalidCid.html b/pr/395/docs/iroh_quinn_proto/cid_generator/struct.InvalidCid.html new file mode 100644 index 000000000..3cc694461 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/cid_generator/struct.InvalidCid.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.InvalidCid.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/cid_generator/struct.RandomConnectionIdGenerator.html b/pr/395/docs/iroh_quinn_proto/cid_generator/struct.RandomConnectionIdGenerator.html new file mode 100644 index 000000000..a85350b7b --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/cid_generator/struct.RandomConnectionIdGenerator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.RandomConnectionIdGenerator.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.html b/pr/395/docs/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..ce1c7d62c --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/trait.ConnectionIdGenerator.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/coding/index.html b/pr/395/docs/iroh_quinn_proto/coding/index.html new file mode 100644 index 000000000..3171b8b2a --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/coding/index.html @@ -0,0 +1,2 @@ +iroh_quinn_proto::coding - Rust

Module coding

Module coding 

Source
Expand description

Coding related traits.

+

Structs§

UnexpectedEnd
Error indicating that the provided buffer was too small

Traits§

Decodable
Decoding of QUIC primitives.
Encodable
Infallible encoding of QUIC primitives.

Type Aliases§

Result
Coding result type
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/coding/sidebar-items.js b/pr/395/docs/iroh_quinn_proto/coding/sidebar-items.js new file mode 100644 index 000000000..e6d58e381 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/coding/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["UnexpectedEnd"],"trait":["Decodable","Encodable"],"type":["Result"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html b/pr/395/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html new file mode 100644 index 000000000..943b22718 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html @@ -0,0 +1,33 @@ +UnexpectedEnd in iroh_quinn_proto::coding - Rust

UnexpectedEnd

Struct UnexpectedEnd 

Source
pub struct UnexpectedEnd;
Expand description

Error indicating that the provided buffer was too small

+

Trait Implementations§

Source§

impl Clone for UnexpectedEnd

Source§

fn clone(&self) -> UnexpectedEnd

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for UnexpectedEnd

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for UnexpectedEnd

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for UnexpectedEnd

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<UnexpectedEnd> for Error

Source§

fn from(_: UnexpectedEnd) -> Self

Converts to this type from the input type.
Source§

impl From<UnexpectedEnd> for PacketDecodeError

Source§

fn from(_: UnexpectedEnd) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for UnexpectedEnd

Source§

fn eq(&self, other: &UnexpectedEnd) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for UnexpectedEnd

Source§

impl Eq for UnexpectedEnd

Source§

impl StructuralPartialEq for UnexpectedEnd

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/coding/trait.Decodable.html b/pr/395/docs/iroh_quinn_proto/coding/trait.Decodable.html new file mode 100644 index 000000000..fea011b46 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/coding/trait.Decodable.html @@ -0,0 +1,6 @@ +Decodable in iroh_quinn_proto::coding - Rust

Decodable

Trait Decodable 

Source
pub trait Decodable: Sized {
+    // Required method
+    fn decode<B: Buf>(buf: &mut B) -> Result<Self>;
+}
Expand description

Decoding of QUIC primitives.

+

Required Methods§

Source

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough

+

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl Decodable for u8

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for u16

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for u32

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for u64

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for Ipv4Addr

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for Ipv6Addr

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/coding/trait.Encodable.html b/pr/395/docs/iroh_quinn_proto/coding/trait.Encodable.html new file mode 100644 index 000000000..c87ab4de4 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/coding/trait.Encodable.html @@ -0,0 +1,6 @@ +Encodable in iroh_quinn_proto::coding - Rust

Encodable

Trait Encodable 

Source
pub trait Encodable {
+    // Required method
+    fn encode<B: BufMut>(&self, buf: &mut B);
+}
Expand description

Infallible encoding of QUIC primitives.

+

Required Methods§

Source

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer.

+

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl Encodable for u8

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u16

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u32

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u64

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for Ipv4Addr

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for Ipv6Addr

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/coding/type.Result.html b/pr/395/docs/iroh_quinn_proto/coding/type.Result.html new file mode 100644 index 000000000..057747f8b --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/coding/type.Result.html @@ -0,0 +1,7 @@ +Result in iroh_quinn_proto::coding - Rust

Result

Type Alias Result 

Source
pub type Result<T> = Result<T, UnexpectedEnd>;
Expand description

Coding result type

+

Aliased Type§

pub enum Result<T> {
+    Ok(T),
+    Err(UnexpectedEnd),
+}

Variants§

§1.0.0

Ok(T)

Contains the success value

+
§1.0.0

Err(UnexpectedEnd)

Contains the error value

+
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/enum.ConfigError.html b/pr/395/docs/iroh_quinn_proto/config/enum.ConfigError.html new file mode 100644 index 000000000..a2318c2f1 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/enum.ConfigError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.ConfigError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/qlog/struct.QlogConfig.html b/pr/395/docs/iroh_quinn_proto/config/qlog/struct.QlogConfig.html new file mode 100644 index 000000000..1e9d560a8 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/qlog/struct.QlogConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.QlogConfig.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/qlog/struct.QlogFileFactory.html b/pr/395/docs/iroh_quinn_proto/config/qlog/struct.QlogFileFactory.html new file mode 100644 index 000000000..6a7bb652e --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/qlog/struct.QlogFileFactory.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.QlogFileFactory.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/qlog/trait.QlogFactory.html b/pr/395/docs/iroh_quinn_proto/config/qlog/trait.QlogFactory.html new file mode 100644 index 000000000..e58263549 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/qlog/trait.QlogFactory.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/trait.QlogFactory.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/struct.ClientConfig.html b/pr/395/docs/iroh_quinn_proto/config/struct.ClientConfig.html new file mode 100644 index 000000000..72ad29942 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/struct.ClientConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ClientConfig.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/struct.EndpointConfig.html b/pr/395/docs/iroh_quinn_proto/config/struct.EndpointConfig.html new file mode 100644 index 000000000..f787522dd --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/struct.EndpointConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.EndpointConfig.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/struct.ServerConfig.html b/pr/395/docs/iroh_quinn_proto/config/struct.ServerConfig.html new file mode 100644 index 000000000..54e34e58b --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/struct.ServerConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ServerConfig.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/struct.StdSystemTime.html b/pr/395/docs/iroh_quinn_proto/config/struct.StdSystemTime.html new file mode 100644 index 000000000..a036660a0 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/struct.StdSystemTime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.StdSystemTime.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/struct.ValidationTokenConfig.html b/pr/395/docs/iroh_quinn_proto/config/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..71c171cbb --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/struct.ValidationTokenConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ValidationTokenConfig.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/trait.TimeSource.html b/pr/395/docs/iroh_quinn_proto/config/trait.TimeSource.html new file mode 100644 index 000000000..531e889ff --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/trait.TimeSource.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/trait.TimeSource.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html b/pr/395/docs/iroh_quinn_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html new file mode 100644 index 000000000..b07cdbacc --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/transport/struct.AckFrequencyConfig.html b/pr/395/docs/iroh_quinn_proto/config/transport/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..f1cfe6e06 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/transport/struct.AckFrequencyConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.AckFrequencyConfig.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/transport/struct.IdleTimeout.html b/pr/395/docs/iroh_quinn_proto/config/transport/struct.IdleTimeout.html new file mode 100644 index 000000000..d3b3774c0 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/transport/struct.IdleTimeout.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.IdleTimeout.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/transport/struct.MtuDiscoveryConfig.html b/pr/395/docs/iroh_quinn_proto/config/transport/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..8f76f4934 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/transport/struct.MtuDiscoveryConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.MtuDiscoveryConfig.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/config/transport/struct.TransportConfig.html b/pr/395/docs/iroh_quinn_proto/config/transport/struct.TransportConfig.html new file mode 100644 index 000000000..b849996c3 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/config/transport/struct.TransportConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.TransportConfig.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/bbr/struct.Bbr.html b/pr/395/docs/iroh_quinn_proto/congestion/bbr/struct.Bbr.html new file mode 100644 index 000000000..ab7174506 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/bbr/struct.Bbr.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/congestion/struct.Bbr.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/bbr/struct.BbrConfig.html b/pr/395/docs/iroh_quinn_proto/congestion/bbr/struct.BbrConfig.html new file mode 100644 index 000000000..0d5073995 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/bbr/struct.BbrConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/congestion/struct.BbrConfig.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/cubic/struct.Cubic.html b/pr/395/docs/iroh_quinn_proto/congestion/cubic/struct.Cubic.html new file mode 100644 index 000000000..687e48b93 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/cubic/struct.Cubic.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/congestion/struct.Cubic.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/cubic/struct.CubicConfig.html b/pr/395/docs/iroh_quinn_proto/congestion/cubic/struct.CubicConfig.html new file mode 100644 index 000000000..5c78ca8d6 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/cubic/struct.CubicConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/congestion/struct.CubicConfig.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/index.html b/pr/395/docs/iroh_quinn_proto/congestion/index.html new file mode 100644 index 000000000..22280c3f9 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/index.html @@ -0,0 +1,2 @@ +iroh_quinn_proto::congestion - Rust

Module congestion

Module congestion 

Source
Expand description

Logic for controlling the rate at which data is sent

+

Structs§

Bbr
Experimental! Use at your own risk.
BbrConfig
Configuration for the Bbr congestion controller
ControllerMetrics
Common congestion controller metrics
Cubic
The RFC8312 congestion controller, as widely used for TCP
CubicConfig
Configuration for the Cubic congestion controller
NewReno
A simple, standard congestion controller
NewRenoConfig
Configuration for the NewReno congestion controller

Traits§

Controller
Common interface for different congestion controllers
ControllerFactory
Constructs controllers on demand
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/new_reno/struct.NewReno.html b/pr/395/docs/iroh_quinn_proto/congestion/new_reno/struct.NewReno.html new file mode 100644 index 000000000..2722e12a4 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/new_reno/struct.NewReno.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/congestion/struct.NewReno.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/new_reno/struct.NewRenoConfig.html b/pr/395/docs/iroh_quinn_proto/congestion/new_reno/struct.NewRenoConfig.html new file mode 100644 index 000000000..50d3f5805 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/new_reno/struct.NewRenoConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/congestion/struct.NewRenoConfig.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/sidebar-items.js b/pr/395/docs/iroh_quinn_proto/congestion/sidebar-items.js new file mode 100644 index 000000000..888f550d4 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["Bbr","BbrConfig","ControllerMetrics","Cubic","CubicConfig","NewReno","NewRenoConfig"],"trait":["Controller","ControllerFactory"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/struct.Bbr.html b/pr/395/docs/iroh_quinn_proto/congestion/struct.Bbr.html new file mode 100644 index 000000000..302918b66 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/struct.Bbr.html @@ -0,0 +1,52 @@ +Bbr in iroh_quinn_proto::congestion - Rust

Bbr

Struct Bbr 

Source
pub struct Bbr { /* private fields */ }
Expand description

Experimental! Use at your own risk.

+

Aims for reduced buffer bloat and improved performance over high bandwidth-delay product networks. +Based on google’s quiche implementation https://source.chromium.org/chromium/chromium/src/+/master:net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc +of BBR https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control. +More discussion and links at https://groups.google.com/g/bbr-dev.

+

Implementations§

Source§

impl Bbr

Source

pub fn new(config: Arc<BbrConfig>, current_mtu: u16) -> Self

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for Bbr

Source§

fn clone(&self) -> Bbr

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Controller for Bbr

Source§

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent
Source§

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.
Source§

fn on_congestion_event( + &mut self, + _now: Instant, + _sent: Instant, + _is_persistent_congestion: bool, + _is_ecn: bool, + lost_bytes: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated
Source§

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight
Source§

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled
Source§

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

fn into_any(self: Box<Self>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

impl Debug for Bbr

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Bbr

§

impl RefUnwindSafe for Bbr

§

impl Send for Bbr

§

impl Sync for Bbr

§

impl Unpin for Bbr

§

impl UnwindSafe for Bbr

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/struct.BbrConfig.html b/pr/395/docs/iroh_quinn_proto/congestion/struct.BbrConfig.html new file mode 100644 index 000000000..742832b07 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/struct.BbrConfig.html @@ -0,0 +1,33 @@ +BbrConfig in iroh_quinn_proto::congestion - Rust

BbrConfig

Struct BbrConfig 

Source
pub struct BbrConfig { /* private fields */ }
Expand description

Configuration for the Bbr congestion controller

+

Implementations§

Source§

impl BbrConfig

Source

pub fn initial_window(&mut self, value: u64) -> &mut Self

Default limit on the amount of outstanding data in bytes.

+

Recommended value: min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))

+

Trait Implementations§

Source§

impl Clone for BbrConfig

Source§

fn clone(&self) -> BbrConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for BbrConfig

Source§

fn build( + self: Arc<Self>, + _now: Instant, + current_mtu: u16, +) -> Box<dyn Controller>

Construct a fresh Controller
Source§

impl Debug for BbrConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for BbrConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/struct.ControllerMetrics.html b/pr/395/docs/iroh_quinn_proto/congestion/struct.ControllerMetrics.html new file mode 100644 index 000000000..4a0b8a38b --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/struct.ControllerMetrics.html @@ -0,0 +1,32 @@ +ControllerMetrics in iroh_quinn_proto::congestion - Rust

ControllerMetrics

Struct ControllerMetrics 

Source
#[non_exhaustive]
pub struct ControllerMetrics { + pub congestion_window: u64, + pub ssthresh: Option<u64>, + pub pacing_rate: Option<u64>, +}
Expand description

Common congestion controller metrics

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§congestion_window: u64

Congestion window (bytes)

+
§ssthresh: Option<u64>

Slow start threshold (bytes)

+
§pacing_rate: Option<u64>

Pacing rate (bits/s)

+

Trait Implementations§

Source§

impl Debug for ControllerMetrics

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ControllerMetrics

Source§

fn default() -> ControllerMetrics

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/struct.Cubic.html b/pr/395/docs/iroh_quinn_proto/congestion/struct.Cubic.html new file mode 100644 index 000000000..c1ead7a9b --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/struct.Cubic.html @@ -0,0 +1,48 @@ +Cubic in iroh_quinn_proto::congestion - Rust

Cubic

Struct Cubic 

Source
pub struct Cubic { /* private fields */ }
Expand description

The RFC8312 congestion controller, as widely used for TCP

+

Implementations§

Source§

impl Cubic

Source

pub fn new(config: Arc<CubicConfig>, _now: Instant, current_mtu: u16) -> Self

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for Cubic

Source§

fn clone(&self) -> Cubic

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Controller for Cubic

Source§

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + is_ecn: bool, + _lost_bytes: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated
Source§

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight
Source§

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled
Source§

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

fn into_any(self: Box<Self>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details
Source§

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent
Source§

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.
Source§

impl Debug for Cubic

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Cubic

§

impl RefUnwindSafe for Cubic

§

impl Send for Cubic

§

impl Sync for Cubic

§

impl Unpin for Cubic

§

impl UnwindSafe for Cubic

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/struct.CubicConfig.html b/pr/395/docs/iroh_quinn_proto/congestion/struct.CubicConfig.html new file mode 100644 index 000000000..88749061e --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/struct.CubicConfig.html @@ -0,0 +1,29 @@ +CubicConfig in iroh_quinn_proto::congestion - Rust

CubicConfig

Struct CubicConfig 

Source
pub struct CubicConfig { /* private fields */ }
Expand description

Configuration for the Cubic congestion controller

+

Implementations§

Source§

impl CubicConfig

Source

pub fn initial_window(&mut self, value: u64) -> &mut Self

Default limit on the amount of outstanding data in bytes.

+

Recommended value: min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))

+

Trait Implementations§

Source§

impl Clone for CubicConfig

Source§

fn clone(&self) -> CubicConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for CubicConfig

Source§

fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller>

Construct a fresh Controller
Source§

impl Debug for CubicConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for CubicConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/struct.NewReno.html b/pr/395/docs/iroh_quinn_proto/congestion/struct.NewReno.html new file mode 100644 index 000000000..75e230a30 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/struct.NewReno.html @@ -0,0 +1,48 @@ +NewReno in iroh_quinn_proto::congestion - Rust

NewReno

Struct NewReno 

Source
pub struct NewReno { /* private fields */ }
Expand description

A simple, standard congestion controller

+

Implementations§

Source§

impl NewReno

Source

pub fn new(config: Arc<NewRenoConfig>, now: Instant, current_mtu: u16) -> Self

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for NewReno

Source§

fn clone(&self) -> NewReno

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Controller for NewReno

Source§

fn on_ack( + &mut self, + _now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + _rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + _is_ecn: bool, + _lost_bytes: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated
Source§

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight
Source§

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled
Source§

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

fn into_any(self: Box<Self>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details
Source§

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent
Source§

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

impl Debug for NewReno

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/struct.NewRenoConfig.html b/pr/395/docs/iroh_quinn_proto/congestion/struct.NewRenoConfig.html new file mode 100644 index 000000000..4acb3beb2 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/struct.NewRenoConfig.html @@ -0,0 +1,30 @@ +NewRenoConfig in iroh_quinn_proto::congestion - Rust

NewRenoConfig

Struct NewRenoConfig 

Source
pub struct NewRenoConfig { /* private fields */ }
Expand description

Configuration for the NewReno congestion controller

+

Implementations§

Source§

impl NewRenoConfig

Source

pub fn initial_window(&mut self, value: u64) -> &mut Self

Default limit on the amount of outstanding data in bytes.

+

Recommended value: min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))

+
Source

pub fn loss_reduction_factor(&mut self, value: f32) -> &mut Self

Reduction in congestion window when a new loss event is detected.

+

Trait Implementations§

Source§

impl Clone for NewRenoConfig

Source§

fn clone(&self) -> NewRenoConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for NewRenoConfig

Source§

fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller>

Construct a fresh Controller
Source§

impl Debug for NewRenoConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for NewRenoConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/trait.Controller.html b/pr/395/docs/iroh_quinn_proto/congestion/trait.Controller.html new file mode 100644 index 000000000..2826d0cd6 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/trait.Controller.html @@ -0,0 +1,79 @@ +Controller in iroh_quinn_proto::congestion - Rust

Controller

Trait Controller 

Source
pub trait Controller:
+    Send
+    + Sync
+    + Debug {
+    // Required methods
+    fn on_congestion_event(
+        &mut self,
+        now: Instant,
+        sent: Instant,
+        is_persistent_congestion: bool,
+        is_ecn: bool,
+        lost_bytes: u64,
+    );
+    fn on_mtu_update(&mut self, new_mtu: u16);
+    fn window(&self) -> u64;
+    fn clone_box(&self) -> Box<dyn Controller>;
+    fn initial_window(&self) -> u64;
+    fn into_any(self: Box<Self>) -> Box<dyn Any>;
+
+    // Provided methods
+    fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64) { ... }
+    fn on_ack(
+        &mut self,
+        now: Instant,
+        sent: Instant,
+        bytes: u64,
+        app_limited: bool,
+        rtt: &RttEstimator,
+    ) { ... }
+    fn on_end_acks(
+        &mut self,
+        now: Instant,
+        in_flight: u64,
+        app_limited: bool,
+        largest_packet_num_acked: Option<u64>,
+    ) { ... }
+    fn on_spurious_congestion_event(&mut self) { ... }
+    fn metrics(&self) -> ControllerMetrics { ... }
+}
Expand description

Common interface for different congestion controllers

+

Required Methods§

Source

fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + is_ecn: bool, + lost_bytes: u64, +)

Packets were deemed lost or marked congested

+

in_persistent_congestion indicates whether all packets sent within the persistent +congestion threshold period ending when the most recent packet in this batch was sent were +lost. +lost_bytes indicates how many bytes were lost. This value will be 0 for ECN triggers.

+
Source

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated

+
Source

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight

+
Source

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state

+
Source

fn initial_window(&self) -> u64

Initial congestion window

+
Source

fn into_any(self: Box<Self>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details

+

Provided Methods§

Source

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent

+
Source

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + rtt: &RttEstimator, +)

Packet deliveries were confirmed

+

app_limited indicates whether the connection was blocked on outgoing +application data prior to receiving these acknowledgements.

+
Source

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.

+
Source

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost

+

This function is called when all packets that were deemed lost (for instance because +of packet reordering) are acknowledged after the congestion event was raised.

+
Source

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/congestion/trait.ControllerFactory.html b/pr/395/docs/iroh_quinn_proto/congestion/trait.ControllerFactory.html new file mode 100644 index 000000000..a55d3d72d --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/congestion/trait.ControllerFactory.html @@ -0,0 +1,10 @@ +ControllerFactory in iroh_quinn_proto::congestion - Rust

ControllerFactory

Trait ControllerFactory 

Source
pub trait ControllerFactory {
+    // Required method
+    fn build(
+        self: Arc<Self>,
+        now: Instant,
+        current_mtu: u16,
+    ) -> Box<dyn Controller>;
+}
Expand description

Constructs controllers on demand

+

Required Methods§

Source

fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller>

Construct a fresh Controller

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/assembler/struct.Chunk.html b/pr/395/docs/iroh_quinn_proto/connection/assembler/struct.Chunk.html new file mode 100644 index 000000000..f7ac7a578 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/assembler/struct.Chunk.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.Chunk.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/datagrams/enum.SendDatagramError.html b/pr/395/docs/iroh_quinn_proto/connection/datagrams/enum.SendDatagramError.html new file mode 100644 index 000000000..addce8553 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/datagrams/enum.SendDatagramError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/enum.SendDatagramError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/datagrams/struct.Datagrams.html b/pr/395/docs/iroh_quinn_proto/connection/datagrams/struct.Datagrams.html new file mode 100644 index 000000000..240a4cef6 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/datagrams/struct.Datagrams.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.Datagrams.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/enum.ClosePathError.html b/pr/395/docs/iroh_quinn_proto/connection/enum.ClosePathError.html new file mode 100644 index 000000000..73bb84ef9 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/enum.ClosePathError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.ClosePathError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/enum.ConnectionError.html b/pr/395/docs/iroh_quinn_proto/connection/enum.ConnectionError.html new file mode 100644 index 000000000..abbb38eab --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/enum.ConnectionError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.ConnectionError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/enum.Event.html b/pr/395/docs/iroh_quinn_proto/connection/enum.Event.html new file mode 100644 index 000000000..d1dc43357 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/enum.Event.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.Event.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/enum.PathError.html b/pr/395/docs/iroh_quinn_proto/connection/enum.PathError.html new file mode 100644 index 000000000..c45b49c08 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/enum.PathError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.PathError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/paths/enum.PathEvent.html b/pr/395/docs/iroh_quinn_proto/connection/paths/enum.PathEvent.html new file mode 100644 index 000000000..5002bde8e --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/paths/enum.PathEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/enum.PathEvent.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/paths/enum.PathStatus.html b/pr/395/docs/iroh_quinn_proto/connection/paths/enum.PathStatus.html new file mode 100644 index 000000000..a886eee7b --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/paths/enum.PathStatus.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/enum.PathStatus.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/paths/enum.SetPathStatusError.html b/pr/395/docs/iroh_quinn_proto/connection/paths/enum.SetPathStatusError.html new file mode 100644 index 000000000..60829b195 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/paths/enum.SetPathStatusError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/enum.SetPathStatusError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/paths/struct.ClosedPath.html b/pr/395/docs/iroh_quinn_proto/connection/paths/struct.ClosedPath.html new file mode 100644 index 000000000..63b45525e --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/paths/struct.ClosedPath.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.ClosedPath.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/paths/struct.PathId.html b/pr/395/docs/iroh_quinn_proto/connection/paths/struct.PathId.html new file mode 100644 index 000000000..2fd79824a --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/paths/struct.PathId.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.PathId.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/paths/struct.RttEstimator.html b/pr/395/docs/iroh_quinn_proto/connection/paths/struct.RttEstimator.html new file mode 100644 index 000000000..21f95bb18 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/paths/struct.RttEstimator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.RttEstimator.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/fn.get_into_many_segments.html b/pr/395/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/fn.get_into_many_segments.html new file mode 100644 index 000000000..6ac22cfec --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/fn.get_into_many_segments.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/fn.get_loop_many_segments.html b/pr/395/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/fn.get_loop_many_segments.html new file mode 100644 index 000000000..a30f48238 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/fn.get_loop_many_segments.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/index.html b/pr/395/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/index.html new file mode 100644 index 000000000..4a1c7ea83 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/index.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/bench_exports/send_buffer_benches/index.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/stats/struct.ConnectionStats.html b/pr/395/docs/iroh_quinn_proto/connection/stats/struct.ConnectionStats.html new file mode 100644 index 000000000..2102eceea --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/stats/struct.ConnectionStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.ConnectionStats.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/stats/struct.FrameStats.html b/pr/395/docs/iroh_quinn_proto/connection/stats/struct.FrameStats.html new file mode 100644 index 000000000..9385ca7df --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/stats/struct.FrameStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.FrameStats.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/stats/struct.PathStats.html b/pr/395/docs/iroh_quinn_proto/connection/stats/struct.PathStats.html new file mode 100644 index 000000000..e561e9dcf --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/stats/struct.PathStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.PathStats.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/stats/struct.UdpStats.html b/pr/395/docs/iroh_quinn_proto/connection/stats/struct.UdpStats.html new file mode 100644 index 000000000..f606e4f17 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/stats/struct.UdpStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.UdpStats.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/streams/enum.StreamEvent.html b/pr/395/docs/iroh_quinn_proto/connection/streams/enum.StreamEvent.html new file mode 100644 index 000000000..03531d047 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/streams/enum.StreamEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/enum.StreamEvent.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadError.html b/pr/395/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadError.html new file mode 100644 index 000000000..ebeb73d2e --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/enum.ReadError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadableError.html b/pr/395/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadableError.html new file mode 100644 index 000000000..41f875c3e --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadableError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/enum.ReadableError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/streams/recv/struct.Chunks.html b/pr/395/docs/iroh_quinn_proto/connection/streams/recv/struct.Chunks.html new file mode 100644 index 000000000..2c3a1e2c2 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/streams/recv/struct.Chunks.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/struct.Chunks.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/streams/send/enum.FinishError.html b/pr/395/docs/iroh_quinn_proto/connection/streams/send/enum.FinishError.html new file mode 100644 index 000000000..404821c56 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/streams/send/enum.FinishError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/enum.FinishError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/streams/send/enum.WriteError.html b/pr/395/docs/iroh_quinn_proto/connection/streams/send/enum.WriteError.html new file mode 100644 index 000000000..a3a2799d8 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/streams/send/enum.WriteError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/enum.WriteError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/streams/send/struct.Written.html b/pr/395/docs/iroh_quinn_proto/connection/streams/send/struct.Written.html new file mode 100644 index 000000000..886ef15f4 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/streams/send/struct.Written.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/struct.Written.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/streams/struct.ClosedStream.html b/pr/395/docs/iroh_quinn_proto/connection/streams/struct.ClosedStream.html new file mode 100644 index 000000000..c276926d2 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/streams/struct.ClosedStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.ClosedStream.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/streams/struct.RecvStream.html b/pr/395/docs/iroh_quinn_proto/connection/streams/struct.RecvStream.html new file mode 100644 index 000000000..db77de12a --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/streams/struct.RecvStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.RecvStream.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/streams/struct.SendStream.html b/pr/395/docs/iroh_quinn_proto/connection/streams/struct.SendStream.html new file mode 100644 index 000000000..84c2e122d --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/streams/struct.SendStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.SendStream.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/streams/struct.ShouldTransmit.html b/pr/395/docs/iroh_quinn_proto/connection/streams/struct.ShouldTransmit.html new file mode 100644 index 000000000..8023ab946 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/streams/struct.ShouldTransmit.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.ShouldTransmit.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/streams/struct.Streams.html b/pr/395/docs/iroh_quinn_proto/connection/streams/struct.Streams.html new file mode 100644 index 000000000..b3a078714 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/streams/struct.Streams.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.Streams.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/struct.Connection.html b/pr/395/docs/iroh_quinn_proto/connection/struct.Connection.html new file mode 100644 index 000000000..6e3774403 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/struct.Connection.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.Connection.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/connection/struct.MultipathNotNegotiated.html b/pr/395/docs/iroh_quinn_proto/connection/struct.MultipathNotNegotiated.html new file mode 100644 index 000000000..797cb208f --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/connection/struct.MultipathNotNegotiated.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.MultipathNotNegotiated.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html b/pr/395/docs/iroh_quinn_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html new file mode 100644 index 000000000..f4ae96dab --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html @@ -0,0 +1,3 @@ +DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED in iroh_quinn_proto - Rust

DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED

Constant DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED 

Source
pub const DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED: NonZeroU32;
Expand description

When multipath is required and has not been explicitly enabled, this value will be used for

+

TransportConfig::max_concurrent_multipath_paths.

+
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html b/pr/395/docs/iroh_quinn_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html new file mode 100644 index 000000000..ee05813da --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html @@ -0,0 +1,2 @@ +DEFAULT_SUPPORTED_VERSIONS in iroh_quinn_proto - Rust

DEFAULT_SUPPORTED_VERSIONS

Constant DEFAULT_SUPPORTED_VERSIONS 

Source
pub const DEFAULT_SUPPORTED_VERSIONS: &[u32];
Expand description

The QUIC protocol version implemented.

+
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/index.html b/pr/395/docs/iroh_quinn_proto/crypto/index.html new file mode 100644 index 000000000..b23db0a56 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/index.html @@ -0,0 +1,8 @@ +iroh_quinn_proto::crypto - Rust

Module crypto

Module crypto 

Source
Expand description

Traits and implementations for the QUIC cryptography protocol

+

The protocol logic in Quinn is contained in types that abstract over the actual +cryptographic protocol used. This module contains the traits used for this +abstraction layer as well as a single implementation of these traits that uses +ring and rustls to implement the TLS protocol support.

+

Note that usage of any protocol (version) other than TLS 1.3 does not conform to any +published versions of the specification, and will not be supported in QUIC v1.

+

Modules§

rustls
TLS interface based on rustls

Structs§

CryptoError
Generic crypto errors
ExportKeyingMaterialError
Error returned by Session::export_keying_material.
KeyPair
A pair of keys for bidirectional communication
Keys
A complete set of keys for a certain packet space
UnsupportedVersion
Error indicating that the specified QUIC version is not supported

Traits§

AeadKey
A key for sealing data with AEAD-based algorithms
ClientConfig
Client-side configuration for the crypto protocol
HandshakeTokenKey
A pseudo random key for HKDF
HeaderKey
Keys used to protect packet headers
HmacKey
A key for signing with HMAC-based algorithms
PacketKey
Keys used to protect packet payloads
ServerConfig
Server-side configuration for the crypto protocol
Session
A cryptographic session (commonly TLS)
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/rustls/enum.Error.html b/pr/395/docs/iroh_quinn_proto/crypto/rustls/enum.Error.html new file mode 100644 index 000000000..fb0f86cdb --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/rustls/enum.Error.html @@ -0,0 +1,101 @@ +Error in iroh_quinn_proto::crypto::rustls - Rust

Error

Enum Error 

#[non_exhaustive]
pub enum Error { +
Show 22 variants InappropriateMessage { + expect_types: Vec<ContentType>, + got_type: ContentType, + }, + InappropriateHandshakeMessage { + expect_types: Vec<HandshakeType>, + got_type: HandshakeType, + }, + InvalidEncryptedClientHello(EncryptedClientHelloError), + InvalidMessage(InvalidMessage), + NoCertificatesPresented, + UnsupportedNameType, + DecryptError, + EncryptError, + PeerIncompatible(PeerIncompatible), + PeerMisbehaved(PeerMisbehaved), + AlertReceived(AlertDescription), + InvalidCertificate(CertificateError), + InvalidCertRevocationList(CertRevocationListError), + General(String), + FailedToGetCurrentTime, + FailedToGetRandomBytes, + HandshakeNotComplete, + PeerSentOversizedRecord, + NoApplicationProtocol, + BadMaxFragmentSize, + InconsistentKeys(InconsistentKeys), + Other(OtherError), +
}
Expand description

rustls reports protocol errors using this type.

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

InappropriateMessage

We received a TLS message that isn’t valid right now. +expect_types lists the message types we can expect right now. +got_type is the type we found. This error is typically +caused by a buggy TLS stack (the peer or this one), a broken +network, or an attack.

+

Fields

§expect_types: Vec<ContentType>

Which types we expected

+
§got_type: ContentType

What type we received

+
§

InappropriateHandshakeMessage

We received a TLS handshake message that isn’t valid right now. +expect_types lists the handshake message types we can expect +right now. got_type is the type we found.

+

Fields

§expect_types: Vec<HandshakeType>

Which handshake type we expected

+
§got_type: HandshakeType

What handshake type we received

+
§

InvalidEncryptedClientHello(EncryptedClientHelloError)

An error occurred while handling Encrypted Client Hello (ECH).

+
§

InvalidMessage(InvalidMessage)

The peer sent us a TLS message with invalid contents.

+
§

NoCertificatesPresented

The peer didn’t give us any certificates.

+
§

UnsupportedNameType

The certificate verifier doesn’t support the given type of name.

+
§

DecryptError

We couldn’t decrypt a message. This is invariably fatal.

+
§

EncryptError

We couldn’t encrypt a message because it was larger than the allowed message size. +This should never happen if the application is using valid record sizes.

+
§

PeerIncompatible(PeerIncompatible)

The peer doesn’t support a protocol version/feature we require. +The parameter gives a hint as to what version/feature it is.

+
§

PeerMisbehaved(PeerMisbehaved)

The peer deviated from the standard TLS protocol. +The parameter gives a hint where.

+
§

AlertReceived(AlertDescription)

We received a fatal alert. This means the peer is unhappy.

+
§

InvalidCertificate(CertificateError)

We saw an invalid certificate.

+

The contained error is from the certificate validation trait +implementation.

+
§

InvalidCertRevocationList(CertRevocationListError)

A provided certificate revocation list (CRL) was invalid.

+
§

General(String)

A catch-all error for unlikely errors.

+
§

FailedToGetCurrentTime

We failed to figure out what time it currently is.

+
§

FailedToGetRandomBytes

We failed to acquire random bytes from the system.

+
§

HandshakeNotComplete

This function doesn’t work until the TLS handshake +is complete.

+
§

PeerSentOversizedRecord

The peer sent an oversized record/fragment.

+
§

NoApplicationProtocol

An incoming connection did not support any known application protocol.

+
§

BadMaxFragmentSize

The max_fragment_size value supplied in configuration was too small, +or too large.

+
§

InconsistentKeys(InconsistentKeys)

Specific failure cases from keys_match or a [crate::crypto::signer::SigningKey] that cannot produce a corresponding public key.

+
§

Other(OtherError)

Any other error.

+

This variant should only be used when the error is not better described by a more +specific variant. For example, if a custom crypto provider returns a +provider specific error.

+

Enums holding this variant will never compare equal to each other.

+

Trait Implementations§

§

impl Clone for Error

§

fn clone(&self) -> Error

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for Error

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl Display for Error

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl Error for Error

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
§

impl From<CertRevocationListError> for Error

§

fn from(e: CertRevocationListError) -> Error

Converts to this type from the input type.
§

impl From<CertificateError> for Error

§

fn from(e: CertificateError) -> Error

Converts to this type from the input type.
§

impl From<EncryptedClientHelloError> for Error

§

fn from(e: EncryptedClientHelloError) -> Error

Converts to this type from the input type.
§

impl From<GetRandomFailed> for Error

§

fn from(_: GetRandomFailed) -> Error

Converts to this type from the input type.
§

impl From<InconsistentKeys> for Error

§

fn from(e: InconsistentKeys) -> Error

Converts to this type from the input type.
§

impl From<InvalidMessage> for Error

§

fn from(e: InvalidMessage) -> Error

Converts to this type from the input type.
§

impl From<OtherError> for Error

§

fn from(value: OtherError) -> Error

Converts to this type from the input type.
§

impl From<PeerIncompatible> for Error

§

fn from(e: PeerIncompatible) -> Error

Converts to this type from the input type.
§

impl From<PeerMisbehaved> for Error

§

fn from(e: PeerMisbehaved) -> Error

Converts to this type from the input type.
§

impl From<SystemTimeError> for Error

§

fn from(_: SystemTimeError) -> Error

Converts to this type from the input type.
§

impl From<UnsupportedOperationError> for Error

§

fn from(value: UnsupportedOperationError) -> Error

Converts to this type from the input type.
§

impl PartialEq for Error

§

fn eq(&self, other: &Error) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
§

impl StructuralPartialEq for Error

Auto Trait Implementations§

§

impl Freeze for Error

§

impl !RefUnwindSafe for Error

§

impl Send for Error

§

impl Sync for Error

§

impl Unpin for Error

§

impl !UnwindSafe for Error

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/rustls/index.html b/pr/395/docs/iroh_quinn_proto/crypto/rustls/index.html new file mode 100644 index 000000000..fe230888b --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/rustls/index.html @@ -0,0 +1,2 @@ +iroh_quinn_proto::crypto::rustls - Rust

Module rustls

Module rustls 

Source
Expand description

TLS interface based on rustls

+

Structs§

HandshakeData
Authentication data for (rustls) TLS session
NoInitialCipherSuite
The initial cipher suite (AES-128-GCM-SHA256) is not available
QuicClientConfig
A QUIC-compatible TLS client configuration
QuicServerConfig
A QUIC-compatible TLS server configuration
TlsSession
A rustls TLS session

Enums§

Error
rustls reports protocol errors using this type.
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/rustls/sidebar-items.js b/pr/395/docs/iroh_quinn_proto/crypto/rustls/sidebar-items.js new file mode 100644 index 000000000..65f98d4d4 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/rustls/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Error"],"struct":["HandshakeData","NoInitialCipherSuite","QuicClientConfig","QuicServerConfig","TlsSession"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.HandshakeData.html b/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.HandshakeData.html new file mode 100644 index 000000000..5dcea94c4 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.HandshakeData.html @@ -0,0 +1,34 @@ +HandshakeData in iroh_quinn_proto::crypto::rustls - Rust

HandshakeData

Struct HandshakeData 

Source
pub struct HandshakeData {
+    pub protocol: Option<Vec<u8>>,
+    pub server_name: Option<String>,
+    pub negotiated_key_exchange_group: NamedGroup,
+}
Expand description

Authentication data for (rustls) TLS session

+

Fields§

§protocol: Option<Vec<u8>>

The negotiated application protocol, if ALPN is in use

+

Guaranteed to be set if a nonempty list of protocols was specified for this connection.

+
§server_name: Option<String>

The server name specified by the client, if any

+

Always None for outgoing connections

+
§negotiated_key_exchange_group: NamedGroup

The key exchange group negotiated with the peer

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.NoInitialCipherSuite.html b/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.NoInitialCipherSuite.html new file mode 100644 index 000000000..b00928ef9 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.NoInitialCipherSuite.html @@ -0,0 +1,31 @@ +NoInitialCipherSuite in iroh_quinn_proto::crypto::rustls - Rust

NoInitialCipherSuite

Struct NoInitialCipherSuite 

Source
pub struct NoInitialCipherSuite { /* private fields */ }
Expand description

The initial cipher suite (AES-128-GCM-SHA256) is not available

+

When the cipher suite is supplied with_initial(), it must be +[CipherSuite::TLS13_AES_128_GCM_SHA256]. When the cipher suite is derived from a config’s +CryptoProvider, that provider must reference a cipher suite with the same ID.

+

Trait Implementations§

Source§

impl Clone for NoInitialCipherSuite

Source§

fn clone(&self) -> NoInitialCipherSuite

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for NoInitialCipherSuite

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for NoInitialCipherSuite

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for NoInitialCipherSuite

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.QuicClientConfig.html b/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.QuicClientConfig.html new file mode 100644 index 000000000..bf409f691 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.QuicClientConfig.html @@ -0,0 +1,48 @@ +QuicClientConfig in iroh_quinn_proto::crypto::rustls - Rust

QuicClientConfig

Struct QuicClientConfig 

Source
pub struct QuicClientConfig { /* private fields */ }
Expand description

A QUIC-compatible TLS client configuration

+

Quinn implicitly constructs a QuicClientConfig with reasonable defaults within +ClientConfig::with_root_certificates() and ClientConfig::try_with_platform_verifier(). +Alternatively, QuicClientConfig’s TryFrom implementation can be used to wrap around a +custom [rustls::ClientConfig], in which case care should be taken around certain points:

+
    +
  • If enable_early_data is not set to true, then sending 0-RTT data will not be possible on +outgoing connections.
  • +
  • The [rustls::ClientConfig] must have TLS 1.3 support enabled for conversion to succeed.
  • +
+

The object in the resumption field of the inner [rustls::ClientConfig] determines whether +calling into_0rtt on outgoing connections returns Ok or Err. It typically allows +into_0rtt to proceed if it recognizes the server name, and defaults to an in-memory cache of +256 server names.

+

Implementations§

Source§

impl QuicClientConfig

Source

pub fn with_initial( + inner: Arc<ClientConfig>, + initial: Suite, +) -> Result<Self, NoInitialCipherSuite>

Initialize a QUIC-compatible TLS client configuration with a separate initial cipher suite

+

This is useful if you want to avoid the initial cipher suite for traffic encryption.

+

Trait Implementations§

Source§

impl ClientConfig for QuicClientConfig

Source§

fn start_session( + self: Arc<Self>, + version: u32, + server_name: &str, + params: &TransportParameters, +) -> Result<Box<dyn Session>, ConnectError>

Start a client session with this configuration
Source§

impl TryFrom<Arc<ClientConfig>> for QuicClientConfig

Source§

type Error = NoInitialCipherSuite

The type returned in the event of a conversion error.
Source§

fn try_from(inner: Arc<ClientConfig>) -> Result<Self, Self::Error>

Performs the conversion.
Source§

impl TryFrom<ClientConfig> for QuicClientConfig

Source§

type Error = NoInitialCipherSuite

The type returned in the event of a conversion error.
Source§

fn try_from(inner: ClientConfig) -> Result<Self, Self::Error>

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.QuicServerConfig.html b/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.QuicServerConfig.html new file mode 100644 index 000000000..34b5a40a5 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.QuicServerConfig.html @@ -0,0 +1,52 @@ +QuicServerConfig in iroh_quinn_proto::crypto::rustls - Rust

QuicServerConfig

Struct QuicServerConfig 

Source
pub struct QuicServerConfig { /* private fields */ }
Expand description

A QUIC-compatible TLS server configuration

+

Quinn implicitly constructs a QuicServerConfig with reasonable defaults within +ServerConfig::with_single_cert(). Alternatively, QuicServerConfig’s TryFrom +implementation or with_initial method can be used to wrap around a custom +[rustls::ServerConfig], in which case care should be taken around certain points:

+
    +
  • If max_early_data_size is not set to u32::MAX, the server will not be able to accept +incoming 0-RTT data. QUIC prohibits max_early_data_size values other than 0 or u32::MAX.
  • +
  • The rustls::ServerConfig must have TLS 1.3 support enabled for conversion to succeed.
  • +
+

Implementations§

Source§

impl QuicServerConfig

Source

pub fn with_initial( + inner: Arc<ServerConfig>, + initial: Suite, +) -> Result<Self, NoInitialCipherSuite>

Initialize a QUIC-compatible TLS client configuration with a separate initial cipher suite

+

This is useful if you want to avoid the initial cipher suite for traffic encryption.

+

Trait Implementations§

Source§

impl ServerConfig for QuicServerConfig

Source§

fn start_session( + self: Arc<Self>, + version: u32, + params: &TransportParameters, +) -> Box<dyn Session>

Start a server session with this configuration Read more
Source§

fn initial_keys( + &self, + version: u32, + dst_cid: ConnectionId, +) -> Result<Keys, UnsupportedVersion>

Create the initial set of keys given the client’s initial destination ConnectionId
Source§

fn retry_tag( + &self, + version: u32, + orig_dst_cid: ConnectionId, + packet: &[u8], +) -> [u8; 16]

Generate the integrity tag for a retry packet Read more
Source§

impl TryFrom<Arc<ServerConfig>> for QuicServerConfig

Source§

type Error = NoInitialCipherSuite

The type returned in the event of a conversion error.
Source§

fn try_from(inner: Arc<ServerConfig>) -> Result<Self, Self::Error>

Performs the conversion.
Source§

impl TryFrom<ServerConfig> for QuicServerConfig

Source§

type Error = NoInitialCipherSuite

The type returned in the event of a conversion error.
Source§

fn try_from(inner: ServerConfig) -> Result<Self, Self::Error>

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.TlsSession.html b/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.TlsSession.html new file mode 100644 index 000000000..5706bc84a --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/rustls/struct.TlsSession.html @@ -0,0 +1,40 @@ +TlsSession in iroh_quinn_proto::crypto::rustls - Rust

TlsSession

Struct TlsSession 

Source
pub struct TlsSession { /* private fields */ }
Expand description

A rustls TLS session

+

Trait Implementations§

Source§

impl Session for TlsSession

Source§

fn peer_identity(&self) -> Option<Box<dyn Any>>

For the rustls TlsSession, the Any type is Vec<rustls::pki_types::CertificateDer>

+
Source§

fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys

Create the initial set of keys given the client’s initial destination ConnectionId
Source§

fn handshake_data(&self) -> Option<Box<dyn Any>>

Get data negotiated during the handshake, if available Read more
Source§

fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>

Get the 0-RTT keys if available (clients only) Read more
Source§

fn early_data_accepted(&self) -> Option<bool>

If the 0-RTT-encrypted data has been accepted by the peer
Source§

fn is_handshaking(&self) -> bool

Returns true until the connection is fully established.
Source§

fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError>

Read bytes of handshake data Read more
Source§

fn transport_parameters( + &self, +) -> Result<Option<TransportParameters>, TransportError>

The peer’s QUIC transport parameters Read more
Source§

fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>

Writes handshake bytes into the given buffer and optionally returns the negotiated keys Read more
Source§

fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>

Compute keys for the next key update
Source§

fn is_valid_retry( + &self, + orig_dst_cid: ConnectionId, + header: &[u8], + payload: &[u8], +) -> bool

Verify the integrity of a retry packet
Source§

fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: &[u8], +) -> Result<(), ExportKeyingMaterialError>

Fill output with output.len() bytes of keying material derived +from the Session’s secrets, using label and context for domain +separation. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/sidebar-items.js b/pr/395/docs/iroh_quinn_proto/crypto/sidebar-items.js new file mode 100644 index 000000000..375413e92 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["rustls"],"struct":["CryptoError","ExportKeyingMaterialError","KeyPair","Keys","UnsupportedVersion"],"trait":["AeadKey","ClientConfig","HandshakeTokenKey","HeaderKey","HmacKey","PacketKey","ServerConfig","Session"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/struct.CryptoError.html b/pr/395/docs/iroh_quinn_proto/crypto/struct.CryptoError.html new file mode 100644 index 000000000..7f9be178c --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/struct.CryptoError.html @@ -0,0 +1,25 @@ +CryptoError in iroh_quinn_proto::crypto - Rust

CryptoError

Struct CryptoError 

Source
pub struct CryptoError;
Expand description

Generic crypto errors

+

Trait Implementations§

Source§

impl Debug for CryptoError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<Unspecified> for CryptoError

Source§

fn from(_: Unspecified) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/struct.ExportKeyingMaterialError.html b/pr/395/docs/iroh_quinn_proto/crypto/struct.ExportKeyingMaterialError.html new file mode 100644 index 000000000..48a5a8fef --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/struct.ExportKeyingMaterialError.html @@ -0,0 +1,31 @@ +ExportKeyingMaterialError in iroh_quinn_proto::crypto - Rust

ExportKeyingMaterialError

Struct ExportKeyingMaterialError 

Source
pub struct ExportKeyingMaterialError;
Expand description

Error returned by Session::export_keying_material.

+

This error occurs if the requested output length is too large.

+

Trait Implementations§

Source§

impl Debug for ExportKeyingMaterialError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for ExportKeyingMaterialError

Source§

fn eq(&self, other: &ExportKeyingMaterialError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ExportKeyingMaterialError

Source§

impl StructuralPartialEq for ExportKeyingMaterialError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/struct.KeyPair.html b/pr/395/docs/iroh_quinn_proto/crypto/struct.KeyPair.html new file mode 100644 index 000000000..6b5e804fd --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/struct.KeyPair.html @@ -0,0 +1,36 @@ +KeyPair in iroh_quinn_proto::crypto - Rust

KeyPair

Struct KeyPair 

Source
pub struct KeyPair<T> {
+    pub local: T,
+    pub remote: T,
+}
Expand description

A pair of keys for bidirectional communication

+

Fields§

§local: T

Key for encrypting data

+
§remote: T

Key for decrypting data

+

Auto Trait Implementations§

§

impl<T> Freeze for KeyPair<T>
where + T: Freeze,

§

impl<T> RefUnwindSafe for KeyPair<T>
where + T: RefUnwindSafe,

§

impl<T> Send for KeyPair<T>
where + T: Send,

§

impl<T> Sync for KeyPair<T>
where + T: Sync,

§

impl<T> Unpin for KeyPair<T>
where + T: Unpin,

§

impl<T> UnwindSafe for KeyPair<T>
where + T: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/struct.Keys.html b/pr/395/docs/iroh_quinn_proto/crypto/struct.Keys.html new file mode 100644 index 000000000..2d9b3fc3e --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/struct.Keys.html @@ -0,0 +1,30 @@ +Keys in iroh_quinn_proto::crypto - Rust

Keys

Struct Keys 

Source
pub struct Keys {
+    pub header: KeyPair<Box<dyn HeaderKey>>,
+    pub packet: KeyPair<Box<dyn PacketKey>>,
+}
Expand description

A complete set of keys for a certain packet space

+

Fields§

§header: KeyPair<Box<dyn HeaderKey>>

Header protection keys

+
§packet: KeyPair<Box<dyn PacketKey>>

Packet protection keys

+

Auto Trait Implementations§

§

impl Freeze for Keys

§

impl !RefUnwindSafe for Keys

§

impl Send for Keys

§

impl Sync for Keys

§

impl Unpin for Keys

§

impl !UnwindSafe for Keys

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/struct.UnsupportedVersion.html b/pr/395/docs/iroh_quinn_proto/crypto/struct.UnsupportedVersion.html new file mode 100644 index 000000000..1a566decd --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/struct.UnsupportedVersion.html @@ -0,0 +1,25 @@ +UnsupportedVersion in iroh_quinn_proto::crypto - Rust

UnsupportedVersion

Struct UnsupportedVersion 

Source
pub struct UnsupportedVersion;
Expand description

Error indicating that the specified QUIC version is not supported

+

Trait Implementations§

Source§

impl Debug for UnsupportedVersion

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<UnsupportedVersion> for ConnectError

Source§

fn from(_: UnsupportedVersion) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/trait.AeadKey.html b/pr/395/docs/iroh_quinn_proto/crypto/trait.AeadKey.html new file mode 100644 index 000000000..fa5f150b2 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/trait.AeadKey.html @@ -0,0 +1,32 @@ +AeadKey in iroh_quinn_proto::crypto - Rust

AeadKey

Trait AeadKey 

Source
pub trait AeadKey {
+    // Required methods
+    fn seal(
+        &self,
+        data: &mut Vec<u8>,
+        additional_data: &[u8],
+    ) -> Result<(), CryptoError>;
+    fn open<'a>(
+        &self,
+        data: &'a mut [u8],
+        additional_data: &[u8],
+    ) -> Result<&'a mut [u8], CryptoError>;
+}
Expand description

A key for sealing data with AEAD-based algorithms

+

Required Methods§

Source

fn seal( + &self, + data: &mut Vec<u8>, + additional_data: &[u8], +) -> Result<(), CryptoError>

Method for sealing message data

+
Source

fn open<'a>( + &self, + data: &'a mut [u8], + additional_data: &[u8], +) -> Result<&'a mut [u8], CryptoError>

Method for opening a sealed message data

+

Implementations on Foreign Types§

Source§

impl AeadKey for LessSafeKey

Source§

fn seal( + &self, + data: &mut Vec<u8>, + additional_data: &[u8], +) -> Result<(), CryptoError>

Source§

fn open<'a>( + &self, + data: &'a mut [u8], + additional_data: &[u8], +) -> Result<&'a mut [u8], CryptoError>

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/trait.ClientConfig.html b/pr/395/docs/iroh_quinn_proto/crypto/trait.ClientConfig.html new file mode 100644 index 000000000..4e7283af4 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/trait.ClientConfig.html @@ -0,0 +1,16 @@ +ClientConfig in iroh_quinn_proto::crypto - Rust

ClientConfig

Trait ClientConfig 

Source
pub trait ClientConfig: Send + Sync {
+    // Required method
+    fn start_session(
+        self: Arc<Self>,
+        version: u32,
+        server_name: &str,
+        params: &TransportParameters,
+    ) -> Result<Box<dyn Session>, ConnectError>;
+}
Expand description

Client-side configuration for the crypto protocol

+

Required Methods§

Source

fn start_session( + self: Arc<Self>, + version: u32, + server_name: &str, + params: &TransportParameters, +) -> Result<Box<dyn Session>, ConnectError>

Start a client session with this configuration

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.html b/pr/395/docs/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.html new file mode 100644 index 000000000..c5b4b0d53 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.html @@ -0,0 +1,6 @@ +HandshakeTokenKey in iroh_quinn_proto::crypto - Rust

HandshakeTokenKey

Trait HandshakeTokenKey 

Source
pub trait HandshakeTokenKey: Send + Sync {
+    // Required method
+    fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>;
+}
Expand description

A pseudo random key for HKDF

+

Required Methods§

Source

fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>

Derive AEAD using hkdf

+

Implementations on Foreign Types§

Source§

impl HandshakeTokenKey for Prk

Source§

fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/trait.HeaderKey.html b/pr/395/docs/iroh_quinn_proto/crypto/trait.HeaderKey.html new file mode 100644 index 000000000..1ce632348 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/trait.HeaderKey.html @@ -0,0 +1,10 @@ +HeaderKey in iroh_quinn_proto::crypto - Rust

HeaderKey

Trait HeaderKey 

Source
pub trait HeaderKey: Send + Sync {
+    // Required methods
+    fn decrypt(&self, pn_offset: usize, packet: &mut [u8]);
+    fn encrypt(&self, pn_offset: usize, packet: &mut [u8]);
+    fn sample_size(&self) -> usize;
+}
Expand description

Keys used to protect packet headers

+

Required Methods§

Source

fn decrypt(&self, pn_offset: usize, packet: &mut [u8])

Decrypt the given packet’s header

+
Source

fn encrypt(&self, pn_offset: usize, packet: &mut [u8])

Encrypt the given packet’s header

+
Source

fn sample_size(&self) -> usize

The sample size used for this key’s algorithm

+

Implementations on Foreign Types§

Source§

impl HeaderKey for Box<dyn HeaderProtectionKey>

Source§

fn decrypt(&self, pn_offset: usize, packet: &mut [u8])

Source§

fn encrypt(&self, pn_offset: usize, packet: &mut [u8])

Source§

fn sample_size(&self) -> usize

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/trait.HmacKey.html b/pr/395/docs/iroh_quinn_proto/crypto/trait.HmacKey.html new file mode 100644 index 000000000..6c0bb1c0f --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/trait.HmacKey.html @@ -0,0 +1,10 @@ +HmacKey in iroh_quinn_proto::crypto - Rust

HmacKey

Trait HmacKey 

Source
pub trait HmacKey: Send + Sync {
+    // Required methods
+    fn sign(&self, data: &[u8], signature_out: &mut [u8]);
+    fn signature_len(&self) -> usize;
+    fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>;
+}
Expand description

A key for signing with HMAC-based algorithms

+

Required Methods§

Source

fn sign(&self, data: &[u8], signature_out: &mut [u8])

Method for signing a message

+
Source

fn signature_len(&self) -> usize

Length of sign’s output

+
Source

fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>

Method for verifying a message

+

Implementations on Foreign Types§

Source§

impl HmacKey for Key

Source§

fn sign(&self, data: &[u8], out: &mut [u8])

Source§

fn signature_len(&self) -> usize

Source§

fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/trait.PacketKey.html b/pr/395/docs/iroh_quinn_proto/crypto/trait.PacketKey.html new file mode 100644 index 000000000..795333f06 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/trait.PacketKey.html @@ -0,0 +1,51 @@ +PacketKey in iroh_quinn_proto::crypto - Rust

PacketKey

Trait PacketKey 

Source
pub trait PacketKey: Send + Sync {
+    // Required methods
+    fn encrypt(
+        &self,
+        path_id: PathId,
+        packet: u64,
+        buf: &mut [u8],
+        header_len: usize,
+    );
+    fn decrypt(
+        &self,
+        path_id: PathId,
+        packet: u64,
+        header: &[u8],
+        payload: &mut BytesMut,
+    ) -> Result<(), CryptoError>;
+    fn tag_len(&self) -> usize;
+    fn confidentiality_limit(&self) -> u64;
+    fn integrity_limit(&self) -> u64;
+}
Expand description

Keys used to protect packet payloads

+

Required Methods§

Source

fn encrypt( + &self, + path_id: PathId, + packet: u64, + buf: &mut [u8], + header_len: usize, +)

Encrypt the packet payload with the given packet number

+
Source

fn decrypt( + &self, + path_id: PathId, + packet: u64, + header: &[u8], + payload: &mut BytesMut, +) -> Result<(), CryptoError>

Decrypt the packet payload with the given packet number

+
Source

fn tag_len(&self) -> usize

The length of the AEAD tag appended to packets on encryption

+
Source

fn confidentiality_limit(&self) -> u64

Maximum number of packets that may be sent using a single key

+
Source

fn integrity_limit(&self) -> u64

Maximum number of incoming packets that may fail decryption before the connection must be +abandoned

+

Implementations on Foreign Types§

Source§

impl PacketKey for Box<dyn PacketKey>

Source§

fn encrypt( + &self, + path_id: PathId, + packet: u64, + buf: &mut [u8], + header_len: usize, +)

Source§

fn decrypt( + &self, + path_id: PathId, + packet: u64, + header: &[u8], + payload: &mut BytesMut, +) -> Result<(), CryptoError>

Source§

fn tag_len(&self) -> usize

Source§

fn confidentiality_limit(&self) -> u64

Source§

fn integrity_limit(&self) -> u64

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/trait.ServerConfig.html b/pr/395/docs/iroh_quinn_proto/crypto/trait.ServerConfig.html new file mode 100644 index 000000000..eff1d38d6 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/trait.ServerConfig.html @@ -0,0 +1,38 @@ +ServerConfig in iroh_quinn_proto::crypto - Rust

ServerConfig

Trait ServerConfig 

Source
pub trait ServerConfig: Send + Sync {
+    // Required methods
+    fn initial_keys(
+        &self,
+        version: u32,
+        dst_cid: ConnectionId,
+    ) -> Result<Keys, UnsupportedVersion>;
+    fn retry_tag(
+        &self,
+        version: u32,
+        orig_dst_cid: ConnectionId,
+        packet: &[u8],
+    ) -> [u8; 16];
+    fn start_session(
+        self: Arc<Self>,
+        version: u32,
+        params: &TransportParameters,
+    ) -> Box<dyn Session>;
+}
Expand description

Server-side configuration for the crypto protocol

+

Required Methods§

Source

fn initial_keys( + &self, + version: u32, + dst_cid: ConnectionId, +) -> Result<Keys, UnsupportedVersion>

Create the initial set of keys given the client’s initial destination ConnectionId

+
Source

fn retry_tag( + &self, + version: u32, + orig_dst_cid: ConnectionId, + packet: &[u8], +) -> [u8; 16]

Generate the integrity tag for a retry packet

+

Never called if initial_keys rejected version.

+
Source

fn start_session( + self: Arc<Self>, + version: u32, + params: &TransportParameters, +) -> Box<dyn Session>

Start a server session with this configuration

+

Never called if initial_keys rejected version.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/crypto/trait.Session.html b/pr/395/docs/iroh_quinn_proto/crypto/trait.Session.html new file mode 100644 index 000000000..d1660b288 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/crypto/trait.Session.html @@ -0,0 +1,72 @@ +Session in iroh_quinn_proto::crypto - Rust

Session

Trait Session 

Source
pub trait Session:
+    Send
+    + Sync
+    + 'static {
+    // Required methods
+    fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys;
+    fn handshake_data(&self) -> Option<Box<dyn Any>>;
+    fn peer_identity(&self) -> Option<Box<dyn Any>>;
+    fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>;
+    fn early_data_accepted(&self) -> Option<bool>;
+    fn is_handshaking(&self) -> bool;
+    fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError>;
+    fn transport_parameters(
+        &self,
+    ) -> Result<Option<TransportParameters>, TransportError>;
+    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>;
+    fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>;
+    fn is_valid_retry(
+        &self,
+        orig_dst_cid: ConnectionId,
+        header: &[u8],
+        payload: &[u8],
+    ) -> bool;
+    fn export_keying_material(
+        &self,
+        output: &mut [u8],
+        label: &[u8],
+        context: &[u8],
+    ) -> Result<(), ExportKeyingMaterialError>;
+}
Expand description

A cryptographic session (commonly TLS)

+

Required Methods§

Source

fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys

Create the initial set of keys given the client’s initial destination ConnectionId

+
Source

fn handshake_data(&self) -> Option<Box<dyn Any>>

Get data negotiated during the handshake, if available

+

Returns None until the connection emits HandshakeDataReady.

+
Source

fn peer_identity(&self) -> Option<Box<dyn Any>>

Get the peer’s identity, if available

+
Source

fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>

Get the 0-RTT keys if available (clients only)

+

On the client side, this method can be used to see if 0-RTT key material is available +to start sending data before the protocol handshake has completed.

+

Returns None if the key material is not available. This might happen if you have +not connected to this server before.

+
Source

fn early_data_accepted(&self) -> Option<bool>

If the 0-RTT-encrypted data has been accepted by the peer

+
Source

fn is_handshaking(&self) -> bool

Returns true until the connection is fully established.

+
Source

fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError>

Read bytes of handshake data

+

This should be called with the contents of CRYPTO frames. If it returns Ok, the +caller should call write_handshake() to check if the crypto protocol has anything +to send to the peer. This method will only return true the first time that +handshake data is available. Future calls will always return false.

+

On success, returns true iff self.handshake_data() has been populated.

+
Source

fn transport_parameters( + &self, +) -> Result<Option<TransportParameters>, TransportError>

The peer’s QUIC transport parameters

+

These are only available after the first flight from the peer has been received.

+
Source

fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>

Writes handshake bytes into the given buffer and optionally returns the negotiated keys

+

When the handshake proceeds to the next phase, this method will return a new set of +keys to encrypt data with.

+
Source

fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>

Compute keys for the next key update

+
Source

fn is_valid_retry( + &self, + orig_dst_cid: ConnectionId, + header: &[u8], + payload: &[u8], +) -> bool

Verify the integrity of a retry packet

+
Source

fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: &[u8], +) -> Result<(), ExportKeyingMaterialError>

Fill output with output.len() bytes of keying material derived +from the Session’s secrets, using label and context for domain +separation.

+

This function will fail, returning ExportKeyingMaterialError, +if the requested output length is too large.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/endpoint/enum.ConnectError.html b/pr/395/docs/iroh_quinn_proto/endpoint/enum.ConnectError.html new file mode 100644 index 000000000..d80e75b8e --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/endpoint/enum.ConnectError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.ConnectError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/endpoint/enum.DatagramEvent.html b/pr/395/docs/iroh_quinn_proto/endpoint/enum.DatagramEvent.html new file mode 100644 index 000000000..dd7b60907 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/endpoint/enum.DatagramEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.DatagramEvent.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/endpoint/struct.AcceptError.html b/pr/395/docs/iroh_quinn_proto/endpoint/struct.AcceptError.html new file mode 100644 index 000000000..dc615b45d --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/endpoint/struct.AcceptError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.AcceptError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/endpoint/struct.ConnectionHandle.html b/pr/395/docs/iroh_quinn_proto/endpoint/struct.ConnectionHandle.html new file mode 100644 index 000000000..72a5a9b92 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/endpoint/struct.ConnectionHandle.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ConnectionHandle.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/endpoint/struct.Endpoint.html b/pr/395/docs/iroh_quinn_proto/endpoint/struct.Endpoint.html new file mode 100644 index 000000000..80d25b39b --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/endpoint/struct.Endpoint.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.Endpoint.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/endpoint/struct.Incoming.html b/pr/395/docs/iroh_quinn_proto/endpoint/struct.Incoming.html new file mode 100644 index 000000000..4c2ebf7d4 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/endpoint/struct.Incoming.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.Incoming.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/endpoint/struct.RetryError.html b/pr/395/docs/iroh_quinn_proto/endpoint/struct.RetryError.html new file mode 100644 index 000000000..8df786829 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/endpoint/struct.RetryError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.RetryError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.ClosePathError.html b/pr/395/docs/iroh_quinn_proto/enum.ClosePathError.html new file mode 100644 index 000000000..e82bdfee0 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.ClosePathError.html @@ -0,0 +1,38 @@ +ClosePathError in iroh_quinn_proto - Rust

ClosePathError

Enum ClosePathError 

Source
pub enum ClosePathError {
+    ClosedPath,
+    LastOpenPath,
+}
Expand description

Errors triggered when abandoning a path

+

Variants§

§

ClosedPath

The path is already closed or was never opened

+
§

LastOpenPath

This is the last path, which can not be abandoned

+

Trait Implementations§

Source§

impl Clone for ClosePathError

Source§

fn clone(&self) -> ClosePathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosePathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ClosePathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosePathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosePathError

Source§

fn eq(&self, other: &ClosePathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosePathError

Source§

impl StructuralPartialEq for ClosePathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.ConfigError.html b/pr/395/docs/iroh_quinn_proto/enum.ConfigError.html new file mode 100644 index 000000000..c4e00e418 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.ConfigError.html @@ -0,0 +1,36 @@ +ConfigError in iroh_quinn_proto - Rust

ConfigError

Enum ConfigError 

Source
#[non_exhaustive]
pub enum ConfigError { + OutOfBounds, +}
Expand description

Errors in the configuration of an endpoint

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

OutOfBounds

Value exceeds supported bounds

+

Trait Implementations§

Source§

impl Clone for ConfigError

Source§

fn clone(&self) -> ConfigError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConfigError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ConfigError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ConfigError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<TryFromIntError> for ConfigError

Source§

fn from(_: TryFromIntError) -> Self

Converts to this type from the input type.
Source§

impl From<VarIntBoundsExceeded> for ConfigError

Source§

fn from(_: VarIntBoundsExceeded) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConfigError

Source§

fn eq(&self, other: &ConfigError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConfigError

Source§

impl StructuralPartialEq for ConfigError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.ConnectError.html b/pr/395/docs/iroh_quinn_proto/enum.ConnectError.html new file mode 100644 index 000000000..5710f8f3d --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.ConnectError.html @@ -0,0 +1,51 @@ +ConnectError in iroh_quinn_proto - Rust

ConnectError

Enum ConnectError 

Source
pub enum ConnectError {
+    EndpointStopping,
+    CidsExhausted,
+    InvalidServerName(String),
+    InvalidRemoteAddress(SocketAddr),
+    NoDefaultClientConfig,
+    UnsupportedVersion,
+}
Expand description

Errors in the parameters being used to create a new connection

+

These arise before any I/O has been performed.

+

Variants§

§

EndpointStopping

The endpoint can no longer create new connections

+

Indicates that a necessary component of the endpoint has been dropped or otherwise disabled.

+
§

CidsExhausted

The connection could not be created because not enough of the CID space is available

+

Try using longer connection IDs

+
§

InvalidServerName(String)

The given server name was malformed

+
§

InvalidRemoteAddress(SocketAddr)

The remote SocketAddr supplied was malformed

+

Examples include attempting to connect to port 0, or using an inappropriate address family.

+
§

NoDefaultClientConfig

No default client configuration was set up

+

Use Endpoint::connect_with to specify a client configuration.

+
§

UnsupportedVersion

The local endpoint does not support the QUIC version specified in the client configuration

+

Trait Implementations§

Source§

impl Clone for ConnectError

Source§

fn clone(&self) -> ConnectError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<UnsupportedVersion> for ConnectError

Source§

fn from(_: UnsupportedVersion) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConnectError

Source§

fn eq(&self, other: &ConnectError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConnectError

Source§

impl StructuralPartialEq for ConnectError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.ConnectionError.html b/pr/395/docs/iroh_quinn_proto/enum.ConnectionError.html new file mode 100644 index 000000000..937f94495 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.ConnectionError.html @@ -0,0 +1,54 @@ +ConnectionError in iroh_quinn_proto - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
+    VersionMismatch,
+    TransportError(TransportError),
+    ConnectionClosed(ConnectionClose),
+    ApplicationClosed(ApplicationClose),
+    Reset,
+    TimedOut,
+    LocallyClosed,
+    CidsExhausted,
+}
Expand description

Reasons why a connection might be lost

+

Variants§

§

VersionMismatch

The peer doesn’t implement any supported version

+
§

TransportError(TransportError)

The peer violated the QUIC specification as understood by this implementation

+
§

ConnectionClosed(ConnectionClose)

The peer’s QUIC stack aborted the connection automatically

+
§

ApplicationClosed(ApplicationClose)

The peer closed the connection

+
§

Reset

The peer is unable to continue processing this connection, usually due to having restarted

+
§

TimedOut

Communication with the peer has lapsed for longer than the negotiated idle timeout

+

If neither side is sending keep-alives, a connection will time out after a long enough idle +period even if the peer is still reachable. See also TransportConfig::max_idle_timeout() +and TransportConfig::keep_alive_interval().

+
§

LocallyClosed

The local application closed the connection

+
§

CidsExhausted

The connection could not be created because not enough of the CID space is available

+

Try using longer connection IDs.

+

Trait Implementations§

Source§

impl Clone for ConnectionError

Source§

fn clone(&self) -> ConnectionError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectionError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectionError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ConnectionError> for Error

Source§

fn from(x: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: TransportError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionError

Source§

fn eq(&self, other: &ConnectionError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConnectionError

Source§

impl StructuralPartialEq for ConnectionError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.DatagramEvent.html b/pr/395/docs/iroh_quinn_proto/enum.DatagramEvent.html new file mode 100644 index 000000000..6a29b6ae0 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.DatagramEvent.html @@ -0,0 +1,32 @@ +DatagramEvent in iroh_quinn_proto - Rust

DatagramEvent

Enum DatagramEvent 

Source
pub enum DatagramEvent {
+    ConnectionEvent(ConnectionHandle, ConnectionEvent),
+    NewConnection(Incoming),
+    Response(Transmit),
+}
Expand description

Event resulting from processing a single datagram

+

Variants§

§

ConnectionEvent(ConnectionHandle, ConnectionEvent)

The datagram is redirected to its Connection

+
§

NewConnection(Incoming)

The datagram may result in starting a new Connection

+
§

Response(Transmit)

Response generated directly by the endpoint

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.Dir.html b/pr/395/docs/iroh_quinn_proto/enum.Dir.html new file mode 100644 index 000000000..2ff63faf2 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.Dir.html @@ -0,0 +1,53 @@ +Dir in iroh_quinn_proto - Rust

Dir

Enum Dir 

Source
pub enum Dir {
+    Bi = 0,
+    Uni = 1,
+}
Expand description

Whether a stream communicates data in both directions or only from the initiator

+

Variants§

§

Bi = 0

Data flows in both directions

+
§

Uni = 1

Data flows only from the stream’s initiator

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for Dir

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the given unstructured data. Read more
Source§

fn arbitrary_take_rest(u: Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
Source§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for Dir

Source§

fn clone(&self) -> Dir

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Dir

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Dir

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<Dir> for StreamType

Available on crate feature qlog only.
Source§

fn from(value: Dir) -> Self

Converts to this type from the input type.
Source§

impl Hash for Dir

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for Dir

Source§

fn cmp(&self, other: &Dir) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for Dir

Source§

fn eq(&self, other: &Dir) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for Dir

Source§

fn partial_cmp(&self, other: &Dir) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for Dir

Source§

impl Eq for Dir

Source§

impl StructuralPartialEq for Dir

Auto Trait Implementations§

§

impl Freeze for Dir

§

impl RefUnwindSafe for Dir

§

impl Send for Dir

§

impl Sync for Dir

§

impl Unpin for Dir

§

impl UnwindSafe for Dir

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.EcnCodepoint.html b/pr/395/docs/iroh_quinn_proto/enum.EcnCodepoint.html new file mode 100644 index 000000000..7032e36a3 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.EcnCodepoint.html @@ -0,0 +1,41 @@ +EcnCodepoint in iroh_quinn_proto - Rust

EcnCodepoint

Enum EcnCodepoint 

Source
#[repr(u8)]
pub enum EcnCodepoint { + Ect0 = 2, + Ect1 = 1, + Ce = 3, +}
Expand description

Explicit congestion notification codepoint

+

Variants§

§

Ect0 = 2

The ECT(0) codepoint, indicating that an endpoint is ECN-capable

+
§

Ect1 = 1

The ECT(1) codepoint, indicating that an endpoint is ECN-capable

+
§

Ce = 3

The CE codepoint, signalling that congestion was experienced

+

Implementations§

Source§

impl EcnCodepoint

Source

pub fn from_bits(x: u8) -> Option<Self>

Create new object from the given bits

+
Source

pub fn is_ce(self) -> bool

Returns whether the codepoint is a CE, signalling that congestion was experienced

+

Trait Implementations§

Source§

impl Clone for EcnCodepoint

Source§

fn clone(&self) -> EcnCodepoint

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EcnCodepoint

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for EcnCodepoint

Source§

fn eq(&self, other: &EcnCodepoint) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for EcnCodepoint

Source§

impl Eq for EcnCodepoint

Source§

impl StructuralPartialEq for EcnCodepoint

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.Event.html b/pr/395/docs/iroh_quinn_proto/enum.Event.html new file mode 100644 index 000000000..e800b25a5 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.Event.html @@ -0,0 +1,48 @@ +Event in iroh_quinn_proto - Rust

Event

Enum Event 

Source
pub enum Event {
+    HandshakeDataReady,
+    Connected,
+    HandshakeConfirmed,
+    ConnectionLost {
+        reason: ConnectionError,
+    },
+    Stream(StreamEvent),
+    DatagramReceived,
+    DatagramsUnblocked,
+    Path(PathEvent),
+    NatTraversal(Event),
+}
Expand description

Events of interest to the application

+

Variants§

§

HandshakeDataReady

The connection’s handshake data is ready

+
§

Connected

The connection was successfully established

+
§

HandshakeConfirmed

The TLS handshake was confirmed

+
§

ConnectionLost

The connection was lost

+

Emitted if the peer closes the connection or an error is encountered.

+

Fields

§reason: ConnectionError

Reason that the connection was closed

+
§

Stream(StreamEvent)

Stream events

+
§

DatagramReceived

One or more application datagrams have been received

+
§

DatagramsUnblocked

One or more application datagrams have been sent after blocking

+
§

Path(PathEvent)

(Multi)Path events

+
§

NatTraversal(Event)

Iroh’s nat traversal events

+

Trait Implementations§

Source§

impl Debug for Event

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<PathEvent> for Event

Source§

fn from(source: PathEvent) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl !Freeze for Event

§

impl !RefUnwindSafe for Event

§

impl Send for Event

§

impl Sync for Event

§

impl Unpin for Event

§

impl !UnwindSafe for Event

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.FinishError.html b/pr/395/docs/iroh_quinn_proto/enum.FinishError.html new file mode 100644 index 000000000..1838c6736 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.FinishError.html @@ -0,0 +1,40 @@ +FinishError in iroh_quinn_proto - Rust

FinishError

Enum FinishError 

Source
pub enum FinishError {
+    Stopped(VarInt),
+    ClosedStream,
+}
Expand description

Reasons why attempting to finish a stream might fail

+

Variants§

§

Stopped(VarInt)

The peer is no longer accepting data on this stream. No +StreamEvent::Finished event will be emitted for this stream.

+

Carries an application-defined error code.

+
§

ClosedStream

The stream has not been opened or was already finished or reset

+

Trait Implementations§

Source§

impl Clone for FinishError

Source§

fn clone(&self) -> FinishError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FinishError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for FinishError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for FinishError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for FinishError

Source§

fn eq(&self, other: &FinishError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for FinishError

Source§

impl StructuralPartialEq for FinishError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.FrameType.html b/pr/395/docs/iroh_quinn_proto/enum.FrameType.html new file mode 100644 index 000000000..69b90270e --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.FrameType.html @@ -0,0 +1,77 @@ +FrameType in iroh_quinn_proto - Rust

FrameType

Enum FrameType 

Source
pub enum FrameType {
+
Show 44 variants Padding, + Ping, + Ack, + AckEcn, + ResetStream, + StopSending, + Crypto, + NewToken, + Stream(StreamInfo), + MaxData, + MaxStreamData, + MaxStreamsBidi, + MaxStreamsUni, + DataBlocked, + StreamDataBlocked, + StreamsBlockedBidi, + StreamsBlockedUni, + NewConnectionId, + RetireConnectionId, + PathChallenge, + PathResponse, + ConnectionClose, + ApplicationClose, + HandshakeDone, + AckFrequency, + ImmediateAck, + Datagram(DatagramInfo), + ObservedIpv4Addr, + ObservedIpv6Addr, + PathAck, + PathAckEcn, + PathAbandon, + PathStatusBackup, + PathStatusAvailable, + PathNewConnectionId, + PathRetireConnectionId, + MaxPathId, + PathsBlocked, + PathCidsBlocked, + AddIpv4Address, + AddIpv6Address, + ReachOutAtIpv4, + ReachOutAtIpv6, + RemoveAddress, +
}

Variants§

§

Padding

§

Ping

§

Ack

§

AckEcn

§

ResetStream

§

StopSending

§

Crypto

§

NewToken

§

Stream(StreamInfo)

§

MaxData

§

MaxStreamData

§

MaxStreamsBidi

§

MaxStreamsUni

§

DataBlocked

§

StreamDataBlocked

§

StreamsBlockedBidi

§

StreamsBlockedUni

§

NewConnectionId

§

RetireConnectionId

§

PathChallenge

§

PathResponse

§

ConnectionClose

§

ApplicationClose

§

HandshakeDone

§

AckFrequency

§

ImmediateAck

§

Datagram(DatagramInfo)

§

ObservedIpv4Addr

§

ObservedIpv6Addr

§

PathAck

§

PathAckEcn

§

PathAbandon

§

PathStatusBackup

§

PathStatusAvailable

§

PathNewConnectionId

§

PathRetireConnectionId

§

MaxPathId

§

PathsBlocked

§

PathCidsBlocked

§

AddIpv4Address

§

AddIpv6Address

§

ReachOutAtIpv4

§

ReachOutAtIpv6

§

RemoveAddress

Trait Implementations§

Source§

impl Clone for FrameType

Source§

fn clone(&self) -> FrameType

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FrameType

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Decodable for FrameType

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for FrameType

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Encodable for FrameType

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer.
Source§

impl PartialEq for FrameType

Source§

fn eq(&self, other: &FrameType) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl TryFrom<u64> for FrameType

Source§

type Error = InvalidFrameId

The type returned in the event of a conversion error.
Source§

fn try_from(value: u64) -> Result<Self, Self::Error>

Performs the conversion.
Source§

impl Copy for FrameType

Source§

impl Eq for FrameType

Source§

impl StructuralPartialEq for FrameType

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.LongType.html b/pr/395/docs/iroh_quinn_proto/enum.LongType.html new file mode 100644 index 000000000..9ad4ac070 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.LongType.html @@ -0,0 +1,37 @@ +LongType in iroh_quinn_proto - Rust

LongType

Enum LongType 

Source
pub enum LongType {
+    Handshake,
+    ZeroRtt,
+}
Expand description

Long packet types with uniform header structure

+

Variants§

§

Handshake

Handshake packet

+
§

ZeroRtt

0-RTT packet

+

Trait Implementations§

Source§

impl Clone for LongType

Source§

fn clone(&self) -> LongType

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for LongType

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for LongType

Source§

fn eq(&self, other: &LongType) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for LongType

Source§

impl Eq for LongType

Source§

impl StructuralPartialEq for LongType

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.MaybeFrame.html b/pr/395/docs/iroh_quinn_proto/enum.MaybeFrame.html new file mode 100644 index 000000000..9c9fd84f4 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.MaybeFrame.html @@ -0,0 +1,40 @@ +MaybeFrame in iroh_quinn_proto - Rust

MaybeFrame

Enum MaybeFrame 

Source
pub enum MaybeFrame {
+    None,
+    Unknown(u64),
+    Known(FrameType),
+}
Expand description

The type used to refer to FrameTypes in closing and transport errors.

+

Variants§

§

None

Not attributed to any particular FrameType.

+
§

Unknown(u64)

Attributed to some frame type this implementation does not recognize.

+
§

Known(FrameType)

Attributed to a specific FrameType, never FrameType::Padding.

+

Trait Implementations§

Source§

impl Clone for MaybeFrame

Source§

fn clone(&self) -> MaybeFrame

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MaybeFrame

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Decodable for MaybeFrame

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for MaybeFrame

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Encodable for MaybeFrame

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer.
Source§

impl PartialEq for MaybeFrame

Source§

fn eq(&self, other: &MaybeFrame) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for MaybeFrame

Source§

impl Eq for MaybeFrame

Source§

impl StructuralPartialEq for MaybeFrame

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.PacketDecodeError.html b/pr/395/docs/iroh_quinn_proto/enum.PacketDecodeError.html new file mode 100644 index 000000000..ff99a4e25 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.PacketDecodeError.html @@ -0,0 +1,55 @@ +PacketDecodeError in iroh_quinn_proto - Rust

PacketDecodeError

Enum PacketDecodeError 

Source
pub enum PacketDecodeError {
+    UnsupportedVersion {
+        src_cid: ConnectionId,
+        dst_cid: ConnectionId,
+        version: u32,
+    },
+    InvalidHeader(&'static str),
+}
Expand description

Packet decode error

+

Variants§

§

UnsupportedVersion

Packet uses a QUIC version that is not supported

+

Fields

§src_cid: ConnectionId

Source Connection ID

+
§dst_cid: ConnectionId

Destination Connection ID

+
§version: u32

The version that was unsupported

+
§

InvalidHeader(&'static str)

The packet header is invalid

+

Trait Implementations§

Source§

impl Clone for PacketDecodeError

Source§

fn clone(&self) -> PacketDecodeError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PacketDecodeError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for PacketDecodeError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for PacketDecodeError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<UnexpectedEnd> for PacketDecodeError

Source§

fn from(_: UnexpectedEnd) -> Self

Converts to this type from the input type.
Source§

impl Hash for PacketDecodeError

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for PacketDecodeError

Source§

fn cmp(&self, other: &PacketDecodeError) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for PacketDecodeError

Source§

fn eq(&self, other: &PacketDecodeError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for PacketDecodeError

Source§

fn partial_cmp(&self, other: &PacketDecodeError) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Eq for PacketDecodeError

Source§

impl StructuralPartialEq for PacketDecodeError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.PathError.html b/pr/395/docs/iroh_quinn_proto/enum.PathError.html new file mode 100644 index 000000000..2caab3a91 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.PathError.html @@ -0,0 +1,46 @@ +PathError in iroh_quinn_proto - Rust

PathError

Enum PathError 

Source
pub enum PathError {
+    MultipathNotNegotiated,
+    ServerSideNotAllowed,
+    MaxPathIdReached,
+    RemoteCidsExhausted,
+    ValidationFailed,
+    InvalidRemoteAddress(SocketAddr),
+}
Expand description

Errors that might trigger a path being closed

+

Variants§

§

MultipathNotNegotiated

The extension was not negotiated with the peer

+
§

ServerSideNotAllowed

Paths can only be opened client-side

+
§

MaxPathIdReached

Current limits do not allow us to open more paths

+
§

RemoteCidsExhausted

No remote CIDs available to open a new path

+
§

ValidationFailed

Path could not be validated and will be abandoned

+
§

InvalidRemoteAddress(SocketAddr)

The remote address for the path is not supported by the endpoint

+

Trait Implementations§

Source§

impl Clone for PathError

Source§

fn clone(&self) -> PathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for PathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for PathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for PathError

Source§

fn eq(&self, other: &PathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathError

Source§

impl Eq for PathError

Source§

impl StructuralPartialEq for PathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.PathEvent.html b/pr/395/docs/iroh_quinn_proto/enum.PathEvent.html new file mode 100644 index 000000000..08bb20e4c --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.PathEvent.html @@ -0,0 +1,80 @@ +PathEvent in iroh_quinn_proto - Rust

PathEvent

Enum PathEvent 

Source
pub enum PathEvent {
+    Opened {
+        id: PathId,
+    },
+    Closed {
+        id: PathId,
+        error_code: VarInt,
+    },
+    Abandoned {
+        id: PathId,
+        path_stats: PathStats,
+    },
+    LocallyClosed {
+        id: PathId,
+        error: PathError,
+    },
+    RemoteStatus {
+        id: PathId,
+        status: PathStatus,
+    },
+    ObservedAddr {
+        id: PathId,
+        addr: SocketAddr,
+    },
+}
Expand description

Application events about paths

+

Variants§

§

Opened

A new path has been opened

+

Fields

§id: PathId

Which path is now open

+
§

Closed

A path has been closed

+

Fields

§id: PathId

Which path has been closed

+
§error_code: VarInt

Error code supplied by the peer +See https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-error-codes +for a list of known errors.

+
§

Abandoned

All remaining state for a path has been removed

+

The PathEvent::Closed would have been emitted for this path earlier.

+

Fields

§id: PathId

Which path had its state dropped

+
§path_stats: PathStats

The final path stats, they are no longer available via Connection::stats

+
§

LocallyClosed

Path was closed locally

+

Fields

§id: PathId

Path for which the error occurred

+
§error: PathError

The error that occurred

+
§

RemoteStatus

The remote changed the status of the path

+

The local status is not changed because of this event. It is up to the application +to update the local status, which is used for packet scheduling, when the remote +changes the status.

+

Fields

§id: PathId

Path which has changed status

+
§status: PathStatus

The new status set by the remote

+
§

ObservedAddr

Received an observation of our external address from the peer.

+

Fields

§id: PathId

Path over which the observed address was reported, PathId::ZERO when multipath is +not negotiated

+
§addr: SocketAddr

The address observed by the remote over this path

+

Trait Implementations§

Source§

impl Clone for PathEvent

Source§

fn clone(&self) -> PathEvent

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<PathEvent> for Event

Source§

fn from(source: PathEvent) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for PathEvent

Source§

fn eq(&self, other: &PathEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for PathEvent

Source§

impl StructuralPartialEq for PathEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.PathStatus.html b/pr/395/docs/iroh_quinn_proto/enum.PathStatus.html new file mode 100644 index 000000000..ed19d9e6e --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.PathStatus.html @@ -0,0 +1,43 @@ +PathStatus in iroh_quinn_proto - Rust

PathStatus

Enum PathStatus 

Source
pub enum PathStatus {
+    Available,
+    Backup,
+}
Expand description

The QUIC-MULTIPATH path status

+

See section “3.3 Path Status Management”: +https://quicwg.org/multipath/draft-ietf-quic-multipath.html#name-path-status-management

+

Variants§

§

Available

Paths marked with as available will be used when scheduling packets

+

If multiple paths are available, packets will be scheduled on whichever has +capacity.

+
§

Backup

Paths marked as backup will only be used if there are no available paths

+

If the max_idle_timeout is specified the path will be kept alive so that it does not +expire.

+

Trait Implementations§

Source§

impl Clone for PathStatus

Source§

fn clone(&self) -> PathStatus

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStatus

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for PathStatus

Source§

fn default() -> PathStatus

Returns the “default value” for a type. Read more
Source§

impl PartialEq for PathStatus

Source§

fn eq(&self, other: &PathStatus) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathStatus

Source§

impl Eq for PathStatus

Source§

impl StructuralPartialEq for PathStatus

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.ProtectedHeader.html b/pr/395/docs/iroh_quinn_proto/enum.ProtectedHeader.html new file mode 100644 index 000000000..9b37c5a4b --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.ProtectedHeader.html @@ -0,0 +1,75 @@ +ProtectedHeader in iroh_quinn_proto - Rust

ProtectedHeader

Enum ProtectedHeader 

Source
pub enum ProtectedHeader {
+    Initial(ProtectedInitialHeader),
+    Long {
+        ty: LongType,
+        dst_cid: ConnectionId,
+        src_cid: ConnectionId,
+        len: u64,
+        version: u32,
+    },
+    Retry {
+        dst_cid: ConnectionId,
+        src_cid: ConnectionId,
+        version: u32,
+    },
+    Short {
+        spin: bool,
+        dst_cid: ConnectionId,
+    },
+    VersionNegotiate {
+        random: u8,
+        dst_cid: ConnectionId,
+        src_cid: ConnectionId,
+    },
+}
Expand description

Plain packet header

+

Variants§

§

Initial(ProtectedInitialHeader)

An Initial packet header

+
§

Long

A Long packet header, as used during the handshake

+

Fields

§ty: LongType

Type of the Long header packet

+
§dst_cid: ConnectionId

Destination Connection ID

+
§src_cid: ConnectionId

Source Connection ID

+
§len: u64

Length of the packet payload

+
§version: u32

QUIC version

+
§

Retry

A Retry packet header

+

Fields

§dst_cid: ConnectionId

Destination Connection ID

+
§src_cid: ConnectionId

Source Connection ID

+
§version: u32

QUIC version

+
§

Short

A short packet header, as used during the data phase

+

Fields

§spin: bool

Spin bit

+
§dst_cid: ConnectionId

Destination Connection ID

+
§

VersionNegotiate

A Version Negotiation packet header

+

Fields

§random: u8

Random value

+
§dst_cid: ConnectionId

Destination Connection ID

+
§src_cid: ConnectionId

Source Connection ID

+

Implementations§

Source§

impl ProtectedHeader

Source

pub fn dst_cid(&self) -> ConnectionId

The destination Connection ID of the packet

+
Source

pub fn decode( + buf: &mut Cursor<BytesMut>, + cid_parser: &(impl ConnectionIdParser + ?Sized), + supported_versions: &[u32], + grease_quic_bit: bool, +) -> Result<Self, PacketDecodeError>

Decode a plain header from given buffer, with given ConnectionIdParser.

+

Trait Implementations§

Source§

impl Clone for ProtectedHeader

Source§

fn clone(&self) -> ProtectedHeader

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ProtectedHeader

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.ReadError.html b/pr/395/docs/iroh_quinn_proto/enum.ReadError.html new file mode 100644 index 000000000..db9c08531 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.ReadError.html @@ -0,0 +1,51 @@ +ReadError in iroh_quinn_proto - Rust

ReadError

Enum ReadError 

Source
pub enum ReadError {
+    Blocked,
+    Reset(VarInt),
+}
Expand description

Errors triggered when reading from a recv stream

+

Variants§

§

Blocked

No more data is currently available on this stream.

+

If more data on this stream is received from the peer, an Event::StreamReadable will be +generated for this stream, indicating that retrying the read might succeed.

+
§

Reset(VarInt)

The peer abandoned transmitting data on this stream.

+

Carries an application-defined error code.

+

Trait Implementations§

Source§

impl Clone for ReadError

Source§

fn clone(&self) -> ReadError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ReadError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ReadError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ReadError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Hash for ReadError

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for ReadError

Source§

fn cmp(&self, other: &ReadError) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for ReadError

Source§

fn eq(&self, other: &ReadError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for ReadError

Source§

fn partial_cmp(&self, other: &ReadError) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Eq for ReadError

Source§

impl StructuralPartialEq for ReadError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.ReadableError.html b/pr/395/docs/iroh_quinn_proto/enum.ReadableError.html new file mode 100644 index 000000000..4244330af --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.ReadableError.html @@ -0,0 +1,50 @@ +ReadableError in iroh_quinn_proto - Rust

ReadableError

Enum ReadableError 

Source
pub enum ReadableError {
+    ClosedStream,
+    IllegalOrderedRead,
+}
Expand description

Errors triggered when opening a recv stream for reading

+

Variants§

§

ClosedStream

The stream has not been opened or was already stopped, finished, or reset

+
§

IllegalOrderedRead

Attempted an ordered read following an unordered read

+

Performing an unordered read allows discontinuities to arise in the receive buffer of a +stream which cannot be recovered, making further ordered reads impossible.

+

Trait Implementations§

Source§

impl Clone for ReadableError

Source§

fn clone(&self) -> ReadableError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ReadableError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ReadableError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ReadableError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Hash for ReadableError

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for ReadableError

Source§

fn cmp(&self, other: &ReadableError) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for ReadableError

Source§

fn eq(&self, other: &ReadableError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for ReadableError

Source§

fn partial_cmp(&self, other: &ReadableError) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Eq for ReadableError

Source§

impl StructuralPartialEq for ReadableError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.SendDatagramError.html b/pr/395/docs/iroh_quinn_proto/enum.SendDatagramError.html new file mode 100644 index 000000000..2d22aefbb --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.SendDatagramError.html @@ -0,0 +1,54 @@ +SendDatagramError in iroh_quinn_proto - Rust

SendDatagramError

Enum SendDatagramError 

Source
pub enum SendDatagramError {
+    UnsupportedByPeer,
+    Disabled,
+    TooLarge,
+    Blocked(Bytes),
+}
Expand description

Errors that can arise when sending a datagram

+

Variants§

§

UnsupportedByPeer

The peer does not support receiving datagram frames

+
§

Disabled

Datagram support is disabled locally

+
§

TooLarge

The datagram is larger than the connection can currently accommodate

+

Indicates that the path MTU minus overhead or the limit advertised by the peer has been +exceeded.

+
§

Blocked(Bytes)

Send would block

+

Trait Implementations§

Source§

impl Clone for SendDatagramError

Source§

fn clone(&self) -> SendDatagramError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SendDatagramError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for SendDatagramError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for SendDatagramError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Hash for SendDatagramError

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for SendDatagramError

Source§

fn cmp(&self, other: &SendDatagramError) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for SendDatagramError

Source§

fn eq(&self, other: &SendDatagramError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for SendDatagramError

Source§

fn partial_cmp(&self, other: &SendDatagramError) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Eq for SendDatagramError

Source§

impl StructuralPartialEq for SendDatagramError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.SetPathStatusError.html b/pr/395/docs/iroh_quinn_proto/enum.SetPathStatusError.html new file mode 100644 index 000000000..c1546d760 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.SetPathStatusError.html @@ -0,0 +1,38 @@ +SetPathStatusError in iroh_quinn_proto - Rust

SetPathStatusError

Enum SetPathStatusError 

Source
pub enum SetPathStatusError {
+    ClosedPath,
+    MultipathNotNegotiated,
+}
Expand description

Error from setting path status

+

Variants§

§

ClosedPath

Error indicating that a path has not been opened or has already been abandoned

+
§

MultipathNotNegotiated

Error indicating that this operation requires multipath to be negotiated whereas it hasn’t been

+

Trait Implementations§

Source§

impl Clone for SetPathStatusError

Source§

fn clone(&self) -> SetPathStatusError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SetPathStatusError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for SetPathStatusError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for SetPathStatusError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for SetPathStatusError

Source§

fn eq(&self, other: &SetPathStatusError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for SetPathStatusError

Source§

impl StructuralPartialEq for SetPathStatusError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.Side.html b/pr/395/docs/iroh_quinn_proto/enum.Side.html new file mode 100644 index 000000000..741741acb --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.Side.html @@ -0,0 +1,54 @@ +Side in iroh_quinn_proto - Rust

Side

Enum Side 

Source
pub enum Side {
+    Client = 0,
+    Server = 1,
+}
Expand description

Whether an endpoint was the initiator of a connection

+

Variants§

§

Client = 0

The initiator of a connection

+
§

Server = 1

The acceptor of a connection

+

Implementations§

Source§

impl Side

Source

pub fn is_client(self) -> bool

Shorthand for self == Side::Client

+
Source

pub fn is_server(self) -> bool

Shorthand for self == Side::Server

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for Side

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the given unstructured data. Read more
Source§

fn arbitrary_take_rest(u: Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
Source§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for Side

Source§

fn clone(&self) -> Side

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Side

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<Side> for Side

Source§

fn from(s: Side) -> Self

Converts to this type from the input type.
Source§

impl Hash for Side

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Not for Side

Source§

type Output = Side

The resulting type after applying the ! operator.
Source§

fn not(self) -> Self

Performs the unary ! operation. Read more
Source§

impl Ord for Side

Source§

fn cmp(&self, other: &Side) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for Side

Source§

fn eq(&self, other: &Side) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for Side

Source§

fn partial_cmp(&self, other: &Side) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for Side

Source§

impl Eq for Side

Source§

impl StructuralPartialEq for Side

Auto Trait Implementations§

§

impl Freeze for Side

§

impl RefUnwindSafe for Side

§

impl Send for Side

§

impl Sync for Side

§

impl Unpin for Side

§

impl UnwindSafe for Side

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.StreamEvent.html b/pr/395/docs/iroh_quinn_proto/enum.StreamEvent.html new file mode 100644 index 000000000..b5f82286a --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.StreamEvent.html @@ -0,0 +1,64 @@ +StreamEvent in iroh_quinn_proto - Rust

StreamEvent

Enum StreamEvent 

Source
pub enum StreamEvent {
+    Opened {
+        dir: Dir,
+    },
+    Readable {
+        id: StreamId,
+    },
+    Writable {
+        id: StreamId,
+    },
+    Finished {
+        id: StreamId,
+    },
+    Stopped {
+        id: StreamId,
+        error_code: VarInt,
+    },
+    Available {
+        dir: Dir,
+    },
+}
Expand description

Application events about streams

+

Variants§

§

Opened

One or more new streams has been opened and might be readable

+

Fields

§dir: Dir

Directionality for which streams have been opened

+
§

Readable

A currently open stream likely has data or errors waiting to be read

+

Fields

§id: StreamId

Which stream is now readable

+
§

Writable

A formerly write-blocked stream might be ready for a write or have been stopped

+

Only generated for streams that are currently open.

+

Fields

§id: StreamId

Which stream is now writable

+
§

Finished

A finished stream has been fully acknowledged or stopped

+

Fields

§id: StreamId

Which stream has been finished

+
§

Stopped

The peer asked us to stop sending on an outgoing stream

+

Fields

§id: StreamId

Which stream has been stopped

+
§error_code: VarInt

Error code supplied by the peer

+
§

Available

At least one new stream of a certain directionality may be opened

+

Fields

§dir: Dir

Directionality for which streams are newly available

+

Trait Implementations§

Source§

impl Debug for StreamEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for StreamEvent

Source§

fn eq(&self, other: &StreamEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for StreamEvent

Source§

impl StructuralPartialEq for StreamEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/enum.WriteError.html b/pr/395/docs/iroh_quinn_proto/enum.WriteError.html new file mode 100644 index 000000000..d3990d399 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/enum.WriteError.html @@ -0,0 +1,54 @@ +WriteError in iroh_quinn_proto - Rust

WriteError

Enum WriteError 

Source
pub enum WriteError {
+    Blocked,
+    Stopped(VarInt),
+    ClosedStream,
+}
Expand description

Errors triggered while writing to a send stream

+

Variants§

§

Blocked

The peer is not able to accept additional data, or the connection is congested.

+

If the peer issues additional flow control credit, a StreamEvent::Writable event will +be generated, indicating that retrying the write might succeed.

+
§

Stopped(VarInt)

The peer is no longer accepting data on this stream, and it has been implicitly reset. The +stream cannot be finished or further written to.

+

Carries an application-defined error code.

+
§

ClosedStream

The stream has not been opened or has already been finished or reset

+

Trait Implementations§

Source§

impl Clone for WriteError

Source§

fn clone(&self) -> WriteError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for WriteError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for WriteError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for WriteError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Hash for WriteError

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for WriteError

Source§

fn cmp(&self, other: &WriteError) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for WriteError

Source§

fn eq(&self, other: &WriteError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for WriteError

Source§

fn partial_cmp(&self, other: &WriteError) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Eq for WriteError

Source§

impl StructuralPartialEq for WriteError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/frame/enum.FrameType.html b/pr/395/docs/iroh_quinn_proto/frame/enum.FrameType.html new file mode 100644 index 000000000..26072c98b --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/frame/enum.FrameType.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.FrameType.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/frame/enum.MaybeFrame.html b/pr/395/docs/iroh_quinn_proto/frame/enum.MaybeFrame.html new file mode 100644 index 000000000..7b537bd88 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/frame/enum.MaybeFrame.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.MaybeFrame.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/frame/struct.ApplicationClose.html b/pr/395/docs/iroh_quinn_proto/frame/struct.ApplicationClose.html new file mode 100644 index 000000000..6785c96b6 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/frame/struct.ApplicationClose.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ApplicationClose.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/frame/struct.ConnectionClose.html b/pr/395/docs/iroh_quinn_proto/frame/struct.ConnectionClose.html new file mode 100644 index 000000000..9839746e5 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/frame/struct.ConnectionClose.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ConnectionClose.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/frame/struct.Datagram.html b/pr/395/docs/iroh_quinn_proto/frame/struct.Datagram.html new file mode 100644 index 000000000..96b3bbb73 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/frame/struct.Datagram.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.Datagram.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/frame/struct.DatagramInfo.html b/pr/395/docs/iroh_quinn_proto/frame/struct.DatagramInfo.html new file mode 100644 index 000000000..afde3edc2 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/frame/struct.DatagramInfo.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.DatagramInfo.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/frame/struct.InvalidFrameId.html b/pr/395/docs/iroh_quinn_proto/frame/struct.InvalidFrameId.html new file mode 100644 index 000000000..7f96a888b --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/frame/struct.InvalidFrameId.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.InvalidFrameId.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/frame/struct.StreamInfo.html b/pr/395/docs/iroh_quinn_proto/frame/struct.StreamInfo.html new file mode 100644 index 000000000..ec4678daa --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/frame/struct.StreamInfo.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.StreamInfo.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/index.html b/pr/395/docs/iroh_quinn_proto/index.html new file mode 100644 index 000000000..5afc7fb23 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/index.html @@ -0,0 +1,14 @@ +iroh_quinn_proto - Rust

Crate iroh_quinn_proto

Crate iroh_quinn_proto 

Source
Expand description

Low-level protocol logic for the QUIC protoocol

+

quinn-proto contains a fully deterministic implementation of QUIC protocol logic. It contains +no networking code and does not get any relevant timestamps from the operating system. Most +users may want to use the futures-based quinn API instead.

+

The quinn-proto API might be of interest if you want to use it from a C or C++ project +through C bindings or if you want to use a different event loop than the one tokio provides.

+

The most important types are Endpoint, which conceptually represents the protocol state for +a single socket and mostly manages configuration and dispatches incoming datagrams to the +related Connection. Connection types contain the bulk of the protocol logic related to +managing a single connection and all the related state (such as streams).

+

Re-exports§

pub use rustls;

Modules§

bench_exports
Exports for benchmarks
coding
Coding related traits.
congestion
Logic for controlling the rate at which data is sent
crypto
Traits and implementations for the QUIC cryptography protocol
iroh_hp
iroh NAT Traversal
transport_parameters
QUIC connection transport parameters

Structs§

AcceptError
Error type for attempting to accept an Incoming
AckFrequencyConfig
Parameters for controlling the peer’s acknowledgement frequency
ApplicationClose
Reason given by an application for closing the connection
BloomTokenLog
Bloom filter-based TokenLog
Chunk
A chunk of data from the receive stream
Chunks
Chunks returned from RecvStream::read().
ClientConfig
Configuration for outgoing connections
ClosedPath
Error indicating that a path has not been opened or has already been abandoned
ClosedStream
Error indicating that a stream has not been opened or has already been finished or reset
Connection
Protocol state and logic for a single QUIC connection
ConnectionClose
Reason given by the transport for closing the connection
ConnectionEvent
Events sent from an Endpoint to a Connection
ConnectionHandle
Internal identifier for a Connection currently associated with an endpoint
ConnectionId
Protocol-level identifier for a connection.
ConnectionStats
Connection statistics
Datagram
An unreliable datagram
DatagramInfo
Datagrams
API to control datagram traffic
Endpoint
The main entry point to the library
EndpointConfig
Global configuration for the endpoint, affecting all connections
EndpointEvent
Events sent from a Connection to an Endpoint
FixedLengthConnectionIdParser
A ConnectionIdParser implementation that assumes the connection ID is of fixed length
FourTuple
Identifies a network path by the combination of remote and local addresses
FrameStats
Number of frames transmitted or received of each frame type
HashedConnectionIdGenerator
Generates 8-byte connection IDs that can be efficiently +validated
IdleTimeout
Maximum duration of inactivity to accept before timing out the connection
Incoming
An incoming connection for which the server has not yet begun its part of the handshake.
InvalidCid
The connection ID was not recognized by the ConnectionIdGenerator
InvalidFrameId
Encounter a frame ID that was not valid.
MtuDiscoveryConfig
Parameters governing MTU discovery.
MultipathNotNegotiated
Error when the multipath extension was not negotiated, but attempted to be used.
NoneTokenLog
Null implementation of TokenLog, which never accepts tokens
NoneTokenStore
Null implementation of TokenStore, which does not store any tokens
PartialDecode
Decodes a QUIC packet’s invariant header
PathId
Id representing different paths when using multipath extension
PathStats
Statistics related to a transmission path
ProtectedInitialHeader
Header of an Initial packet, before decryption
QlogConfig
Configuration for qlog trace logging.
QlogFileFactory
Enables writing qlog traces to a directory.
RandomConnectionIdGenerator
Generates purely random connection IDs of a specified length
RecvStream
Access to streams
RetryError
Error for attempting to retry an Incoming which already bears a token from a previous retry
RttEstimator
RTT estimation for a particular network path
SendStream
Access to streams
ServerConfig
Parameters governing incoming connections
ShouldTransmit
Indicates whether a frame needs to be transmitted
StdSystemTime
Default implementation of TimeSource
StreamId
Identifier for a stream within a particular connection
StreamInfo
Streams
Access to streams
TokenMemoryCache
TokenStore implementation that stores up to N tokens per server name for up to a +limited number of server names, in-memory
TokenReuseError
Error for when a validation token may have been reused
Transmit
An outgoing packet
TransportConfig
Parameters governing the core QUIC state machine
TransportError
Transport-level errors occur when a peer violates the protocol specification
TransportErrorCode
Transport-level error code
UdpStats
Statistics about UDP datagrams transmitted or received on a connection
ValidationTokenConfig
Configuration for sending and handling validation tokens in incoming connections
VarInt
An integer less than 2^62
VarIntBoundsExceeded
Error returned when constructing a VarInt from a value >= 2^62
Written
Indicates how many bytes and chunks had been transferred in a write operation

Enums§

ClosePathError
Errors triggered when abandoning a path
ConfigError
Errors in the configuration of an endpoint
ConnectError
Errors in the parameters being used to create a new connection
ConnectionError
Reasons why a connection might be lost
DatagramEvent
Event resulting from processing a single datagram
Dir
Whether a stream communicates data in both directions or only from the initiator
EcnCodepoint
Explicit congestion notification codepoint
Event
Events of interest to the application
FinishError
Reasons why attempting to finish a stream might fail
FrameType
LongType
Long packet types with uniform header structure
MaybeFrame
The type used to refer to FrameTypes in closing and transport errors.
PacketDecodeError
Packet decode error
PathError
Errors that might trigger a path being closed
PathEvent
Application events about paths
PathStatus
The QUIC-MULTIPATH path status
ProtectedHeader
Plain packet header
ReadError
Errors triggered when reading from a recv stream
ReadableError
Errors triggered when opening a recv stream for reading
SendDatagramError
Errors that can arise when sending a datagram
SetPathStatusError
Error from setting path status
Side
Whether an endpoint was the initiator of a connection
StreamEvent
Application events about streams
WriteError
Errors triggered while writing to a send stream

Constants§

DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED
When multipath is required and has not been explicitly enabled, this value will be used for
DEFAULT_SUPPORTED_VERSIONS
The QUIC protocol version implemented.

Traits§

ConnectionIdGenerator
Generates connection IDs for incoming connections
ConnectionIdParser
Parse connection id in short header packet
QlogFactory
Constructs a QlogConfig for individual connections.
TimeSource
Object to get current SystemTime
TokenLog
Responsible for limiting clients’ ability to reuse validation tokens
TokenStore
Responsible for storing validation tokens received from servers and retrieving them for use in +subsequent connections
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/iroh_hp/enum.Error.html b/pr/395/docs/iroh_quinn_proto/iroh_hp/enum.Error.html new file mode 100644 index 000000000..3998de21a --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/iroh_hp/enum.Error.html @@ -0,0 +1,39 @@ +Error in iroh_quinn_proto::iroh_hp - Rust

Error

Enum Error 

Source
pub enum Error {
+    TooManyAddresses,
+    WrongConnectionSide,
+    ExtensionNotNegotiated,
+    NotEnoughAddresses,
+    Multipath(PathError),
+    Closed,
+}
Expand description

Errors that the nat traversal state might encounter.

+

Variants§

§

TooManyAddresses

An endpoint (local or remote) tried to add too many addresses to their advertised set

+
§

WrongConnectionSide

The operation is not allowed for this endpoint’s connection side

+
§

ExtensionNotNegotiated

The extension was not negotiated

+
§

NotEnoughAddresses

Not enough addresses to complete the operation

+
§

Multipath(PathError)

Nat traversal attempt failed due to a multipath error

+
§

Closed

Attempted to initiate NAT traversal on a closed, or closing connection.

+

Trait Implementations§

Source§

impl Debug for Error

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Error

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for Error

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more

Auto Trait Implementations§

§

impl Freeze for Error

§

impl RefUnwindSafe for Error

§

impl Send for Error

§

impl Sync for Error

§

impl Unpin for Error

§

impl UnwindSafe for Error

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/iroh_hp/enum.Event.html b/pr/395/docs/iroh_quinn_proto/iroh_hp/enum.Event.html new file mode 100644 index 000000000..ff5901c89 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/iroh_hp/enum.Event.html @@ -0,0 +1,32 @@ +Event in iroh_quinn_proto::iroh_hp - Rust

Event

Enum Event 

Source
pub enum Event {
+    AddressAdded(SocketAddr),
+    AddressRemoved(SocketAddr),
+}
Expand description

Event emitted when the client receives ADD_ADDRESS or REMOVE_ADDRESS frames.

+

Variants§

§

AddressAdded(SocketAddr)

An ADD_ADDRESS frame was received.

+
§

AddressRemoved(SocketAddr)

A REMOVE_ADDRESS frame was received.

+

Trait Implementations§

Source§

impl Clone for Event

Source§

fn clone(&self) -> Event

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Event

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Event

§

impl RefUnwindSafe for Event

§

impl Send for Event

§

impl Sync for Event

§

impl Unpin for Event

§

impl UnwindSafe for Event

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/iroh_hp/index.html b/pr/395/docs/iroh_quinn_proto/iroh_hp/index.html new file mode 100644 index 000000000..0b68f7127 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/iroh_hp/index.html @@ -0,0 +1,2 @@ +iroh_quinn_proto::iroh_hp - Rust

Module iroh_hp

Module iroh_hp 

Source
Expand description

iroh NAT Traversal

+

Enums§

Error
Errors that the nat traversal state might encounter.
Event
Event emitted when the client receives ADD_ADDRESS or REMOVE_ADDRESS frames.
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/iroh_hp/sidebar-items.js b/pr/395/docs/iroh_quinn_proto/iroh_hp/sidebar-items.js new file mode 100644 index 000000000..e03b07ea1 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/iroh_hp/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Error","Event"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/packet/enum.LongType.html b/pr/395/docs/iroh_quinn_proto/packet/enum.LongType.html new file mode 100644 index 000000000..dcf81f61f --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/packet/enum.LongType.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.LongType.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/packet/enum.PacketDecodeError.html b/pr/395/docs/iroh_quinn_proto/packet/enum.PacketDecodeError.html new file mode 100644 index 000000000..494edb286 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/packet/enum.PacketDecodeError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.PacketDecodeError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/packet/enum.ProtectedHeader.html b/pr/395/docs/iroh_quinn_proto/packet/enum.ProtectedHeader.html new file mode 100644 index 000000000..4e6ca3502 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/packet/enum.ProtectedHeader.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.ProtectedHeader.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/packet/struct.FixedLengthConnectionIdParser.html b/pr/395/docs/iroh_quinn_proto/packet/struct.FixedLengthConnectionIdParser.html new file mode 100644 index 000000000..63a343905 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/packet/struct.FixedLengthConnectionIdParser.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/packet/struct.PartialDecode.html b/pr/395/docs/iroh_quinn_proto/packet/struct.PartialDecode.html new file mode 100644 index 000000000..8ea383e01 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/packet/struct.PartialDecode.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.PartialDecode.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/packet/struct.ProtectedInitialHeader.html b/pr/395/docs/iroh_quinn_proto/packet/struct.ProtectedInitialHeader.html new file mode 100644 index 000000000..e2c23e247 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/packet/struct.ProtectedInitialHeader.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ProtectedInitialHeader.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/packet/trait.ConnectionIdParser.html b/pr/395/docs/iroh_quinn_proto/packet/trait.ConnectionIdParser.html new file mode 100644 index 000000000..2d526fcd6 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/packet/trait.ConnectionIdParser.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/trait.ConnectionIdParser.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/shared/enum.EcnCodepoint.html b/pr/395/docs/iroh_quinn_proto/shared/enum.EcnCodepoint.html new file mode 100644 index 000000000..31fd913dc --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/shared/enum.EcnCodepoint.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.EcnCodepoint.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/shared/struct.ConnectionEvent.html b/pr/395/docs/iroh_quinn_proto/shared/struct.ConnectionEvent.html new file mode 100644 index 000000000..18bfd544f --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/shared/struct.ConnectionEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ConnectionEvent.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/shared/struct.ConnectionId.html b/pr/395/docs/iroh_quinn_proto/shared/struct.ConnectionId.html new file mode 100644 index 000000000..67aa4df61 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/shared/struct.ConnectionId.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ConnectionId.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/shared/struct.EndpointEvent.html b/pr/395/docs/iroh_quinn_proto/shared/struct.EndpointEvent.html new file mode 100644 index 000000000..4b4f3e586 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/shared/struct.EndpointEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.EndpointEvent.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/sidebar-items.js b/pr/395/docs/iroh_quinn_proto/sidebar-items.js new file mode 100644 index 000000000..146401cef --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"constant":["DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED","DEFAULT_SUPPORTED_VERSIONS"],"enum":["ClosePathError","ConfigError","ConnectError","ConnectionError","DatagramEvent","Dir","EcnCodepoint","Event","FinishError","FrameType","LongType","MaybeFrame","PacketDecodeError","PathError","PathEvent","PathStatus","ProtectedHeader","ReadError","ReadableError","SendDatagramError","SetPathStatusError","Side","StreamEvent","WriteError"],"mod":["bench_exports","coding","congestion","crypto","iroh_hp","transport_parameters"],"struct":["AcceptError","AckFrequencyConfig","ApplicationClose","BloomTokenLog","Chunk","Chunks","ClientConfig","ClosedPath","ClosedStream","Connection","ConnectionClose","ConnectionEvent","ConnectionHandle","ConnectionId","ConnectionStats","Datagram","DatagramInfo","Datagrams","Endpoint","EndpointConfig","EndpointEvent","FixedLengthConnectionIdParser","FourTuple","FrameStats","HashedConnectionIdGenerator","IdleTimeout","Incoming","InvalidCid","InvalidFrameId","MtuDiscoveryConfig","MultipathNotNegotiated","NoneTokenLog","NoneTokenStore","PartialDecode","PathId","PathStats","ProtectedInitialHeader","QlogConfig","QlogFileFactory","RandomConnectionIdGenerator","RecvStream","RetryError","RttEstimator","SendStream","ServerConfig","ShouldTransmit","StdSystemTime","StreamId","StreamInfo","Streams","TokenMemoryCache","TokenReuseError","Transmit","TransportConfig","TransportError","TransportErrorCode","UdpStats","ValidationTokenConfig","VarInt","VarIntBoundsExceeded","Written"],"trait":["ConnectionIdGenerator","ConnectionIdParser","QlogFactory","TimeSource","TokenLog","TokenStore"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.AcceptError.html b/pr/395/docs/iroh_quinn_proto/struct.AcceptError.html new file mode 100644 index 000000000..a8f720cd0 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.AcceptError.html @@ -0,0 +1,30 @@ +AcceptError in iroh_quinn_proto - Rust

AcceptError

Struct AcceptError 

Source
pub struct AcceptError {
+    pub cause: ConnectionError,
+    pub response: Option<Transmit>,
+}
Expand description

Error type for attempting to accept an Incoming

+

Fields§

§cause: ConnectionError

Underlying error describing reason for failure

+
§response: Option<Transmit>

Optional response to transmit back

+

Trait Implementations§

Source§

impl Debug for AcceptError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.AckFrequencyConfig.html b/pr/395/docs/iroh_quinn_proto/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..cdfb7e6b5 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.AckFrequencyConfig.html @@ -0,0 +1,59 @@ +AckFrequencyConfig in iroh_quinn_proto - Rust

AckFrequencyConfig

Struct AckFrequencyConfig 

Source
pub struct AckFrequencyConfig { /* private fields */ }
Expand description

Parameters for controlling the peer’s acknowledgement frequency

+

The parameters provided in this config will be sent to the peer at the beginning of the +connection, so it can take them into account when sending acknowledgements (see each parameter’s +description for details on how it influences acknowledgement frequency).

+

Quinn’s implementation follows the fourth draft of the +QUIC Acknowledgement Frequency extension. +The defaults produce behavior slightly different than the behavior without this extension, +because they change the way reordered packets are handled (see +AckFrequencyConfig::reordering_threshold for details).

+

Implementations§

Source§

impl AckFrequencyConfig

Source

pub fn ack_eliciting_threshold(&mut self, value: VarInt) -> &mut Self

The ack-eliciting threshold we will request the peer to use

+

This threshold represents the number of ack-eliciting packets an endpoint may receive +without immediately sending an ACK.

+

The remote peer should send at least one ACK frame when more than this number of +ack-eliciting packets have been received. A value of 0 results in a receiver immediately +acknowledging every ack-eliciting packet.

+

Defaults to 1, which sends ACK frames for every other ack-eliciting packet.

+
Source

pub fn max_ack_delay(&mut self, value: Option<Duration>) -> &mut Self

The max_ack_delay we will request the peer to use

+

This parameter represents the maximum amount of time that an endpoint waits before sending +an ACK when the ack-eliciting threshold hasn’t been reached.

+

The effective max_ack_delay will be clamped to be at least the peer’s min_ack_delay +transport parameter, and at most the greater of the current path RTT or 25ms.

+

Defaults to None, in which case the peer’s original max_ack_delay will be used, as +obtained from its transport parameters.

+
Source

pub fn reordering_threshold(&mut self, value: VarInt) -> &mut Self

The reordering threshold we will request the peer to use

+

This threshold represents the amount of out-of-order packets that will trigger an endpoint +to send an ACK, without waiting for ack_eliciting_threshold to be exceeded or for +max_ack_delay to be elapsed.

+

A value of 0 indicates out-of-order packets do not elicit an immediate ACK. A value of 1 +immediately acknowledges any packets that are received out of order (this is also the +behavior when the extension is disabled).

+

It is recommended to set this value to TransportConfig::packet_threshold minus one. +Since the default value for TransportConfig::packet_threshold is 3, this value defaults +to 2.

+

Trait Implementations§

Source§

impl Clone for AckFrequencyConfig

Source§

fn clone(&self) -> AckFrequencyConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for AckFrequencyConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for AckFrequencyConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.ApplicationClose.html b/pr/395/docs/iroh_quinn_proto/struct.ApplicationClose.html new file mode 100644 index 000000000..f7d992b47 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.ApplicationClose.html @@ -0,0 +1,38 @@ +ApplicationClose in iroh_quinn_proto - Rust

ApplicationClose

Struct ApplicationClose 

Source
pub struct ApplicationClose {
+    pub error_code: VarInt,
+    pub reason: Bytes,
+}
Expand description

Reason given by an application for closing the connection

+

Fields§

§error_code: VarInt

Application-specific reason code

+
§reason: Bytes

Human-readable reason for the close

+

Trait Implementations§

Source§

impl Clone for ApplicationClose

Source§

fn clone(&self) -> ApplicationClose

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ApplicationClose

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ApplicationClose

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for ApplicationClose

Source§

fn eq(&self, other: &ApplicationClose) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ApplicationClose

Source§

impl StructuralPartialEq for ApplicationClose

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.BloomTokenLog.html b/pr/395/docs/iroh_quinn_proto/struct.BloomTokenLog.html new file mode 100644 index 000000000..22f3d5f1a --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.BloomTokenLog.html @@ -0,0 +1,48 @@ +BloomTokenLog in iroh_quinn_proto - Rust

BloomTokenLog

Struct BloomTokenLog 

Source
pub struct BloomTokenLog(/* private fields */);
Expand description

Bloom filter-based TokenLog

+

Parameterizable over an approximate maximum number of bytes to allocate. Starts out by storing +used tokens in a hash set. Once the hash set becomes too large, converts it to a bloom filter. +This achieves a memory profile of linear growth with an upper bound.

+

Divides time into periods based on lifetime and stores two filters at any given moment, for +each of the two periods currently non-expired tokens could expire in. As such, turns over +filters as time goes on to avoid bloom filter false positive rate increasing infinitely over +time.

+

Implementations§

Source§

impl BloomTokenLog

Source

pub fn new_expected_items(max_bytes: usize, expected_hits: u64) -> Self

Construct with an approximate maximum memory usage and expected number of validation token +usages per expiration period

+

Calculates the optimal bloom filter k number automatically.

+
Source

pub fn new(max_bytes: usize, k_num: u32) -> Self

Construct with an approximate maximum memory usage and a bloom filter k number

+

If choosing a custom k number, note that BloomTokenLog always maintains two filters +between them and divides the allocation budget of max_bytes evenly between them. As such, +each bloom filter will contain max_bytes * 4 bits.

+

Trait Implementations§

Source§

impl Default for BloomTokenLog

Default to 20 MiB max memory consumption and expected one million hits

+

With the default validation token lifetime of 2 weeks, this corresponds to one token usage per +1.21 seconds.

+
Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl TokenLog for BloomTokenLog

Source§

fn check_and_insert( + &self, + nonce: u128, + issued: SystemTime, + lifetime: Duration, +) -> Result<(), TokenReuseError>

Record that the token was used and, ideally, return a token reuse error if the token may +have been already used previously Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.Chunk.html b/pr/395/docs/iroh_quinn_proto/struct.Chunk.html new file mode 100644 index 000000000..b24666798 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.Chunk.html @@ -0,0 +1,35 @@ +Chunk in iroh_quinn_proto - Rust

Chunk

Struct Chunk 

Source
pub struct Chunk {
+    pub offset: u64,
+    pub bytes: Bytes,
+}
Expand description

A chunk of data from the receive stream

+

Fields§

§offset: u64

The offset in the stream

+
§bytes: Bytes

The contents of the chunk

+

Trait Implementations§

Source§

impl Debug for Chunk

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for Chunk

Source§

fn eq(&self, other: &Chunk) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for Chunk

Source§

impl StructuralPartialEq for Chunk

Auto Trait Implementations§

§

impl !Freeze for Chunk

§

impl RefUnwindSafe for Chunk

§

impl Send for Chunk

§

impl Sync for Chunk

§

impl Unpin for Chunk

§

impl UnwindSafe for Chunk

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.Chunks.html b/pr/395/docs/iroh_quinn_proto/struct.Chunks.html new file mode 100644 index 000000000..bedd6e477 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.Chunks.html @@ -0,0 +1,41 @@ +Chunks in iroh_quinn_proto - Rust

Chunks

Struct Chunks 

Source
pub struct Chunks<'a> { /* private fields */ }
Expand description

Chunks returned from RecvStream::read().

+

§Note: Finalization Needed

+

Bytes read from the stream are not released from the congestion window until +either Self::finalize() is called, or this type is dropped.

+

It is recommended that you call Self::finalize() because it returns a flag +telling you whether reading from the stream has resulted in the need to transmit a packet.

+

If this type is leaked, the stream will remain blocked on the remote peer until +another read from the stream is done.

+

Implementations§

Source§

impl<'a> Chunks<'a>

Source

pub fn next(&mut self, max_length: usize) -> Result<Option<Chunk>, ReadError>

Next

+

Should call finalize() when done calling this.

+
Source

pub fn finalize(self) -> ShouldTransmit

Mark the read data as consumed from the stream.

+

The number of read bytes will be released from the congestion window, +allowing the remote peer to send more data if it was previously blocked.

+

If ShouldTransmit::should_transmit() returns true, +a packet needs to be sent to the peer informing them that the stream is unblocked. +This means that you should call Connection::poll_transmit() +and send the returned packet as soon as is reasonable, to unblock the remote peer.

+

Trait Implementations§

Source§

impl Drop for Chunks<'_>

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

§

impl<'a> Freeze for Chunks<'a>

§

impl<'a> RefUnwindSafe for Chunks<'a>

§

impl<'a> Send for Chunks<'a>

§

impl<'a> Sync for Chunks<'a>

§

impl<'a> Unpin for Chunks<'a>

§

impl<'a> !UnwindSafe for Chunks<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.ClientConfig.html b/pr/395/docs/iroh_quinn_proto/struct.ClientConfig.html new file mode 100644 index 000000000..d27bd8b23 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.ClientConfig.html @@ -0,0 +1,46 @@ +ClientConfig in iroh_quinn_proto - Rust

ClientConfig

Struct ClientConfig 

Source
#[non_exhaustive]
pub struct ClientConfig { /* private fields */ }
Expand description

Configuration for outgoing connections

+

Default values should be suitable for most internet applications.

+

Implementations§

Source§

impl ClientConfig

Source

pub fn new(crypto: Arc<dyn ClientConfig>) -> Self

Create a default config with a particular cryptographic config

+
Source

pub fn initial_dst_cid_provider( + &mut self, + initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>, +) -> &mut Self

Configure how to populate the destination CID of the initial packet when attempting to +establish a new connection

+

By default, it’s populated with random bytes with reasonable length, so unless you have +a good reason, you do not need to change it.

+

When prefer to override the default, please note that the generated connection ID MUST be +at least 8 bytes long and unpredictable, as per section 7.2 of RFC 9000.

+
Source

pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self

Set a custom TransportConfig

+
Source

pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut Self

Set a custom TokenStore

+

Defaults to TokenMemoryCache, which is suitable for most internet applications.

+
Source

pub fn version(&mut self, version: u32) -> &mut Self

Set the QUIC version to use

+
Source§

impl ClientConfig

Source

pub fn try_with_platform_verifier() -> Result<Self, Error>

Create a client configuration that trusts the platform’s native roots

+
Source

pub fn with_root_certificates( + roots: Arc<RootCertStore>, +) -> Result<Self, VerifierBuilderError>

Create a client configuration that trusts specified trust anchors

+

Trait Implementations§

Source§

impl Clone for ClientConfig

Source§

fn clone(&self) -> ClientConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClientConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.ClosedPath.html b/pr/395/docs/iroh_quinn_proto/struct.ClosedPath.html new file mode 100644 index 000000000..9f1eeb1b4 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.ClosedPath.html @@ -0,0 +1,33 @@ +ClosedPath in iroh_quinn_proto - Rust

ClosedPath

Struct ClosedPath 

Source
pub struct ClosedPath { /* private fields */ }
Expand description

Error indicating that a path has not been opened or has already been abandoned

+

Trait Implementations§

Source§

impl Clone for ClosedPath

Source§

fn clone(&self) -> ClosedPath

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosedPath

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ClosedPath

Source§

fn default() -> ClosedPath

Returns the “default value” for a type. Read more
Source§

impl Display for ClosedPath

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosedPath

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosedPath

Source§

fn eq(&self, other: &ClosedPath) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosedPath

Source§

impl StructuralPartialEq for ClosedPath

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.ClosedStream.html b/pr/395/docs/iroh_quinn_proto/struct.ClosedStream.html new file mode 100644 index 000000000..478d33c99 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.ClosedStream.html @@ -0,0 +1,33 @@ +ClosedStream in iroh_quinn_proto - Rust

ClosedStream

Struct ClosedStream 

Source
pub struct ClosedStream { /* private fields */ }
Expand description

Error indicating that a stream has not been opened or has already been finished or reset

+

Trait Implementations§

Source§

impl Clone for ClosedStream

Source§

fn clone(&self) -> ClosedStream

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosedStream

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ClosedStream

Source§

fn default() -> ClosedStream

Returns the “default value” for a type. Read more
Source§

impl Display for ClosedStream

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosedStream

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ClosedStream> for Error

Source§

fn from(x: ClosedStream) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ClosedStream

Source§

fn eq(&self, other: &ClosedStream) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosedStream

Source§

impl StructuralPartialEq for ClosedStream

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.Connection.html b/pr/395/docs/iroh_quinn_proto/struct.Connection.html new file mode 100644 index 000000000..4bffda6ac --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.Connection.html @@ -0,0 +1,250 @@ +Connection in iroh_quinn_proto - Rust

Connection

Struct Connection 

Source
pub struct Connection { /* private fields */ }
Expand description

Protocol state and logic for a single QUIC connection

+

Objects of this type receive ConnectionEvents and emit EndpointEvents and application +Events to make progress. To handle timeouts, a Connection returns timer updates and +expects timeouts through various methods. A number of simple getter methods are exposed +to allow callers to inspect some of the connection state.

+

Connection has roughly 4 types of methods:

+
    +
  • A. Simple getters, taking &self
  • +
  • B. Handlers for incoming events from the network or system, named handle_*.
  • +
  • C. State machine mutators, for incoming commands from the application. For convenience we +refer to this as “performing I/O” below, however as per the design of this library none of the +functions actually perform system-level I/O. For example, read and +write, but also things like reset.
  • +
  • D. Polling functions for outgoing events or actions for the caller to +take, named poll_*.
  • +
+

The simplest way to use this API correctly is to call (B) and (C) whenever +appropriate, then after each of those calls, as soon as feasible call all +polling methods (D) and deal with their outputs appropriately, e.g. by +passing it to the application or by making a system-level I/O call. You +should call the polling functions in this order:

+
    +
  1. poll_transmit
  2. +
  3. poll_timeout
  4. +
  5. poll_endpoint_events
  6. +
  7. poll
  8. +
+

Currently the only actual dependency is from (2) to (1), however additional +dependencies may be added in future, so the above order is recommended.

+

(A) may be called whenever desired.

+

Care should be made to ensure that the input events represent monotonically +increasing time. Specifically, calling handle_timeout +with events of the same Instant may be interleaved in any order with a +call to handle_event at that same instant; however +events or timeouts with different instants must not be interleaved.

+

Implementations§

Source§

impl Connection

Source

pub fn poll_timeout(&mut self) -> Option<Instant>

Returns the next time at which handle_timeout should be called

+

The value returned may change after:

+
    +
  • the application performed some I/O on the connection
  • +
  • a call was made to handle_event
  • +
  • a call to poll_transmit returned Some
  • +
  • a call was made to handle_timeout
  • +
+
Source

pub fn poll(&mut self) -> Option<Event>

Returns application-facing events

+

Connections should be polled for events after:

+
    +
  • a call was made to handle_event
  • +
  • a call was made to handle_timeout
  • +
+
Source

pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent>

Return endpoint-facing events

+
Source

pub fn streams(&mut self) -> Streams<'_>

Provide control over streams

+
Source

pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_>

Provide control over streams

+
Source

pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_>

Provide control over streams

+
Source

pub fn open_path_ensure( + &mut self, + network_path: FourTuple, + initial_status: PathStatus, + now: Instant, +) -> Result<(PathId, bool), PathError>

Opens a new path only if no path on the same network path currently exists.

+

This comparison will use FourTuple::is_probably_same_path on the given network_path +and pass it existing path’s network paths.

+

This means that you can pass local_ip: None to make the comparison only compare +remote addresses.

+

This avoids having to guess which local interface will be used to communicate with the +remote, should it not be known yet. We assume that if we already have a path to the remote, +the OS is likely to use the same interface to talk to said remote.

+

See also open_path. Returns (path_id, true) if the path already existed. (path_id, false) if was opened.

+
Source

pub fn open_path( + &mut self, + network_path: FourTuple, + initial_status: PathStatus, + now: Instant, +) -> Result<PathId, PathError>

Opens a new path

+

Further errors might occur and they will be emitted in PathEvent::LocallyClosed events. +When the path is opened it will be reported as an PathEvent::Opened.

+
Source

pub fn close_path( + &mut self, + now: Instant, + path_id: PathId, + error_code: VarInt, +) -> Result<(), ClosePathError>

Closes a path by sending a PATH_ABANDON frame

+

This will not allow closing the last path. It does allow closing paths which have +not yet been opened, as e.g. is the case when receiving a PATH_ABANDON from the peer +for a path that was never opened locally.

+
Source

pub fn paths(&self) -> Vec<PathId>

Returns all known paths.

+

There is no guarantee any of these paths are open or usable.

+
Source

pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath>

Gets the local PathStatus for a known PathId

+
Source

pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath>

Returns the path’s network path represented as a 4-tuple.

+
Source

pub fn set_path_status( + &mut self, + path_id: PathId, + status: PathStatus, +) -> Result<PathStatus, SetPathStatusError>

Sets the PathStatus for a known PathId

+

Returns the previous path status on success.

+
Source

pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus>

Returns the remote path status

+
Source

pub fn set_path_max_idle_timeout( + &mut self, + path_id: PathId, + timeout: Option<Duration>, +) -> Result<Option<Duration>, ClosedPath>

Sets the max_idle_timeout for a specific path

+

See TransportConfig::default_path_max_idle_timeout for details.

+

Returns the previous value of the setting.

+
Source

pub fn set_path_keep_alive_interval( + &mut self, + path_id: PathId, + interval: Option<Duration>, +) -> Result<Option<Duration>, ClosedPath>

Sets the keep_alive_interval for a specific path

+

See TransportConfig::default_path_keep_alive_interval for details.

+

Returns the previous value of the setting.

+
Source

pub fn poll_transmit( + &mut self, + now: Instant, + max_datagrams: NonZeroUsize, + buf: &mut Vec<u8>, +) -> Option<Transmit>

Returns packets to transmit

+

Connections should be polled for transmit after:

+
    +
  • the application performed some I/O on the connection
  • +
  • a call was made to handle_event
  • +
  • a call was made to handle_timeout
  • +
+

max_datagrams specifies how many datagrams can be returned inside a +single Transmit using GSO. This must be at least 1.

+
Source

pub fn handle_event(&mut self, event: ConnectionEvent)

Process ConnectionEvents generated by the associated Endpoint

+

Will execute protocol logic upon receipt of a connection event, in turn preparing signals +(including application Events, EndpointEvents and outgoing datagrams) that should be +extracted through the relevant methods.

+
Source

pub fn handle_timeout(&mut self, now: Instant)

Process timer expirations

+

Executes protocol logic, potentially preparing signals (including application Events, +EndpointEvents and outgoing datagrams) that should be extracted through the relevant +methods.

+

It is most efficient to call this immediately after the system clock reaches the latest +Instant that was output by poll_timeout; however spurious extra calls will simply +no-op and therefore are safe.

+
Source

pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes)

Close a connection immediately

+

This does not ensure delivery of outstanding data. It is the application’s responsibility to +call this only when all important communications have been completed, e.g. by calling +SendStream::finish on outstanding streams and waiting for the corresponding +StreamEvent::Finished event.

+

If Streams::send_streams returns 0, all outstanding stream data has been +delivered. There may still be data from the peer that has not been received.

+
Source

pub fn datagrams(&mut self) -> Datagrams<'_>

Control datagrams

+
Source

pub fn stats(&mut self) -> ConnectionStats

Returns connection statistics

+
Source

pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats>

Returns path statistics

+
Source

pub fn ping(&mut self)

Ping the remote endpoint

+

Causes an ACK-eliciting packet to be transmitted on the connection.

+
Source

pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath>

Ping the remote endpoint over a specific path

+

Causes an ACK-eliciting packet to be transmitted on the path.

+
Source

pub fn force_key_update(&mut self)

Update traffic keys spontaneously

+

This can be useful for testing key updates, as they otherwise only happen infrequently.

+
Source

pub fn crypto_session(&self) -> &dyn Session

Get a session reference

+
Source

pub fn is_handshaking(&self) -> bool

Whether the connection is in the process of being established

+

If this returns false, the connection may be either established or closed, signaled by the +emission of a Connected or ConnectionLost message respectively.

+
Source

pub fn is_closed(&self) -> bool

Whether the connection is closed

+

Closed connections cannot transport any further data. A connection becomes closed when +either peer application intentionally closes it, or when either transport layer detects an +error such as a time-out or certificate validation failure.

+

A ConnectionLost event is emitted with details when the connection becomes closed.

+
Source

pub fn is_drained(&self) -> bool

Whether there is no longer any need to keep the connection around

+

Closed connections become drained after a brief timeout to absorb any remaining in-flight +packets from the peer. All drained connections have been closed.

+
Source

pub fn accepted_0rtt(&self) -> bool

For clients, if the peer accepted the 0-RTT data packets

+

The value is meaningless until after the handshake completes.

+
Source

pub fn has_0rtt(&self) -> bool

Whether 0-RTT is/was possible during the handshake

+
Source

pub fn has_pending_retransmits(&self) -> bool

Whether there are any pending retransmits

+
Source

pub fn side(&self) -> Side

Look up whether we’re the client or server of this Connection

+
Source

pub fn path_observed_address( + &self, + path_id: PathId, +) -> Result<Option<SocketAddr>, ClosedPath>

Get the address observed by the remote over the given path

+
Source

pub fn rtt(&self, path_id: PathId) -> Option<Duration>

Current best estimate of this connection’s latency (round-trip-time)

+
Source

pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller>

Current state of this connection’s congestion controller, for debugging purposes

+
Source

pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt)

Modify the number of remotely initiated streams that may be concurrently open

+

No streams may be opened by the peer unless fewer than count are already open. Large +counts increase both minimum and worst-case memory consumption.

+
Source

pub fn set_max_concurrent_paths( + &mut self, + now: Instant, + count: NonZeroU32, +) -> Result<(), MultipathNotNegotiated>

Modify the number of open paths allowed when multipath is enabled

+

When reducing the number of concurrent paths this will only affect delaying sending +new MAX_PATH_ID frames until fewer than this number of paths are possible. To +actively reduce paths they must be closed using Connection::close_path, which +can also be used to close not-yet-opened paths.

+

If multipath is not negotiated (see the TransportConfig) this can not enable +multipath and will fail.

+
Source

pub fn max_concurrent_streams(&self, dir: Dir) -> u64

Current number of remotely initiated streams that may be concurrently open

+

If the target for this limit is reduced using set_max_concurrent_streams, +it will not change immediately, even if fewer streams are open. Instead, it will +decrement by one for each time a remotely initiated stream of matching directionality is closed.

+
Source

pub fn set_send_window(&mut self, send_window: u64)

Source

pub fn set_receive_window(&mut self, receive_window: VarInt)

Source

pub fn is_multipath_negotiated(&self) -> bool

Whether the Multipath for QUIC extension is enabled.

+

Multipath is only enabled after the handshake is completed and if it was enabled by both +peers.

+
Source

pub fn local_address_changed(&mut self)

Handle a change in the local address, i.e. an active migration

+
Source

pub fn current_mtu(&self) -> u16

Storage size required for the largest packet that can be transmitted on all currently +available paths

+

Buffers passed to Connection::poll_transmit should be at least this large.

+

When multipath is enabled, this value is the minimum MTU across all available paths.

+
Source

pub fn add_nat_traversal_address( + &mut self, + address: SocketAddr, +) -> Result<(), Error>

Add addresses the local endpoint considers are reachable for nat traversal.

+
Source

pub fn remove_nat_traversal_address( + &mut self, + address: SocketAddr, +) -> Result<(), Error>

Removes an address the endpoing no longer considers reachable for nat traversal

+

Addresses not present in the set will be silently ignored.

+
Source

pub fn get_local_nat_traversal_addresses( + &self, +) -> Result<Vec<SocketAddr>, Error>

Get the current local nat traversal addresses

+
Source

pub fn get_remote_nat_traversal_addresses( + &self, +) -> Result<Vec<SocketAddr>, Error>

Get the currently advertised nat traversal addresses by the server

+
Source

pub fn initiate_nat_traversal_round( + &mut self, + now: Instant, +) -> Result<Vec<SocketAddr>, Error>

Initiates a new nat traversal round

+

A nat traversal round involves advertising the client’s local addresses in REACH_OUT +frames, and initiating probing of the known remote addresses. When a new round is +initiated, the previous one is cancelled, and paths that have not been opened are closed.

+

Returns the server addresses that are now being probed. +If addresses fail due to spurious errors, these might succeed later and not be returned in +this set.

+

Trait Implementations§

Source§

impl Debug for Connection

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.ConnectionClose.html b/pr/395/docs/iroh_quinn_proto/struct.ConnectionClose.html new file mode 100644 index 000000000..c16e04b29 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.ConnectionClose.html @@ -0,0 +1,40 @@ +ConnectionClose in iroh_quinn_proto - Rust

ConnectionClose

Struct ConnectionClose 

Source
pub struct ConnectionClose {
+    pub error_code: TransportErrorCode,
+    pub frame_type: MaybeFrame,
+    pub reason: Bytes,
+}
Expand description

Reason given by the transport for closing the connection

+

Fields§

§error_code: TransportErrorCode

Class of error as encoded in the specification

+
§frame_type: MaybeFrame

Type of frame that caused the close

+
§reason: Bytes

Human-readable reason for the close

+

Trait Implementations§

Source§

impl Clone for ConnectionClose

Source§

fn clone(&self) -> ConnectionClose

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionClose

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectionClose

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<Error> for ConnectionClose

Source§

fn from(x: TransportError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionClose

Source§

fn eq(&self, other: &ConnectionClose) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConnectionClose

Source§

impl StructuralPartialEq for ConnectionClose

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.ConnectionEvent.html b/pr/395/docs/iroh_quinn_proto/struct.ConnectionEvent.html new file mode 100644 index 000000000..c19dfdeec --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.ConnectionEvent.html @@ -0,0 +1,25 @@ +ConnectionEvent in iroh_quinn_proto - Rust

ConnectionEvent

Struct ConnectionEvent 

Source
pub struct ConnectionEvent(/* private fields */);
Expand description

Events sent from an Endpoint to a Connection

+

Trait Implementations§

Source§

impl Debug for ConnectionEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.ConnectionHandle.html b/pr/395/docs/iroh_quinn_proto/struct.ConnectionHandle.html new file mode 100644 index 000000000..f78779448 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.ConnectionHandle.html @@ -0,0 +1,42 @@ +ConnectionHandle in iroh_quinn_proto - Rust

ConnectionHandle

Struct ConnectionHandle 

Source
pub struct ConnectionHandle(pub usize);
Expand description

Internal identifier for a Connection currently associated with an endpoint

+

Tuple Fields§

§0: usize

Trait Implementations§

Source§

impl Clone for ConnectionHandle

Source§

fn clone(&self) -> ConnectionHandle

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionHandle

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<ConnectionHandle> for usize

Source§

fn from(x: ConnectionHandle) -> Self

Converts to this type from the input type.
Source§

impl Hash for ConnectionHandle

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Index<ConnectionHandle> for Slab<ConnectionMeta>

Source§

type Output = ConnectionMeta

The returned type after indexing.
Source§

fn index(&self, ch: ConnectionHandle) -> &ConnectionMeta

Performs the indexing (container[index]) operation. Read more
Source§

impl IndexMut<ConnectionHandle> for Slab<ConnectionMeta>

Source§

fn index_mut(&mut self, ch: ConnectionHandle) -> &mut ConnectionMeta

Performs the mutable indexing (container[index]) operation. Read more
Source§

impl Ord for ConnectionHandle

Source§

fn cmp(&self, other: &ConnectionHandle) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for ConnectionHandle

Source§

fn eq(&self, other: &ConnectionHandle) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for ConnectionHandle

Source§

fn partial_cmp(&self, other: &ConnectionHandle) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for ConnectionHandle

Source§

impl Eq for ConnectionHandle

Source§

impl StructuralPartialEq for ConnectionHandle

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.ConnectionId.html b/pr/395/docs/iroh_quinn_proto/struct.ConnectionId.html new file mode 100644 index 000000000..7c18a12b2 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.ConnectionId.html @@ -0,0 +1,2468 @@ +ConnectionId in iroh_quinn_proto - Rust

ConnectionId

Struct ConnectionId 

Source
pub struct ConnectionId { /* private fields */ }
Expand description

Protocol-level identifier for a connection.

+

Mainly useful for identifying this connection’s packets on the wire with tools like Wireshark.

+

Implementations§

Source§

impl ConnectionId

Source

pub fn new(bytes: &[u8]) -> Self

Construct cid from byte array

+
Source

pub fn from_buf(buf: &mut (impl Buf + ?Sized), len: usize) -> Self

Constructs cid by reading len bytes from a Buf

+

Callers need to assure that buf.remaining() >= len

+

Methods from Deref<Target = [u8]>§

1.23.0 · Source

pub fn is_ascii(&self) -> bool

Checks if all bytes in this slice are within the ASCII range.

+
Source

pub fn as_ascii(&self) -> Option<&[AsciiChar]>

🔬This is a nightly-only experimental API. (ascii_char)

If this slice is_ascii, returns it as a slice of +ASCII characters, otherwise returns None.

+
Source

pub unsafe fn as_ascii_unchecked(&self) -> &[AsciiChar]

🔬This is a nightly-only experimental API. (ascii_char)

Converts this slice of bytes into a slice of ASCII characters, +without checking whether they’re valid.

+
§Safety
+

Every byte in the slice must be in 0..=127, or else this is UB.

+
1.23.0 · Source

pub fn eq_ignore_ascii_case(&self, other: &[u8]) -> bool

Checks that two slices are an ASCII case-insensitive match.

+

Same as to_ascii_lowercase(a) == to_ascii_lowercase(b), +but without allocating and copying temporaries.

+
1.23.0 · Source

pub fn make_ascii_uppercase(&mut self)

Converts this slice to its ASCII upper case equivalent in-place.

+

ASCII letters ‘a’ to ‘z’ are mapped to ‘A’ to ‘Z’, +but non-ASCII letters are unchanged.

+

To return a new uppercased value without modifying the existing one, use +to_ascii_uppercase.

+
1.23.0 · Source

pub fn make_ascii_lowercase(&mut self)

Converts this slice to its ASCII lower case equivalent in-place.

+

ASCII letters ‘A’ to ‘Z’ are mapped to ‘a’ to ‘z’, +but non-ASCII letters are unchanged.

+

To return a new lowercased value without modifying the existing one, use +to_ascii_lowercase.

+
1.60.0 · Source

pub fn escape_ascii(&self) -> EscapeAscii<'_>

Returns an iterator that produces an escaped version of this slice, +treating it as an ASCII string.

+
§Examples
+
let s = b"0\t\r\n'\"\\\x9d";
+let escaped = s.escape_ascii().to_string();
+assert_eq!(escaped, "0\\t\\r\\n\\'\\\"\\\\\\x9d");
1.80.0 · Source

pub fn trim_ascii_start(&self) -> &[u8]

Returns a byte slice with leading ASCII whitespace bytes removed.

+

‘Whitespace’ refers to the definition used by +u8::is_ascii_whitespace.

+
§Examples
+
assert_eq!(b" \t hello world\n".trim_ascii_start(), b"hello world\n");
+assert_eq!(b"  ".trim_ascii_start(), b"");
+assert_eq!(b"".trim_ascii_start(), b"");
1.80.0 · Source

pub fn trim_ascii_end(&self) -> &[u8]

Returns a byte slice with trailing ASCII whitespace bytes removed.

+

‘Whitespace’ refers to the definition used by +u8::is_ascii_whitespace.

+
§Examples
+
assert_eq!(b"\r hello world\n ".trim_ascii_end(), b"\r hello world");
+assert_eq!(b"  ".trim_ascii_end(), b"");
+assert_eq!(b"".trim_ascii_end(), b"");
1.80.0 · Source

pub fn trim_ascii(&self) -> &[u8]

Returns a byte slice with leading and trailing ASCII whitespace bytes +removed.

+

‘Whitespace’ refers to the definition used by +u8::is_ascii_whitespace.

+
§Examples
+
assert_eq!(b"\r hello world\n ".trim_ascii(), b"hello world");
+assert_eq!(b"  ".trim_ascii(), b"");
+assert_eq!(b"".trim_ascii(), b"");
1.0.0 · Source

pub fn len(&self) -> usize

Returns the number of elements in the slice.

+
§Examples
+
let a = [1, 2, 3];
+assert_eq!(a.len(), 3);
1.0.0 · Source

pub fn is_empty(&self) -> bool

Returns true if the slice has a length of 0.

+
§Examples
+
let a = [1, 2, 3];
+assert!(!a.is_empty());
+
+let b: &[i32] = &[];
+assert!(b.is_empty());
1.0.0 · Source

pub fn first(&self) -> Option<&T>

Returns the first element of the slice, or None if it is empty.

+
§Examples
+
let v = [10, 40, 30];
+assert_eq!(Some(&10), v.first());
+
+let w: &[i32] = &[];
+assert_eq!(None, w.first());
1.0.0 · Source

pub fn first_mut(&mut self) -> Option<&mut T>

Returns a mutable reference to the first element of the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(first) = x.first_mut() {
+    *first = 5;
+}
+assert_eq!(x, &[5, 1, 2]);
+
+let y: &mut [i32] = &mut [];
+assert_eq!(None, y.first_mut());
1.5.0 · Source

pub fn split_first(&self) -> Option<(&T, &[T])>

Returns the first and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((first, elements)) = x.split_first() {
+    assert_eq!(first, &0);
+    assert_eq!(elements, &[1, 2]);
+}
1.5.0 · Source

pub fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])>

Returns the first and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_mut() {
+    *first = 3;
+    elements[0] = 4;
+    elements[1] = 5;
+}
+assert_eq!(x, &[3, 4, 5]);
1.5.0 · Source

pub fn split_last(&self) -> Option<(&T, &[T])>

Returns the last and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((last, elements)) = x.split_last() {
+    assert_eq!(last, &2);
+    assert_eq!(elements, &[0, 1]);
+}
1.5.0 · Source

pub fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])>

Returns the last and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((last, elements)) = x.split_last_mut() {
+    *last = 3;
+    elements[0] = 4;
+    elements[1] = 5;
+}
+assert_eq!(x, &[4, 5, 3]);
1.0.0 · Source

pub fn last(&self) -> Option<&T>

Returns the last element of the slice, or None if it is empty.

+
§Examples
+
let v = [10, 40, 30];
+assert_eq!(Some(&30), v.last());
+
+let w: &[i32] = &[];
+assert_eq!(None, w.last());
1.0.0 · Source

pub fn last_mut(&mut self) -> Option<&mut T>

Returns a mutable reference to the last item in the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(last) = x.last_mut() {
+    *last = 10;
+}
+assert_eq!(x, &[0, 1, 10]);
+
+let y: &mut [i32] = &mut [];
+assert_eq!(None, y.last_mut());
1.77.0 · Source

pub fn first_chunk<const N: usize>(&self) -> Option<&[T; N]>

Returns an array reference to the first N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let u = [10, 40, 30];
+assert_eq!(Some(&[10, 40]), u.first_chunk::<2>());
+
+let v: &[i32] = &[10];
+assert_eq!(None, v.first_chunk::<2>());
+
+let w: &[i32] = &[];
+assert_eq!(Some(&[]), w.first_chunk::<0>());
1.77.0 · Source

pub fn first_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>

Returns a mutable array reference to the first N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(first) = x.first_chunk_mut::<2>() {
+    first[0] = 5;
+    first[1] = 4;
+}
+assert_eq!(x, &[5, 4, 2]);
+
+assert_eq!(None, x.first_chunk_mut::<4>());
1.77.0 · Source

pub fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])>

Returns an array reference to the first N items in the slice and the remaining slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_chunk::<2>() {
+    assert_eq!(first, &[0, 1]);
+    assert_eq!(elements, &[2]);
+}
+
+assert_eq!(None, x.split_first_chunk::<4>());
1.77.0 · Source

pub fn split_first_chunk_mut<const N: usize>( + &mut self, +) -> Option<(&mut [T; N], &mut [T])>

Returns a mutable array reference to the first N items in the slice and the remaining +slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_chunk_mut::<2>() {
+    first[0] = 3;
+    first[1] = 4;
+    elements[0] = 5;
+}
+assert_eq!(x, &[3, 4, 5]);
+
+assert_eq!(None, x.split_first_chunk_mut::<4>());
1.77.0 · Source

pub fn split_last_chunk<const N: usize>(&self) -> Option<(&[T], &[T; N])>

Returns an array reference to the last N items in the slice and the remaining slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((elements, last)) = x.split_last_chunk::<2>() {
+    assert_eq!(elements, &[0]);
+    assert_eq!(last, &[1, 2]);
+}
+
+assert_eq!(None, x.split_last_chunk::<4>());
1.77.0 · Source

pub fn split_last_chunk_mut<const N: usize>( + &mut self, +) -> Option<(&mut [T], &mut [T; N])>

Returns a mutable array reference to the last N items in the slice and the remaining +slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((elements, last)) = x.split_last_chunk_mut::<2>() {
+    last[0] = 3;
+    last[1] = 4;
+    elements[0] = 5;
+}
+assert_eq!(x, &[5, 3, 4]);
+
+assert_eq!(None, x.split_last_chunk_mut::<4>());
1.77.0 · Source

pub fn last_chunk<const N: usize>(&self) -> Option<&[T; N]>

Returns an array reference to the last N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let u = [10, 40, 30];
+assert_eq!(Some(&[40, 30]), u.last_chunk::<2>());
+
+let v: &[i32] = &[10];
+assert_eq!(None, v.last_chunk::<2>());
+
+let w: &[i32] = &[];
+assert_eq!(Some(&[]), w.last_chunk::<0>());
1.77.0 · Source

pub fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>

Returns a mutable array reference to the last N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(last) = x.last_chunk_mut::<2>() {
+    last[0] = 10;
+    last[1] = 20;
+}
+assert_eq!(x, &[0, 10, 20]);
+
+assert_eq!(None, x.last_chunk_mut::<4>());
1.0.0 · Source

pub fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output>
where + I: SliceIndex<[T]>,

Returns a reference to an element or subslice depending on the type of +index.

+
    +
  • If given a position, returns a reference to the element at that +position or None if out of bounds.
  • +
  • If given a range, returns the subslice corresponding to that range, +or None if out of bounds.
  • +
+
§Examples
+
let v = [10, 40, 30];
+assert_eq!(Some(&40), v.get(1));
+assert_eq!(Some(&[10, 40][..]), v.get(0..2));
+assert_eq!(None, v.get(3));
+assert_eq!(None, v.get(0..4));
1.0.0 · Source

pub fn get_mut<I>( + &mut self, + index: I, +) -> Option<&mut <I as SliceIndex<[T]>>::Output>
where + I: SliceIndex<[T]>,

Returns a mutable reference to an element or subslice depending on the +type of index (see get) or None if the index is out of bounds.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(elem) = x.get_mut(1) {
+    *elem = 42;
+}
+assert_eq!(x, &[0, 42, 2]);
1.0.0 · Source

pub unsafe fn get_unchecked<I>( + &self, + index: I, +) -> &<I as SliceIndex<[T]>>::Output
where + I: SliceIndex<[T]>,

Returns a reference to an element or subslice, without doing bounds +checking.

+

For a safe alternative see get.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used.

+

You can think of this like .get(index).unwrap_unchecked(). It’s UB +to call .get_unchecked(len), even if you immediately convert to a +pointer. And it’s UB to call .get_unchecked(..len + 1), +.get_unchecked(..=len), or similar.

+
§Examples
+
let x = &[1, 2, 4];
+
+unsafe {
+    assert_eq!(x.get_unchecked(1), &2);
+}
1.0.0 · Source

pub unsafe fn get_unchecked_mut<I>( + &mut self, + index: I, +) -> &mut <I as SliceIndex<[T]>>::Output
where + I: SliceIndex<[T]>,

Returns a mutable reference to an element or subslice, without doing +bounds checking.

+

For a safe alternative see get_mut.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used.

+

You can think of this like .get_mut(index).unwrap_unchecked(). It’s +UB to call .get_unchecked_mut(len), even if you immediately convert +to a pointer. And it’s UB to call .get_unchecked_mut(..len + 1), +.get_unchecked_mut(..=len), or similar.

+
§Examples
+
let x = &mut [1, 2, 4];
+
+unsafe {
+    let elem = x.get_unchecked_mut(1);
+    *elem = 13;
+}
+assert_eq!(x, &[1, 13, 4]);
1.0.0 · Source

pub fn as_ptr(&self) -> *const T

Returns a raw pointer to the slice’s buffer.

+

The caller must ensure that the slice outlives the pointer this +function returns, or else it will end up dangling.

+

The caller must also ensure that the memory the pointer (non-transitively) points to +is never written to (except inside an UnsafeCell) using this pointer or any pointer +derived from it. If you need to mutate the contents of the slice, use as_mut_ptr.

+

Modifying the container referenced by this slice may cause its buffer +to be reallocated, which would also make any pointers to it invalid.

+
§Examples
+
let x = &[1, 2, 4];
+let x_ptr = x.as_ptr();
+
+unsafe {
+    for i in 0..x.len() {
+        assert_eq!(x.get_unchecked(i), &*x_ptr.add(i));
+    }
+}
1.0.0 · Source

pub fn as_mut_ptr(&mut self) -> *mut T

Returns an unsafe mutable pointer to the slice’s buffer.

+

The caller must ensure that the slice outlives the pointer this +function returns, or else it will end up dangling.

+

Modifying the container referenced by this slice may cause its buffer +to be reallocated, which would also make any pointers to it invalid.

+
§Examples
+
let x = &mut [1, 2, 4];
+let x_ptr = x.as_mut_ptr();
+
+unsafe {
+    for i in 0..x.len() {
+        *x_ptr.add(i) += 2;
+    }
+}
+assert_eq!(x, &[3, 4, 6]);
1.48.0 · Source

pub fn as_ptr_range(&self) -> Range<*const T>

Returns the two raw pointers spanning the slice.

+

The returned range is half-open, which means that the end pointer +points one past the last element of the slice. This way, an empty +slice is represented by two equal pointers, and the difference between +the two pointers represents the size of the slice.

+

See as_ptr for warnings on using these pointers. The end pointer +requires extra caution, as it does not point to a valid element in the +slice.

+

This function is useful for interacting with foreign interfaces which +use two pointers to refer to a range of elements in memory, as is +common in C++.

+

It can also be useful to check if a pointer to an element refers to an +element of this slice:

+ +
let a = [1, 2, 3];
+let x = &a[1] as *const _;
+let y = &5 as *const _;
+
+assert!(a.as_ptr_range().contains(&x));
+assert!(!a.as_ptr_range().contains(&y));
1.48.0 · Source

pub fn as_mut_ptr_range(&mut self) -> Range<*mut T>

Returns the two unsafe mutable pointers spanning the slice.

+

The returned range is half-open, which means that the end pointer +points one past the last element of the slice. This way, an empty +slice is represented by two equal pointers, and the difference between +the two pointers represents the size of the slice.

+

See as_mut_ptr for warnings on using these pointers. The end +pointer requires extra caution, as it does not point to a valid element +in the slice.

+

This function is useful for interacting with foreign interfaces which +use two pointers to refer to a range of elements in memory, as is +common in C++.

+
Source

pub fn as_array<const N: usize>(&self) -> Option<&[T; N]>

🔬This is a nightly-only experimental API. (slice_as_array)

Gets a reference to the underlying array.

+

If N is not exactly equal to the length of self, then this method returns None.

+
Source

pub fn as_mut_array<const N: usize>(&mut self) -> Option<&mut [T; N]>

🔬This is a nightly-only experimental API. (slice_as_array)

Gets a mutable reference to the slice’s underlying array.

+

If N is not exactly equal to the length of self, then this method returns None.

+
1.0.0 · Source

pub fn swap(&mut self, a: usize, b: usize)

Swaps two elements in the slice.

+

If a equals to b, it’s guaranteed that elements won’t change value.

+
§Arguments
+
    +
  • a - The index of the first element
  • +
  • b - The index of the second element
  • +
+
§Panics
+

Panics if a or b are out of bounds.

+
§Examples
+
let mut v = ["a", "b", "c", "d", "e"];
+v.swap(2, 4);
+assert!(v == ["a", "b", "e", "d", "c"]);
Source

pub unsafe fn swap_unchecked(&mut self, a: usize, b: usize)

🔬This is a nightly-only experimental API. (slice_swap_unchecked)

Swaps two elements in the slice, without doing bounds checking.

+

For a safe alternative see swap.

+
§Arguments
+
    +
  • a - The index of the first element
  • +
  • b - The index of the second element
  • +
+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior. +The caller has to ensure that a < self.len() and b < self.len().

+
§Examples
+
#![feature(slice_swap_unchecked)]
+
+let mut v = ["a", "b", "c", "d"];
+// SAFETY: we know that 1 and 3 are both indices of the slice
+unsafe { v.swap_unchecked(1, 3) };
+assert!(v == ["a", "d", "c", "b"]);
1.0.0 · Source

pub fn reverse(&mut self)

Reverses the order of elements in the slice, in place.

+
§Examples
+
let mut v = [1, 2, 3];
+v.reverse();
+assert!(v == [3, 2, 1]);
1.0.0 · Source

pub fn iter(&self) -> Iter<'_, T>

Returns an iterator over the slice.

+

The iterator yields all items from start to end.

+
§Examples
+
let x = &[1, 2, 4];
+let mut iterator = x.iter();
+
+assert_eq!(iterator.next(), Some(&1));
+assert_eq!(iterator.next(), Some(&2));
+assert_eq!(iterator.next(), Some(&4));
+assert_eq!(iterator.next(), None);
1.0.0 · Source

pub fn iter_mut(&mut self) -> IterMut<'_, T>

Returns an iterator that allows modifying each value.

+

The iterator yields all items from start to end.

+
§Examples
+
let x = &mut [1, 2, 4];
+for elem in x.iter_mut() {
+    *elem += 2;
+}
+assert_eq!(x, &[3, 4, 6]);
1.0.0 · Source

pub fn windows(&self, size: usize) -> Windows<'_, T>

Returns an iterator over all contiguous windows of length +size. The windows overlap. If the slice is shorter than +size, the iterator returns no values.

+
§Panics
+

Panics if size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.windows(3);
+assert_eq!(iter.next().unwrap(), &['l', 'o', 'r']);
+assert_eq!(iter.next().unwrap(), &['o', 'r', 'e']);
+assert_eq!(iter.next().unwrap(), &['r', 'e', 'm']);
+assert!(iter.next().is_none());
+

If the slice is shorter than size:

+ +
let slice = ['f', 'o', 'o'];
+let mut iter = slice.windows(4);
+assert!(iter.next().is_none());
+

Because the Iterator trait cannot represent the required lifetimes, +there is no windows_mut analog to windows; +[0,1,2].windows_mut(2).collect() would violate the rules of references +(though a LendingIterator analog is possible). You can sometimes use +Cell::as_slice_of_cells in +conjunction with windows instead:

+ +
use std::cell::Cell;
+
+let mut array = ['R', 'u', 's', 't', ' ', '2', '0', '1', '5'];
+let slice = &mut array[..];
+let slice_of_cells: &[Cell<char>] = Cell::from_mut(slice).as_slice_of_cells();
+for w in slice_of_cells.windows(3) {
+    Cell::swap(&w[0], &w[2]);
+}
+assert_eq!(array, ['s', 't', ' ', '2', '0', '1', '5', 'u', 'R']);
1.0.0 · Source

pub fn chunks(&self, chunk_size: usize) -> Chunks<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last chunk will not have length chunk_size.

+

See chunks_exact for a variant of this iterator that returns chunks of always exactly +chunk_size elements, and rchunks for the same iterator but starting at the end of the +slice.

+

If your chunk_size is a constant, consider using as_chunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.chunks(2);
+assert_eq!(iter.next().unwrap(), &['l', 'o']);
+assert_eq!(iter.next().unwrap(), &['r', 'e']);
+assert_eq!(iter.next().unwrap(), &['m']);
+assert!(iter.next().is_none());
1.0.0 · Source

pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last chunk will not have length chunk_size.

+

See chunks_exact_mut for a variant of this iterator that returns chunks of always +exactly chunk_size elements, and rchunks_mut for the same iterator but starting at +the end of the slice.

+

If your chunk_size is a constant, consider using as_chunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.chunks_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 3]);
1.31.0 · Source

pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last up to chunk_size-1 elements will be omitted and can be retrieved +from the remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of chunks.

+

See chunks for a variant of this iterator that also returns the remainder as a smaller +chunk, and rchunks_exact for the same iterator but starting at the end of the slice.

+

If your chunk_size is a constant, consider using as_chunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.chunks_exact(2);
+assert_eq!(iter.next().unwrap(), &['l', 'o']);
+assert_eq!(iter.next().unwrap(), &['r', 'e']);
+assert!(iter.next().is_none());
+assert_eq!(iter.remainder(), &['m']);
1.31.0 · Source

pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last up to chunk_size-1 elements will be omitted and can be +retrieved from the into_remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of chunks_mut.

+

See chunks_mut for a variant of this iterator that also returns the remainder as a +smaller chunk, and rchunks_exact_mut for the same iterator but starting at the end of +the slice.

+

If your chunk_size is a constant, consider using as_chunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.chunks_exact_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 0]);
1.88.0 · Source

pub unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]]

Splits the slice into a slice of N-element arrays, +assuming that there’s no remainder.

+

This is the inverse operation to as_flattened.

+

As this is unsafe, consider whether you could use as_chunks or +as_rchunks instead, perhaps via something like +if let (chunks, []) = slice.as_chunks() or +let (chunks, []) = slice.as_chunks() else { unreachable!() };.

+
§Safety
+

This may only be called when

+
    +
  • The slice splits exactly into N-element chunks (aka self.len() % N == 0).
  • +
  • N != 0.
  • +
+
§Examples
+
let slice: &[char] = &['l', 'o', 'r', 'e', 'm', '!'];
+let chunks: &[[char; 1]] =
+    // SAFETY: 1-element chunks never have remainder
+    unsafe { slice.as_chunks_unchecked() };
+assert_eq!(chunks, &[['l'], ['o'], ['r'], ['e'], ['m'], ['!']]);
+let chunks: &[[char; 3]] =
+    // SAFETY: The slice length (6) is a multiple of 3
+    unsafe { slice.as_chunks_unchecked() };
+assert_eq!(chunks, &[['l', 'o', 'r'], ['e', 'm', '!']]);
+
+// These would be unsound:
+// let chunks: &[[_; 5]] = slice.as_chunks_unchecked() // The slice length is not a multiple of 5
+// let chunks: &[[_; 0]] = slice.as_chunks_unchecked() // Zero-length chunks are never allowed
1.88.0 · Source

pub fn as_chunks<const N: usize>(&self) -> (&[[T; N]], &[T])

Splits the slice into a slice of N-element arrays, +starting at the beginning of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (chunks, remainder) = slice.as_chunks(), then:

+
    +
  • chunks.len() equals slice.len() / N,
  • +
  • remainder.len() equals slice.len() % N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let (chunks, remainder) = slice.as_chunks();
+assert_eq!(chunks, &[['l', 'o'], ['r', 'e']]);
+assert_eq!(remainder, &['m']);
+

If you expect the slice to be an exact multiple, you can combine +let-else with an empty slice pattern:

+ +
let slice = ['R', 'u', 's', 't'];
+let (chunks, []) = slice.as_chunks::<2>() else {
+    panic!("slice didn't have even length")
+};
+assert_eq!(chunks, &[['R', 'u'], ['s', 't']]);
1.88.0 · Source

pub fn as_rchunks<const N: usize>(&self) -> (&[T], &[[T; N]])

Splits the slice into a slice of N-element arrays, +starting at the end of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (remainder, chunks) = slice.as_rchunks(), then:

+
    +
  • remainder.len() equals slice.len() % N,
  • +
  • chunks.len() equals slice.len() / N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let (remainder, chunks) = slice.as_rchunks();
+assert_eq!(remainder, &['l']);
+assert_eq!(chunks, &[['o', 'r'], ['e', 'm']]);
1.88.0 · Source

pub unsafe fn as_chunks_unchecked_mut<const N: usize>( + &mut self, +) -> &mut [[T; N]]

Splits the slice into a slice of N-element arrays, +assuming that there’s no remainder.

+

This is the inverse operation to as_flattened_mut.

+

As this is unsafe, consider whether you could use as_chunks_mut or +as_rchunks_mut instead, perhaps via something like +if let (chunks, []) = slice.as_chunks_mut() or +let (chunks, []) = slice.as_chunks_mut() else { unreachable!() };.

+
§Safety
+

This may only be called when

+
    +
  • The slice splits exactly into N-element chunks (aka self.len() % N == 0).
  • +
  • N != 0.
  • +
+
§Examples
+
let slice: &mut [char] = &mut ['l', 'o', 'r', 'e', 'm', '!'];
+let chunks: &mut [[char; 1]] =
+    // SAFETY: 1-element chunks never have remainder
+    unsafe { slice.as_chunks_unchecked_mut() };
+chunks[0] = ['L'];
+assert_eq!(chunks, &[['L'], ['o'], ['r'], ['e'], ['m'], ['!']]);
+let chunks: &mut [[char; 3]] =
+    // SAFETY: The slice length (6) is a multiple of 3
+    unsafe { slice.as_chunks_unchecked_mut() };
+chunks[1] = ['a', 'x', '?'];
+assert_eq!(slice, &['L', 'o', 'r', 'a', 'x', '?']);
+
+// These would be unsound:
+// let chunks: &[[_; 5]] = slice.as_chunks_unchecked_mut() // The slice length is not a multiple of 5
+// let chunks: &[[_; 0]] = slice.as_chunks_unchecked_mut() // Zero-length chunks are never allowed
1.88.0 · Source

pub fn as_chunks_mut<const N: usize>(&mut self) -> (&mut [[T; N]], &mut [T])

Splits the slice into a slice of N-element arrays, +starting at the beginning of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (chunks, remainder) = slice.as_chunks_mut(), then:

+
    +
  • chunks.len() equals slice.len() / N,
  • +
  • remainder.len() equals slice.len() % N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened_mut.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+let (chunks, remainder) = v.as_chunks_mut();
+remainder[0] = 9;
+for chunk in chunks {
+    *chunk = [count; 2];
+    count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 9]);
1.88.0 · Source

pub fn as_rchunks_mut<const N: usize>(&mut self) -> (&mut [T], &mut [[T; N]])

Splits the slice into a slice of N-element arrays, +starting at the end of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (remainder, chunks) = slice.as_rchunks_mut(), then:

+
    +
  • remainder.len() equals slice.len() % N,
  • +
  • chunks.len() equals slice.len() / N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened_mut.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+let (remainder, chunks) = v.as_rchunks_mut();
+remainder[0] = 9;
+for chunk in chunks {
+    *chunk = [count; 2];
+    count += 1;
+}
+assert_eq!(v, &[9, 1, 1, 2, 2]);
Source

pub fn array_windows<const N: usize>(&self) -> ArrayWindows<'_, T, N>

🔬This is a nightly-only experimental API. (array_windows)

Returns an iterator over overlapping windows of N elements of a slice, +starting at the beginning of the slice.

+

This is the const generic equivalent of windows.

+

If N is greater than the size of the slice, it will return no windows.

+
§Panics
+

Panics if N is zero. This check will most probably get changed to a compile time +error before this method gets stabilized.

+
§Examples
+
#![feature(array_windows)]
+let slice = [0, 1, 2, 3];
+let mut iter = slice.array_windows();
+assert_eq!(iter.next().unwrap(), &[0, 1]);
+assert_eq!(iter.next().unwrap(), &[1, 2]);
+assert_eq!(iter.next().unwrap(), &[2, 3]);
+assert!(iter.next().is_none());
1.31.0 · Source

pub fn rchunks(&self, chunk_size: usize) -> RChunks<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the end +of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last chunk will not have length chunk_size.

+

See rchunks_exact for a variant of this iterator that returns chunks of always exactly +chunk_size elements, and chunks for the same iterator but starting at the beginning +of the slice.

+

If your chunk_size is a constant, consider using as_rchunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.rchunks(2);
+assert_eq!(iter.next().unwrap(), &['e', 'm']);
+assert_eq!(iter.next().unwrap(), &['o', 'r']);
+assert_eq!(iter.next().unwrap(), &['l']);
+assert!(iter.next().is_none());
1.31.0 · Source

pub fn rchunks_mut(&mut self, chunk_size: usize) -> RChunksMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the end +of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last chunk will not have length chunk_size.

+

See rchunks_exact_mut for a variant of this iterator that returns chunks of always +exactly chunk_size elements, and chunks_mut for the same iterator but starting at the +beginning of the slice.

+

If your chunk_size is a constant, consider using as_rchunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.rchunks_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[3, 2, 2, 1, 1]);
1.31.0 · Source

pub fn rchunks_exact(&self, chunk_size: usize) -> RChunksExact<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +end of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last up to chunk_size-1 elements will be omitted and can be retrieved +from the remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of rchunks.

+

See rchunks for a variant of this iterator that also returns the remainder as a smaller +chunk, and chunks_exact for the same iterator but starting at the beginning of the +slice.

+

If your chunk_size is a constant, consider using as_rchunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.rchunks_exact(2);
+assert_eq!(iter.next().unwrap(), &['e', 'm']);
+assert_eq!(iter.next().unwrap(), &['o', 'r']);
+assert!(iter.next().is_none());
+assert_eq!(iter.remainder(), &['l']);
1.31.0 · Source

pub fn rchunks_exact_mut(&mut self, chunk_size: usize) -> RChunksExactMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the end +of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last up to chunk_size-1 elements will be omitted and can be +retrieved from the into_remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of chunks_mut.

+

See rchunks_mut for a variant of this iterator that also returns the remainder as a +smaller chunk, and chunks_exact_mut for the same iterator but starting at the beginning +of the slice.

+

If your chunk_size is a constant, consider using as_rchunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.rchunks_exact_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[0, 2, 2, 1, 1]);
1.77.0 · Source

pub fn chunk_by<F>(&self, pred: F) -> ChunkBy<'_, T, F>
where + F: FnMut(&T, &T) -> bool,

Returns an iterator over the slice producing non-overlapping runs +of elements using the predicate to separate them.

+

The predicate is called for every pair of consecutive elements, +meaning that it is called on slice[0] and slice[1], +followed by slice[1] and slice[2], and so on.

+
§Examples
+
let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
+
+let mut iter = slice.chunk_by(|a, b| a == b);
+
+assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
+assert_eq!(iter.next(), Some(&[3, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
+assert_eq!(iter.next(), None);
+

This method can be used to extract the sorted subslices:

+ +
let slice = &[1, 1, 2, 3, 2, 3, 2, 3, 4];
+
+let mut iter = slice.chunk_by(|a, b| a <= b);
+
+assert_eq!(iter.next(), Some(&[1, 1, 2, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 3, 4][..]));
+assert_eq!(iter.next(), None);
1.77.0 · Source

pub fn chunk_by_mut<F>(&mut self, pred: F) -> ChunkByMut<'_, T, F>
where + F: FnMut(&T, &T) -> bool,

Returns an iterator over the slice producing non-overlapping mutable +runs of elements using the predicate to separate them.

+

The predicate is called for every pair of consecutive elements, +meaning that it is called on slice[0] and slice[1], +followed by slice[1] and slice[2], and so on.

+
§Examples
+
let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2];
+
+let mut iter = slice.chunk_by_mut(|a, b| a == b);
+
+assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
+assert_eq!(iter.next(), Some(&mut [3, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
+assert_eq!(iter.next(), None);
+

This method can be used to extract the sorted subslices:

+ +
let slice = &mut [1, 1, 2, 3, 2, 3, 2, 3, 4];
+
+let mut iter = slice.chunk_by_mut(|a, b| a <= b);
+
+assert_eq!(iter.next(), Some(&mut [1, 1, 2, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 3, 4][..]));
+assert_eq!(iter.next(), None);
1.0.0 · Source

pub fn split_at(&self, mid: usize) -> (&[T], &[T])

Divides one slice into two at an index.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+
§Panics
+

Panics if mid > len. For a non-panicking alternative see +split_at_checked.

+
§Examples
+
let v = ['a', 'b', 'c'];
+
+{
+   let (left, right) = v.split_at(0);
+   assert_eq!(left, []);
+   assert_eq!(right, ['a', 'b', 'c']);
+}
+
+{
+    let (left, right) = v.split_at(2);
+    assert_eq!(left, ['a', 'b']);
+    assert_eq!(right, ['c']);
+}
+
+{
+    let (left, right) = v.split_at(3);
+    assert_eq!(left, ['a', 'b', 'c']);
+    assert_eq!(right, []);
+}
1.0.0 · Source

pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T])

Divides one mutable slice into two at an index.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+
§Panics
+

Panics if mid > len. For a non-panicking alternative see +split_at_mut_checked.

+
§Examples
+
let mut v = [1, 0, 3, 0, 5, 6];
+let (left, right) = v.split_at_mut(2);
+assert_eq!(left, [1, 0]);
+assert_eq!(right, [3, 0, 5, 6]);
+left[1] = 2;
+right[1] = 4;
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
1.79.0 · Source

pub unsafe fn split_at_unchecked(&self, mid: usize) -> (&[T], &[T])

Divides one slice into two at an index, without doing bounds checking.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+

For a safe alternative see split_at.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used. The caller has to ensure that +0 <= mid <= self.len().

+
§Examples
+
let v = ['a', 'b', 'c'];
+
+unsafe {
+   let (left, right) = v.split_at_unchecked(0);
+   assert_eq!(left, []);
+   assert_eq!(right, ['a', 'b', 'c']);
+}
+
+unsafe {
+    let (left, right) = v.split_at_unchecked(2);
+    assert_eq!(left, ['a', 'b']);
+    assert_eq!(right, ['c']);
+}
+
+unsafe {
+    let (left, right) = v.split_at_unchecked(3);
+    assert_eq!(left, ['a', 'b', 'c']);
+    assert_eq!(right, []);
+}
1.79.0 · Source

pub unsafe fn split_at_mut_unchecked( + &mut self, + mid: usize, +) -> (&mut [T], &mut [T])

Divides one mutable slice into two at an index, without doing bounds checking.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+

For a safe alternative see split_at_mut.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used. The caller has to ensure that +0 <= mid <= self.len().

+
§Examples
+
let mut v = [1, 0, 3, 0, 5, 6];
+// scoped to restrict the lifetime of the borrows
+unsafe {
+    let (left, right) = v.split_at_mut_unchecked(2);
+    assert_eq!(left, [1, 0]);
+    assert_eq!(right, [3, 0, 5, 6]);
+    left[1] = 2;
+    right[1] = 4;
+}
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
1.80.0 · Source

pub fn split_at_checked(&self, mid: usize) -> Option<(&[T], &[T])>

Divides one slice into two at an index, returning None if the slice is +too short.

+

If mid ≤ len returns a pair of slices where the first will contain all +indices from [0, mid) (excluding the index mid itself) and the +second will contain all indices from [mid, len) (excluding the index +len itself).

+

Otherwise, if mid > len, returns None.

+
§Examples
+
let v = [1, -2, 3, -4, 5, -6];
+
+{
+   let (left, right) = v.split_at_checked(0).unwrap();
+   assert_eq!(left, []);
+   assert_eq!(right, [1, -2, 3, -4, 5, -6]);
+}
+
+{
+    let (left, right) = v.split_at_checked(2).unwrap();
+    assert_eq!(left, [1, -2]);
+    assert_eq!(right, [3, -4, 5, -6]);
+}
+
+{
+    let (left, right) = v.split_at_checked(6).unwrap();
+    assert_eq!(left, [1, -2, 3, -4, 5, -6]);
+    assert_eq!(right, []);
+}
+
+assert_eq!(None, v.split_at_checked(7));
1.80.0 · Source

pub fn split_at_mut_checked( + &mut self, + mid: usize, +) -> Option<(&mut [T], &mut [T])>

Divides one mutable slice into two at an index, returning None if the +slice is too short.

+

If mid ≤ len returns a pair of slices where the first will contain all +indices from [0, mid) (excluding the index mid itself) and the +second will contain all indices from [mid, len) (excluding the index +len itself).

+

Otherwise, if mid > len, returns None.

+
§Examples
+
let mut v = [1, 0, 3, 0, 5, 6];
+
+if let Some((left, right)) = v.split_at_mut_checked(2) {
+    assert_eq!(left, [1, 0]);
+    assert_eq!(right, [3, 0, 5, 6]);
+    left[1] = 2;
+    right[1] = 4;
+}
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
+
+assert_eq!(None, v.split_at_mut_checked(7));
1.0.0 · Source

pub fn split<F>(&self, pred: F) -> Split<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred. The matched element is not contained in the subslices.

+
§Examples
+
let slice = [10, 40, 33, 20];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
+

If the first element is matched, an empty slice will be the first item +returned by the iterator. Similarly, if the last element in the slice +is matched, an empty slice will be the last item returned by the +iterator:

+ +
let slice = [10, 40, 33];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40]);
+assert_eq!(iter.next().unwrap(), &[]);
+assert!(iter.next().is_none());
+

If two matched elements are directly adjacent, an empty slice will be +present between them:

+ +
let slice = [10, 6, 33, 20];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10]);
+assert_eq!(iter.next().unwrap(), &[]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
1.0.0 · Source

pub fn split_mut<F>(&mut self, pred: F) -> SplitMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that +match pred. The matched element is not contained in the subslices.

+
§Examples
+
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.split_mut(|num| *num % 3 == 0) {
+    group[0] = 1;
+}
+assert_eq!(v, [1, 40, 30, 1, 60, 1]);
1.51.0 · Source

pub fn split_inclusive<F>(&self, pred: F) -> SplitInclusive<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred. The matched element is contained in the end of the previous +subslice as a terminator.

+
§Examples
+
let slice = [10, 40, 33, 20];
+let mut iter = slice.split_inclusive(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40, 33]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
+

If the last element of the slice is matched, +that element will be considered the terminator of the preceding slice. +That slice will be the last item returned by the iterator.

+ +
let slice = [3, 10, 40, 33];
+let mut iter = slice.split_inclusive(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[3]);
+assert_eq!(iter.next().unwrap(), &[10, 40, 33]);
+assert!(iter.next().is_none());
1.51.0 · Source

pub fn split_inclusive_mut<F>(&mut self, pred: F) -> SplitInclusiveMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that +match pred. The matched element is contained in the previous +subslice as a terminator.

+
§Examples
+
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.split_inclusive_mut(|num| *num % 3 == 0) {
+    let terminator_idx = group.len()-1;
+    group[terminator_idx] = 1;
+}
+assert_eq!(v, [10, 40, 1, 20, 1, 1]);
1.27.0 · Source

pub fn rsplit<F>(&self, pred: F) -> RSplit<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred, starting at the end of the slice and working backwards. +The matched element is not contained in the subslices.

+
§Examples
+
let slice = [11, 22, 33, 0, 44, 55];
+let mut iter = slice.rsplit(|num| *num == 0);
+
+assert_eq!(iter.next().unwrap(), &[44, 55]);
+assert_eq!(iter.next().unwrap(), &[11, 22, 33]);
+assert_eq!(iter.next(), None);
+

As with split(), if the first or last element is matched, an empty +slice will be the first (or last) item returned by the iterator.

+ +
let v = &[0, 1, 1, 2, 3, 5, 8];
+let mut it = v.rsplit(|n| *n % 2 == 0);
+assert_eq!(it.next().unwrap(), &[]);
+assert_eq!(it.next().unwrap(), &[3, 5]);
+assert_eq!(it.next().unwrap(), &[1, 1]);
+assert_eq!(it.next().unwrap(), &[]);
+assert_eq!(it.next(), None);
1.27.0 · Source

pub fn rsplit_mut<F>(&mut self, pred: F) -> RSplitMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that +match pred, starting at the end of the slice and working +backwards. The matched element is not contained in the subslices.

+
§Examples
+
let mut v = [100, 400, 300, 200, 600, 500];
+
+let mut count = 0;
+for group in v.rsplit_mut(|num| *num % 3 == 0) {
+    count += 1;
+    group[0] = count;
+}
+assert_eq!(v, [3, 400, 300, 2, 600, 1]);
1.0.0 · Source

pub fn splitn<F>(&self, n: usize, pred: F) -> SplitN<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred, limited to returning at most n items. The matched element is +not contained in the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+

Print the slice split once by numbers divisible by 3 (i.e., [10, 40], +[20, 60, 50]):

+ +
let v = [10, 40, 30, 20, 60, 50];
+
+for group in v.splitn(2, |num| *num % 3 == 0) {
+    println!("{group:?}");
+}
1.0.0 · Source

pub fn splitn_mut<F>(&mut self, n: usize, pred: F) -> SplitNMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that match +pred, limited to returning at most n items. The matched element is +not contained in the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.splitn_mut(2, |num| *num % 3 == 0) {
+    group[0] = 1;
+}
+assert_eq!(v, [1, 40, 30, 1, 60, 50]);
1.0.0 · Source

pub fn rsplitn<F>(&self, n: usize, pred: F) -> RSplitN<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred limited to returning at most n items. This starts at the end of +the slice and works backwards. The matched element is not contained in +the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+

Print the slice split once, starting from the end, by numbers divisible +by 3 (i.e., [50], [10, 40, 30, 20]):

+ +
let v = [10, 40, 30, 20, 60, 50];
+
+for group in v.rsplitn(2, |num| *num % 3 == 0) {
+    println!("{group:?}");
+}
1.0.0 · Source

pub fn rsplitn_mut<F>(&mut self, n: usize, pred: F) -> RSplitNMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred limited to returning at most n items. This starts at the end of +the slice and works backwards. The matched element is not contained in +the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+
let mut s = [10, 40, 30, 20, 60, 50];
+
+for group in s.rsplitn_mut(2, |num| *num % 3 == 0) {
+    group[0] = 1;
+}
+assert_eq!(s, [1, 40, 30, 20, 60, 1]);
Source

pub fn split_once<F>(&self, pred: F) -> Option<(&[T], &[T])>
where + F: FnMut(&T) -> bool,

🔬This is a nightly-only experimental API. (slice_split_once)

Splits the slice on the first element that matches the specified +predicate.

+

If any matching elements are present in the slice, returns the prefix +before the match and suffix after. The matching element itself is not +included. If no elements match, returns None.

+
§Examples
+
#![feature(slice_split_once)]
+let s = [1, 2, 3, 2, 4];
+assert_eq!(s.split_once(|&x| x == 2), Some((
+    &[1][..],
+    &[3, 2, 4][..]
+)));
+assert_eq!(s.split_once(|&x| x == 0), None);
Source

pub fn rsplit_once<F>(&self, pred: F) -> Option<(&[T], &[T])>
where + F: FnMut(&T) -> bool,

🔬This is a nightly-only experimental API. (slice_split_once)

Splits the slice on the last element that matches the specified +predicate.

+

If any matching elements are present in the slice, returns the prefix +before the match and suffix after. The matching element itself is not +included. If no elements match, returns None.

+
§Examples
+
#![feature(slice_split_once)]
+let s = [1, 2, 3, 2, 4];
+assert_eq!(s.rsplit_once(|&x| x == 2), Some((
+    &[1, 2, 3][..],
+    &[4][..]
+)));
+assert_eq!(s.rsplit_once(|&x| x == 0), None);
1.0.0 · Source

pub fn contains(&self, x: &T) -> bool
where + T: PartialEq,

Returns true if the slice contains an element with the given value.

+

This operation is O(n).

+

Note that if you have a sorted slice, binary_search may be faster.

+
§Examples
+
let v = [10, 40, 30];
+assert!(v.contains(&30));
+assert!(!v.contains(&50));
+

If you do not have a &T, but some other value that you can compare +with one (for example, String implements PartialEq<str>), you can +use iter().any:

+ +
let v = [String::from("hello"), String::from("world")]; // slice of `String`
+assert!(v.iter().any(|e| e == "hello")); // search with `&str`
+assert!(!v.iter().any(|e| e == "hi"));
1.0.0 · Source

pub fn starts_with(&self, needle: &[T]) -> bool
where + T: PartialEq,

Returns true if needle is a prefix of the slice or equal to the slice.

+
§Examples
+
let v = [10, 40, 30];
+assert!(v.starts_with(&[10]));
+assert!(v.starts_with(&[10, 40]));
+assert!(v.starts_with(&v));
+assert!(!v.starts_with(&[50]));
+assert!(!v.starts_with(&[10, 50]));
+

Always returns true if needle is an empty slice:

+ +
let v = &[10, 40, 30];
+assert!(v.starts_with(&[]));
+let v: &[u8] = &[];
+assert!(v.starts_with(&[]));
1.0.0 · Source

pub fn ends_with(&self, needle: &[T]) -> bool
where + T: PartialEq,

Returns true if needle is a suffix of the slice or equal to the slice.

+
§Examples
+
let v = [10, 40, 30];
+assert!(v.ends_with(&[30]));
+assert!(v.ends_with(&[40, 30]));
+assert!(v.ends_with(&v));
+assert!(!v.ends_with(&[50]));
+assert!(!v.ends_with(&[50, 30]));
+

Always returns true if needle is an empty slice:

+ +
let v = &[10, 40, 30];
+assert!(v.ends_with(&[]));
+let v: &[u8] = &[];
+assert!(v.ends_with(&[]));
1.51.0 · Source

pub fn strip_prefix<P>(&self, prefix: &P) -> Option<&[T]>
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

Returns a subslice with the prefix removed.

+

If the slice starts with prefix, returns the subslice after the prefix, wrapped in Some. +If prefix is empty, simply returns the original slice. If prefix is equal to the +original slice, returns an empty slice.

+

If the slice does not start with prefix, returns None.

+
§Examples
+
let v = &[10, 40, 30];
+assert_eq!(v.strip_prefix(&[10]), Some(&[40, 30][..]));
+assert_eq!(v.strip_prefix(&[10, 40]), Some(&[30][..]));
+assert_eq!(v.strip_prefix(&[10, 40, 30]), Some(&[][..]));
+assert_eq!(v.strip_prefix(&[50]), None);
+assert_eq!(v.strip_prefix(&[10, 50]), None);
+
+let prefix : &str = "he";
+assert_eq!(b"hello".strip_prefix(prefix.as_bytes()),
+           Some(b"llo".as_ref()));
1.51.0 · Source

pub fn strip_suffix<P>(&self, suffix: &P) -> Option<&[T]>
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

Returns a subslice with the suffix removed.

+

If the slice ends with suffix, returns the subslice before the suffix, wrapped in Some. +If suffix is empty, simply returns the original slice. If suffix is equal to the +original slice, returns an empty slice.

+

If the slice does not end with suffix, returns None.

+
§Examples
+
let v = &[10, 40, 30];
+assert_eq!(v.strip_suffix(&[30]), Some(&[10, 40][..]));
+assert_eq!(v.strip_suffix(&[40, 30]), Some(&[10][..]));
+assert_eq!(v.strip_suffix(&[10, 40, 30]), Some(&[][..]));
+assert_eq!(v.strip_suffix(&[50]), None);
+assert_eq!(v.strip_suffix(&[50, 30]), None);
Source

pub fn trim_prefix<P>(&self, prefix: &P) -> &[T]
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

🔬This is a nightly-only experimental API. (trim_prefix_suffix)

Returns a subslice with the optional prefix removed.

+

If the slice starts with prefix, returns the subslice after the prefix. If prefix +is empty or the slice does not start with prefix, simply returns the original slice. +If prefix is equal to the original slice, returns an empty slice.

+
§Examples
+
#![feature(trim_prefix_suffix)]
+
+let v = &[10, 40, 30];
+
+// Prefix present - removes it
+assert_eq!(v.trim_prefix(&[10]), &[40, 30][..]);
+assert_eq!(v.trim_prefix(&[10, 40]), &[30][..]);
+assert_eq!(v.trim_prefix(&[10, 40, 30]), &[][..]);
+
+// Prefix absent - returns original slice
+assert_eq!(v.trim_prefix(&[50]), &[10, 40, 30][..]);
+assert_eq!(v.trim_prefix(&[10, 50]), &[10, 40, 30][..]);
+
+let prefix : &str = "he";
+assert_eq!(b"hello".trim_prefix(prefix.as_bytes()), b"llo".as_ref());
Source

pub fn trim_suffix<P>(&self, suffix: &P) -> &[T]
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

🔬This is a nightly-only experimental API. (trim_prefix_suffix)

Returns a subslice with the optional suffix removed.

+

If the slice ends with suffix, returns the subslice before the suffix. If suffix +is empty or the slice does not end with suffix, simply returns the original slice. +If suffix is equal to the original slice, returns an empty slice.

+
§Examples
+
#![feature(trim_prefix_suffix)]
+
+let v = &[10, 40, 30];
+
+// Suffix present - removes it
+assert_eq!(v.trim_suffix(&[30]), &[10, 40][..]);
+assert_eq!(v.trim_suffix(&[40, 30]), &[10][..]);
+assert_eq!(v.trim_suffix(&[10, 40, 30]), &[][..]);
+
+// Suffix absent - returns original slice
+assert_eq!(v.trim_suffix(&[50]), &[10, 40, 30][..]);
+assert_eq!(v.trim_suffix(&[50, 30]), &[10, 40, 30][..]);

Binary searches this slice for a given element. +If the slice is not sorted, the returned result is unspecified and +meaningless.

+

If the value is found then Result::Ok is returned, containing the +index of the matching element. If there are multiple matches, then any +one of the matches could be returned. The index is chosen +deterministically, but is subject to change in future versions of Rust. +If the value is not found then Result::Err is returned, containing +the index where a matching element could be inserted while maintaining +sorted order.

+

See also binary_search_by, binary_search_by_key, and partition_point.

+
§Examples
+

Looks up a series of four elements. The first is found, with a +uniquely determined position; the second and third are not +found; the fourth could match any position in [1, 4].

+ +
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+assert_eq!(s.binary_search(&13),  Ok(9));
+assert_eq!(s.binary_search(&4),   Err(7));
+assert_eq!(s.binary_search(&100), Err(13));
+let r = s.binary_search(&1);
+assert!(match r { Ok(1..=4) => true, _ => false, });
+

If you want to find that whole range of matching items, rather than +an arbitrary matching one, that can be done using partition_point:

+ +
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+let low = s.partition_point(|x| x < &1);
+assert_eq!(low, 1);
+let high = s.partition_point(|x| x <= &1);
+assert_eq!(high, 5);
+let r = s.binary_search(&1);
+assert!((low..high).contains(&r.unwrap()));
+
+assert!(s[..low].iter().all(|&x| x < 1));
+assert!(s[low..high].iter().all(|&x| x == 1));
+assert!(s[high..].iter().all(|&x| x > 1));
+
+// For something not found, the "range" of equal items is empty
+assert_eq!(s.partition_point(|x| x < &11), 9);
+assert_eq!(s.partition_point(|x| x <= &11), 9);
+assert_eq!(s.binary_search(&11), Err(9));
+

If you want to insert an item to a sorted vector, while maintaining +sort order, consider using partition_point:

+ +
let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+let num = 42;
+let idx = s.partition_point(|&x| x <= num);
+// If `num` is unique, `s.partition_point(|&x| x < num)` (with `<`) is equivalent to
+// `s.binary_search(&num).unwrap_or_else(|x| x)`, but using `<=` will allow `insert`
+// to shift less elements.
+s.insert(idx, num);
+assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
1.0.0 · Source

pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result<usize, usize>
where + F: FnMut(&'a T) -> Ordering,

Binary searches this slice with a comparator function.

+

The comparator function should return an order code that indicates +whether its argument is Less, Equal or Greater the desired +target. +If the slice is not sorted or if the comparator function does not +implement an order consistent with the sort order of the underlying +slice, the returned result is unspecified and meaningless.

+

If the value is found then Result::Ok is returned, containing the +index of the matching element. If there are multiple matches, then any +one of the matches could be returned. The index is chosen +deterministically, but is subject to change in future versions of Rust. +If the value is not found then Result::Err is returned, containing +the index where a matching element could be inserted while maintaining +sorted order.

+

See also binary_search, binary_search_by_key, and partition_point.

+
§Examples
+

Looks up a series of four elements. The first is found, with a +uniquely determined position; the second and third are not +found; the fourth could match any position in [1, 4].

+ +
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+let seek = 13;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Ok(9));
+let seek = 4;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(7));
+let seek = 100;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(13));
+let seek = 1;
+let r = s.binary_search_by(|probe| probe.cmp(&seek));
+assert!(match r { Ok(1..=4) => true, _ => false, });
1.10.0 · Source

pub fn binary_search_by_key<'a, B, F>( + &'a self, + b: &B, + f: F, +) -> Result<usize, usize>
where + F: FnMut(&'a T) -> B, + B: Ord,

Binary searches this slice with a key extraction function.

+

Assumes that the slice is sorted by the key, for instance with +sort_by_key using the same key extraction function. +If the slice is not sorted by the key, the returned result is +unspecified and meaningless.

+

If the value is found then Result::Ok is returned, containing the +index of the matching element. If there are multiple matches, then any +one of the matches could be returned. The index is chosen +deterministically, but is subject to change in future versions of Rust. +If the value is not found then Result::Err is returned, containing +the index where a matching element could be inserted while maintaining +sorted order.

+

See also binary_search, binary_search_by, and partition_point.

+
§Examples
+

Looks up a series of four elements in a slice of pairs sorted by +their second elements. The first is found, with a uniquely +determined position; the second and third are not found; the +fourth could match any position in [1, 4].

+ +
let s = [(0, 0), (2, 1), (4, 1), (5, 1), (3, 1),
+         (1, 2), (2, 3), (4, 5), (5, 8), (3, 13),
+         (1, 21), (2, 34), (4, 55)];
+
+assert_eq!(s.binary_search_by_key(&13, |&(a, b)| b),  Ok(9));
+assert_eq!(s.binary_search_by_key(&4, |&(a, b)| b),   Err(7));
+assert_eq!(s.binary_search_by_key(&100, |&(a, b)| b), Err(13));
+let r = s.binary_search_by_key(&1, |&(a, b)| b);
+assert!(match r { Ok(1..=4) => true, _ => false, });
1.20.0 · Source

pub fn sort_unstable(&mut self)
where + T: Ord,

Sorts the slice in ascending order without preserving the initial order of equal elements.

+

This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not +allocate), and O(n * log(n)) worst-case.

+

If the implementation of Ord for T does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+

All original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. Same is true if the implementation of Ord for T panics.

+

Sorting types that only implement PartialOrd such as f32 and f64 require +additional precautions. For example, f32::NAN != f32::NAN, which doesn’t fulfill the +reflexivity requirement of Ord. By using an alternative comparison function with +slice::sort_unstable_by such as f32::total_cmp or f64::total_cmp that defines a +total order users can sort slices containing floating-point values. Alternatively, if all +values in the slice are guaranteed to be in a subset for which PartialOrd::partial_cmp +forms a total order, it’s possible to sort the slice with sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()).

+
§Current implementation
+

The current implementation is based on ipnsort by Lukas Bergdoll and Orson Peters, which +combines the fast average case of quicksort with the fast worst case of heapsort, achieving +linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the +expected time to sort the data is O(n * log(k)).

+

It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice is partially sorted.

+
§Panics
+

May panic if the implementation of Ord for T does not implement a total order, or if +the Ord implementation panics.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+
+v.sort_unstable();
+assert_eq!(v, [-5, -3, 1, 2, 4]);
1.20.0 · Source

pub fn sort_unstable_by<F>(&mut self, compare: F)
where + F: FnMut(&T, &T) -> Ordering,

Sorts the slice in ascending order with a comparison function, without preserving the +initial order of equal elements.

+

This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not +allocate), and O(n * log(n)) worst-case.

+

If the comparison function compare does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+

All original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. Same is true if compare panics.

+
§Current implementation
+

The current implementation is based on ipnsort by Lukas Bergdoll and Orson Peters, which +combines the fast average case of quicksort with the fast worst case of heapsort, achieving +linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the +expected time to sort the data is O(n * log(k)).

+

It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice is partially sorted.

+
§Panics
+

May panic if the compare does not implement a total order, or if +the compare itself panics.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+v.sort_unstable_by(|a, b| a.cmp(b));
+assert_eq!(v, [-5, -3, 1, 2, 4]);
+
+// reverse sorting
+v.sort_unstable_by(|a, b| b.cmp(a));
+assert_eq!(v, [4, 2, 1, -3, -5]);
1.20.0 · Source

pub fn sort_unstable_by_key<K, F>(&mut self, f: F)
where + F: FnMut(&T) -> K, + K: Ord,

Sorts the slice in ascending order with a key extraction function, without preserving +the initial order of equal elements.

+

This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not +allocate), and O(n * log(n)) worst-case.

+

If the implementation of Ord for K does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+

All original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. Same is true if the implementation of Ord for K panics.

+
§Current implementation
+

The current implementation is based on ipnsort by Lukas Bergdoll and Orson Peters, which +combines the fast average case of quicksort with the fast worst case of heapsort, achieving +linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the +expected time to sort the data is O(n * log(k)).

+

It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice is partially sorted.

+
§Panics
+

May panic if the implementation of Ord for K does not implement a total order, or if +the Ord implementation panics.

+
§Examples
+
let mut v = [4i32, -5, 1, -3, 2];
+
+v.sort_unstable_by_key(|k| k.abs());
+assert_eq!(v, [1, 2, -3, 4, -5]);
1.49.0 · Source

pub fn select_nth_unstable( + &mut self, + index: usize, +) -> (&mut [T], &mut T, &mut [T])
where + T: Ord,

Reorders the slice such that the element at index is at a sort-order position. All +elements before index will be <= to this value, and all elements after will be >= to +it.

+

This reordering is unstable (i.e. any element that compares equal to the nth element may end +up at that position), in-place (i.e. does not allocate), and runs in O(n) time. This +function is also known as “kth element” in other libraries.

+

Returns a triple that partitions the reordered slice:

+
    +
  • +

    The unsorted subslice before index, whose elements all satisfy x <= self[index].

    +
  • +
  • +

    The element at index.

    +
  • +
  • +

    The unsorted subslice after index, whose elements all satisfy x >= self[index].

    +
  • +
+
§Current implementation
+

The current algorithm is an introselect implementation based on ipnsort by Lukas Bergdoll +and Orson Peters, which is also the basis for sort_unstable. The fallback algorithm is +Median of Medians using Tukey’s Ninther for pivot selection, which guarantees linear runtime +for all inputs.

+
§Panics
+

Panics when index >= len(), and so always panics on empty slices.

+

May panic if the implementation of Ord for T does not implement a total order.

+
§Examples
+
let mut v = [-5i32, 4, 2, -3, 1];
+
+// Find the items `<=` to the median, the median itself, and the items `>=` to it.
+let (lesser, median, greater) = v.select_nth_unstable(2);
+
+assert!(lesser == [-3, -5] || lesser == [-5, -3]);
+assert_eq!(median, &mut 1);
+assert!(greater == [4, 2] || greater == [2, 4]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [-3, -5, 1, 2, 4] ||
+        v == [-5, -3, 1, 2, 4] ||
+        v == [-3, -5, 1, 4, 2] ||
+        v == [-5, -3, 1, 4, 2]);
1.49.0 · Source

pub fn select_nth_unstable_by<F>( + &mut self, + index: usize, + compare: F, +) -> (&mut [T], &mut T, &mut [T])
where + F: FnMut(&T, &T) -> Ordering,

Reorders the slice with a comparator function such that the element at index is at a +sort-order position. All elements before index will be <= to this value, and all +elements after will be >= to it, according to the comparator function.

+

This reordering is unstable (i.e. any element that compares equal to the nth element may end +up at that position), in-place (i.e. does not allocate), and runs in O(n) time. This +function is also known as “kth element” in other libraries.

+

Returns a triple partitioning the reordered slice:

+
    +
  • +

    The unsorted subslice before index, whose elements all satisfy +compare(x, self[index]).is_le().

    +
  • +
  • +

    The element at index.

    +
  • +
  • +

    The unsorted subslice after index, whose elements all satisfy +compare(x, self[index]).is_ge().

    +
  • +
+
§Current implementation
+

The current algorithm is an introselect implementation based on ipnsort by Lukas Bergdoll +and Orson Peters, which is also the basis for sort_unstable. The fallback algorithm is +Median of Medians using Tukey’s Ninther for pivot selection, which guarantees linear runtime +for all inputs.

+
§Panics
+

Panics when index >= len(), and so always panics on empty slices.

+

May panic if compare does not implement a total order.

+
§Examples
+
let mut v = [-5i32, 4, 2, -3, 1];
+
+// Find the items `>=` to the median, the median itself, and the items `<=` to it, by using
+// a reversed comparator.
+let (before, median, after) = v.select_nth_unstable_by(2, |a, b| b.cmp(a));
+
+assert!(before == [4, 2] || before == [2, 4]);
+assert_eq!(median, &mut 1);
+assert!(after == [-3, -5] || after == [-5, -3]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [2, 4, 1, -5, -3] ||
+        v == [2, 4, 1, -3, -5] ||
+        v == [4, 2, 1, -5, -3] ||
+        v == [4, 2, 1, -3, -5]);
1.49.0 · Source

pub fn select_nth_unstable_by_key<K, F>( + &mut self, + index: usize, + f: F, +) -> (&mut [T], &mut T, &mut [T])
where + F: FnMut(&T) -> K, + K: Ord,

Reorders the slice with a key extraction function such that the element at index is at a +sort-order position. All elements before index will have keys <= to the key at index, +and all elements after will have keys >= to it.

+

This reordering is unstable (i.e. any element that compares equal to the nth element may end +up at that position), in-place (i.e. does not allocate), and runs in O(n) time. This +function is also known as “kth element” in other libraries.

+

Returns a triple partitioning the reordered slice:

+
    +
  • +

    The unsorted subslice before index, whose elements all satisfy f(x) <= f(self[index]).

    +
  • +
  • +

    The element at index.

    +
  • +
  • +

    The unsorted subslice after index, whose elements all satisfy f(x) >= f(self[index]).

    +
  • +
+
§Current implementation
+

The current algorithm is an introselect implementation based on ipnsort by Lukas Bergdoll +and Orson Peters, which is also the basis for sort_unstable. The fallback algorithm is +Median of Medians using Tukey’s Ninther for pivot selection, which guarantees linear runtime +for all inputs.

+
§Panics
+

Panics when index >= len(), meaning it always panics on empty slices.

+

May panic if K: Ord does not implement a total order.

+
§Examples
+
let mut v = [-5i32, 4, 1, -3, 2];
+
+// Find the items `<=` to the absolute median, the absolute median itself, and the items
+// `>=` to it.
+let (lesser, median, greater) = v.select_nth_unstable_by_key(2, |a| a.abs());
+
+assert!(lesser == [1, 2] || lesser == [2, 1]);
+assert_eq!(median, &mut -3);
+assert!(greater == [4, -5] || greater == [-5, 4]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [1, 2, -3, 4, -5] ||
+        v == [1, 2, -3, -5, 4] ||
+        v == [2, 1, -3, 4, -5] ||
+        v == [2, 1, -3, -5, 4]);
Source

pub fn partition_dedup(&mut self) -> (&mut [T], &mut [T])
where + T: PartialEq,

🔬This is a nightly-only experimental API. (slice_partition_dedup)

Moves all consecutive repeated elements to the end of the slice according to the +PartialEq trait implementation.

+

Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.

+

If the slice is sorted, the first returned slice contains no duplicates.

+
§Examples
+
#![feature(slice_partition_dedup)]
+
+let mut slice = [1, 2, 2, 3, 3, 2, 1, 1];
+
+let (dedup, duplicates) = slice.partition_dedup();
+
+assert_eq!(dedup, [1, 2, 3, 2, 1]);
+assert_eq!(duplicates, [2, 3, 1]);
Source

pub fn partition_dedup_by<F>(&mut self, same_bucket: F) -> (&mut [T], &mut [T])
where + F: FnMut(&mut T, &mut T) -> bool,

🔬This is a nightly-only experimental API. (slice_partition_dedup)

Moves all but the first of consecutive elements to the end of the slice satisfying +a given equality relation.

+

Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.

+

The same_bucket function is passed references to two elements from the slice and +must determine if the elements compare equal. The elements are passed in opposite order +from their order in the slice, so if same_bucket(a, b) returns true, a is moved +at the end of the slice.

+

If the slice is sorted, the first returned slice contains no duplicates.

+
§Examples
+
#![feature(slice_partition_dedup)]
+
+let mut slice = ["foo", "Foo", "BAZ", "Bar", "bar", "baz", "BAZ"];
+
+let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.eq_ignore_ascii_case(b));
+
+assert_eq!(dedup, ["foo", "BAZ", "Bar", "baz"]);
+assert_eq!(duplicates, ["bar", "Foo", "BAZ"]);
Source

pub fn partition_dedup_by_key<K, F>(&mut self, key: F) -> (&mut [T], &mut [T])
where + F: FnMut(&mut T) -> K, + K: PartialEq,

🔬This is a nightly-only experimental API. (slice_partition_dedup)

Moves all but the first of consecutive elements to the end of the slice that resolve +to the same key.

+

Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.

+

If the slice is sorted, the first returned slice contains no duplicates.

+
§Examples
+
#![feature(slice_partition_dedup)]
+
+let mut slice = [10, 20, 21, 30, 30, 20, 11, 13];
+
+let (dedup, duplicates) = slice.partition_dedup_by_key(|i| *i / 10);
+
+assert_eq!(dedup, [10, 20, 30, 20, 11]);
+assert_eq!(duplicates, [21, 30, 13]);
1.26.0 · Source

pub fn rotate_left(&mut self, mid: usize)

Rotates the slice in-place such that the first mid elements of the +slice move to the end while the last self.len() - mid elements move to +the front.

+

After calling rotate_left, the element previously at index mid will +become the first element in the slice.

+
§Panics
+

This function will panic if mid is greater than the length of the +slice. Note that mid == self.len() does not panic and is a no-op +rotation.

+
§Complexity
+

Takes linear (in self.len()) time.

+
§Examples
+
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a.rotate_left(2);
+assert_eq!(a, ['c', 'd', 'e', 'f', 'a', 'b']);
+

Rotating a subslice:

+ +
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a[1..5].rotate_left(1);
+assert_eq!(a, ['a', 'c', 'd', 'e', 'b', 'f']);
1.26.0 · Source

pub fn rotate_right(&mut self, k: usize)

Rotates the slice in-place such that the first self.len() - k +elements of the slice move to the end while the last k elements move +to the front.

+

After calling rotate_right, the element previously at index +self.len() - k will become the first element in the slice.

+
§Panics
+

This function will panic if k is greater than the length of the +slice. Note that k == self.len() does not panic and is a no-op +rotation.

+
§Complexity
+

Takes linear (in self.len()) time.

+
§Examples
+
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a.rotate_right(2);
+assert_eq!(a, ['e', 'f', 'a', 'b', 'c', 'd']);
+

Rotating a subslice:

+ +
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a[1..5].rotate_right(1);
+assert_eq!(a, ['a', 'e', 'b', 'c', 'd', 'f']);
1.50.0 · Source

pub fn fill(&mut self, value: T)
where + T: Clone,

Fills self with elements by cloning value.

+
§Examples
+
let mut buf = vec![0; 10];
+buf.fill(1);
+assert_eq!(buf, vec![1; 10]);
1.51.0 · Source

pub fn fill_with<F>(&mut self, f: F)
where + F: FnMut() -> T,

Fills self with elements returned by calling a closure repeatedly.

+

This method uses a closure to create new values. If you’d rather +Clone a given value, use fill. If you want to use the Default +trait to generate values, you can pass Default::default as the +argument.

+
§Examples
+
let mut buf = vec![1; 10];
+buf.fill_with(Default::default);
+assert_eq!(buf, vec![0; 10]);
1.7.0 · Source

pub fn clone_from_slice(&mut self, src: &[T])
where + T: Clone,

Copies the elements from src into self.

+

The length of src must be the same as self.

+
§Panics
+

This function will panic if the two slices have different lengths.

+
§Examples
+

Cloning two elements from a slice into another:

+ +
let src = [1, 2, 3, 4];
+let mut dst = [0, 0];
+
+// Because the slices have to be the same length,
+// we slice the source slice from four elements
+// to two. It will panic if we don't do this.
+dst.clone_from_slice(&src[2..]);
+
+assert_eq!(src, [1, 2, 3, 4]);
+assert_eq!(dst, [3, 4]);
+

Rust enforces that there can only be one mutable reference with no +immutable references to a particular piece of data in a particular +scope. Because of this, attempting to use clone_from_slice on a +single slice will result in a compile failure:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+slice[..2].clone_from_slice(&slice[3..]); // compile fail!
+

To work around this, we can use split_at_mut to create two distinct +sub-slices from a slice:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+{
+    let (left, right) = slice.split_at_mut(2);
+    left.clone_from_slice(&right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 4, 5]);
1.9.0 · Source

pub fn copy_from_slice(&mut self, src: &[T])
where + T: Copy,

Copies all elements from src into self, using a memcpy.

+

The length of src must be the same as self.

+

If T does not implement Copy, use clone_from_slice.

+
§Panics
+

This function will panic if the two slices have different lengths.

+
§Examples
+

Copying two elements from a slice into another:

+ +
let src = [1, 2, 3, 4];
+let mut dst = [0, 0];
+
+// Because the slices have to be the same length,
+// we slice the source slice from four elements
+// to two. It will panic if we don't do this.
+dst.copy_from_slice(&src[2..]);
+
+assert_eq!(src, [1, 2, 3, 4]);
+assert_eq!(dst, [3, 4]);
+

Rust enforces that there can only be one mutable reference with no +immutable references to a particular piece of data in a particular +scope. Because of this, attempting to use copy_from_slice on a +single slice will result in a compile failure:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+slice[..2].copy_from_slice(&slice[3..]); // compile fail!
+

To work around this, we can use split_at_mut to create two distinct +sub-slices from a slice:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+{
+    let (left, right) = slice.split_at_mut(2);
+    left.copy_from_slice(&right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 4, 5]);
1.37.0 · Source

pub fn copy_within<R>(&mut self, src: R, dest: usize)
where + R: RangeBounds<usize>, + T: Copy,

Copies elements from one part of the slice to another part of itself, +using a memmove.

+

src is the range within self to copy from. dest is the starting +index of the range within self to copy to, which will have the same +length as src. The two ranges may overlap. The ends of the two ranges +must be less than or equal to self.len().

+
§Panics
+

This function will panic if either range exceeds the end of the slice, +or if the end of src is before the start.

+
§Examples
+

Copying four bytes within a slice:

+ +
let mut bytes = *b"Hello, World!";
+
+bytes.copy_within(1..5, 8);
+
+assert_eq!(&bytes, b"Hello, Wello!");
1.27.0 · Source

pub fn swap_with_slice(&mut self, other: &mut [T])

Swaps all elements in self with those in other.

+

The length of other must be the same as self.

+
§Panics
+

This function will panic if the two slices have different lengths.

+
§Example
+

Swapping two elements across slices:

+ +
let mut slice1 = [0, 0];
+let mut slice2 = [1, 2, 3, 4];
+
+slice1.swap_with_slice(&mut slice2[2..]);
+
+assert_eq!(slice1, [3, 4]);
+assert_eq!(slice2, [1, 2, 0, 0]);
+

Rust enforces that there can only be one mutable reference to a +particular piece of data in a particular scope. Because of this, +attempting to use swap_with_slice on a single slice will result in +a compile failure:

+ +
let mut slice = [1, 2, 3, 4, 5];
+slice[..2].swap_with_slice(&mut slice[3..]); // compile fail!
+

To work around this, we can use split_at_mut to create two distinct +mutable sub-slices from a slice:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+{
+    let (left, right) = slice.split_at_mut(2);
+    left.swap_with_slice(&mut right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 1, 2]);
1.30.0 · Source

pub unsafe fn align_to<U>(&self) -> (&[T], &[U], &[T])

Transmutes the slice to a slice of another type, ensuring alignment of the types is +maintained.

+

This method splits the slice into three distinct slices: prefix, correctly aligned middle +slice of a new type, and the suffix slice. The middle part will be as big as possible under +the given alignment constraint and element size.

+

This method has no purpose when either input element T or output element U are +zero-sized and will return the original slice without splitting anything.

+
§Safety
+

This method is essentially a transmute with respect to the elements in the returned +middle slice, so all the usual caveats pertaining to transmute::<T, U> also apply here.

+
§Examples
+

Basic usage:

+ +
unsafe {
+    let bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
+    let (prefix, shorts, suffix) = bytes.align_to::<u16>();
+    // less_efficient_algorithm_for_bytes(prefix);
+    // more_efficient_algorithm_for_aligned_shorts(shorts);
+    // less_efficient_algorithm_for_bytes(suffix);
+}
1.30.0 · Source

pub unsafe fn align_to_mut<U>(&mut self) -> (&mut [T], &mut [U], &mut [T])

Transmutes the mutable slice to a mutable slice of another type, ensuring alignment of the +types is maintained.

+

This method splits the slice into three distinct slices: prefix, correctly aligned middle +slice of a new type, and the suffix slice. The middle part will be as big as possible under +the given alignment constraint and element size.

+

This method has no purpose when either input element T or output element U are +zero-sized and will return the original slice without splitting anything.

+
§Safety
+

This method is essentially a transmute with respect to the elements in the returned +middle slice, so all the usual caveats pertaining to transmute::<T, U> also apply here.

+
§Examples
+

Basic usage:

+ +
unsafe {
+    let mut bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
+    let (prefix, shorts, suffix) = bytes.align_to_mut::<u16>();
+    // less_efficient_algorithm_for_bytes(prefix);
+    // more_efficient_algorithm_for_aligned_shorts(shorts);
+    // less_efficient_algorithm_for_bytes(suffix);
+}
Source

pub fn as_simd<const LANES: usize>(&self) -> (&[T], &[Simd<T, LANES>], &[T])
where + Simd<T, LANES>: AsRef<[T; LANES]>, + T: SimdElement, + LaneCount<LANES>: SupportedLaneCount,

🔬This is a nightly-only experimental API. (portable_simd)

Splits a slice into a prefix, a middle of aligned SIMD types, and a suffix.

+

This is a safe wrapper around slice::align_to, so inherits the same +guarantees as that method.

+
§Panics
+

This will panic if the size of the SIMD type is different from +LANES times that of the scalar.

+

At the time of writing, the trait restrictions on Simd<T, LANES> keeps +that from ever happening, as only power-of-two numbers of lanes are +supported. It’s possible that, in the future, those restrictions might +be lifted in a way that would make it possible to see panics from this +method for something like LANES == 3.

+
§Examples
+
#![feature(portable_simd)]
+use core::simd::prelude::*;
+
+let short = &[1, 2, 3];
+let (prefix, middle, suffix) = short.as_simd::<4>();
+assert_eq!(middle, []); // Not enough elements for anything in the middle
+
+// They might be split in any possible way between prefix and suffix
+let it = prefix.iter().chain(suffix).copied();
+assert_eq!(it.collect::<Vec<_>>(), vec![1, 2, 3]);
+
+fn basic_simd_sum(x: &[f32]) -> f32 {
+    use std::ops::Add;
+    let (prefix, middle, suffix) = x.as_simd();
+    let sums = f32x4::from_array([
+        prefix.iter().copied().sum(),
+        0.0,
+        0.0,
+        suffix.iter().copied().sum(),
+    ]);
+    let sums = middle.iter().copied().fold(sums, f32x4::add);
+    sums.reduce_sum()
+}
+
+let numbers: Vec<f32> = (1..101).map(|x| x as _).collect();
+assert_eq!(basic_simd_sum(&numbers[1..99]), 4949.0);
Source

pub fn as_simd_mut<const LANES: usize>( + &mut self, +) -> (&mut [T], &mut [Simd<T, LANES>], &mut [T])
where + Simd<T, LANES>: AsMut<[T; LANES]>, + T: SimdElement, + LaneCount<LANES>: SupportedLaneCount,

🔬This is a nightly-only experimental API. (portable_simd)

Splits a mutable slice into a mutable prefix, a middle of aligned SIMD types, +and a mutable suffix.

+

This is a safe wrapper around slice::align_to_mut, so inherits the same +guarantees as that method.

+

This is the mutable version of slice::as_simd; see that for examples.

+
§Panics
+

This will panic if the size of the SIMD type is different from +LANES times that of the scalar.

+

At the time of writing, the trait restrictions on Simd<T, LANES> keeps +that from ever happening, as only power-of-two numbers of lanes are +supported. It’s possible that, in the future, those restrictions might +be lifted in a way that would make it possible to see panics from this +method for something like LANES == 3.

+
1.82.0 · Source

pub fn is_sorted(&self) -> bool
where + T: PartialOrd,

Checks if the elements of this slice are sorted.

+

That is, for each element a and its following element b, a <= b must hold. If the +slice yields exactly zero or one element, true is returned.

+

Note that if Self::Item is only PartialOrd, but not Ord, the above definition +implies that this function returns false if any two consecutive items are not +comparable.

+
§Examples
+
let empty: [i32; 0] = [];
+
+assert!([1, 2, 2, 9].is_sorted());
+assert!(![1, 3, 2, 4].is_sorted());
+assert!([0].is_sorted());
+assert!(empty.is_sorted());
+assert!(![0.0, 1.0, f32::NAN].is_sorted());
1.82.0 · Source

pub fn is_sorted_by<'a, F>(&'a self, compare: F) -> bool
where + F: FnMut(&'a T, &'a T) -> bool,

Checks if the elements of this slice are sorted using the given comparator function.

+

Instead of using PartialOrd::partial_cmp, this function uses the given compare +function to determine whether two elements are to be considered in sorted order.

+
§Examples
+
assert!([1, 2, 2, 9].is_sorted_by(|a, b| a <= b));
+assert!(![1, 2, 2, 9].is_sorted_by(|a, b| a < b));
+
+assert!([0].is_sorted_by(|a, b| true));
+assert!([0].is_sorted_by(|a, b| false));
+
+let empty: [i32; 0] = [];
+assert!(empty.is_sorted_by(|a, b| false));
+assert!(empty.is_sorted_by(|a, b| true));
1.82.0 · Source

pub fn is_sorted_by_key<'a, F, K>(&'a self, f: F) -> bool
where + F: FnMut(&'a T) -> K, + K: PartialOrd,

Checks if the elements of this slice are sorted using the given key extraction function.

+

Instead of comparing the slice’s elements directly, this function compares the keys of the +elements, as determined by f. Apart from that, it’s equivalent to is_sorted; see its +documentation for more information.

+
§Examples
+
assert!(["c", "bb", "aaa"].is_sorted_by_key(|s| s.len()));
+assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs()));
1.52.0 · Source

pub fn partition_point<P>(&self, pred: P) -> usize
where + P: FnMut(&T) -> bool,

Returns the index of the partition point according to the given predicate +(the index of the first element of the second partition).

+

The slice is assumed to be partitioned according to the given predicate. +This means that all elements for which the predicate returns true are at the start of the slice +and all elements for which the predicate returns false are at the end. +For example, [7, 15, 3, 5, 4, 12, 6] is partitioned under the predicate x % 2 != 0 +(all odd numbers are at the start, all even at the end).

+

If this slice is not partitioned, the returned result is unspecified and meaningless, +as this method performs a kind of binary search.

+

See also binary_search, binary_search_by, and binary_search_by_key.

+
§Examples
+
let v = [1, 2, 3, 3, 5, 6, 7];
+let i = v.partition_point(|&x| x < 5);
+
+assert_eq!(i, 4);
+assert!(v[..i].iter().all(|&x| x < 5));
+assert!(v[i..].iter().all(|&x| !(x < 5)));
+

If all elements of the slice match the predicate, including if the slice +is empty, then the length of the slice will be returned:

+ +
let a = [2, 4, 8];
+assert_eq!(a.partition_point(|x| x < &100), a.len());
+let a: [i32; 0] = [];
+assert_eq!(a.partition_point(|x| x < &100), 0);
+

If you want to insert an item to a sorted vector, while maintaining +sort order:

+ +
let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+let num = 42;
+let idx = s.partition_point(|&x| x <= num);
+s.insert(idx, num);
+assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
1.87.0 · Source

pub fn split_off<'a, R>(self: &mut &'a [T], range: R) -> Option<&'a [T]>
where + R: OneSidedRange<usize>,

Removes the subslice corresponding to the given range +and returns a reference to it.

+

Returns None and does not modify the slice if the given +range is out of bounds.

+

Note that this method only accepts one-sided ranges such as +2.. or ..6, but not 2..6.

+
§Examples
+

Splitting off the first three elements of a slice:

+ +
let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+let mut first_three = slice.split_off(..3).unwrap();
+
+assert_eq!(slice, &['d']);
+assert_eq!(first_three, &['a', 'b', 'c']);
+

Splitting off a slice starting with the third element:

+ +
let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+let mut tail = slice.split_off(2..).unwrap();
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(tail, &['c', 'd']);
+

Getting None when range is out of bounds:

+ +
let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+
+assert_eq!(None, slice.split_off(5..));
+assert_eq!(None, slice.split_off(..5));
+assert_eq!(None, slice.split_off(..=4));
+let expected: &[char] = &['a', 'b', 'c', 'd'];
+assert_eq!(Some(expected), slice.split_off(..4));
1.87.0 · Source

pub fn split_off_mut<'a, R>( + self: &mut &'a mut [T], + range: R, +) -> Option<&'a mut [T]>
where + R: OneSidedRange<usize>,

Removes the subslice corresponding to the given range +and returns a mutable reference to it.

+

Returns None and does not modify the slice if the given +range is out of bounds.

+

Note that this method only accepts one-sided ranges such as +2.. or ..6, but not 2..6.

+
§Examples
+

Splitting off the first three elements of a slice:

+ +
let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+let mut first_three = slice.split_off_mut(..3).unwrap();
+
+assert_eq!(slice, &mut ['d']);
+assert_eq!(first_three, &mut ['a', 'b', 'c']);
+

Splitting off a slice starting with the third element:

+ +
let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+let mut tail = slice.split_off_mut(2..).unwrap();
+
+assert_eq!(slice, &mut ['a', 'b']);
+assert_eq!(tail, &mut ['c', 'd']);
+

Getting None when range is out of bounds:

+ +
let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+
+assert_eq!(None, slice.split_off_mut(5..));
+assert_eq!(None, slice.split_off_mut(..5));
+assert_eq!(None, slice.split_off_mut(..=4));
+let expected: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+assert_eq!(Some(expected), slice.split_off_mut(..4));
1.87.0 · Source

pub fn split_off_first<'a>(self: &mut &'a [T]) -> Option<&'a T>

Removes the first element of the slice and returns a reference +to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &[_] = &['a', 'b', 'c'];
+let first = slice.split_off_first().unwrap();
+
+assert_eq!(slice, &['b', 'c']);
+assert_eq!(first, &'a');
1.87.0 · Source

pub fn split_off_first_mut<'a>(self: &mut &'a mut [T]) -> Option<&'a mut T>

Removes the first element of the slice and returns a mutable +reference to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
+let first = slice.split_off_first_mut().unwrap();
+*first = 'd';
+
+assert_eq!(slice, &['b', 'c']);
+assert_eq!(first, &'d');
1.87.0 · Source

pub fn split_off_last<'a>(self: &mut &'a [T]) -> Option<&'a T>

Removes the last element of the slice and returns a reference +to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &[_] = &['a', 'b', 'c'];
+let last = slice.split_off_last().unwrap();
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(last, &'c');
1.87.0 · Source

pub fn split_off_last_mut<'a>(self: &mut &'a mut [T]) -> Option<&'a mut T>

Removes the last element of the slice and returns a mutable +reference to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
+let last = slice.split_off_last_mut().unwrap();
+*last = 'd';
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(last, &'d');
1.86.0 · Source

pub unsafe fn get_disjoint_unchecked_mut<I, const N: usize>( + &mut self, + indices: [I; N], +) -> [&mut <I as SliceIndex<[T]>>::Output; N]

Returns mutable references to many indices at once, without doing any checks.

+

An index can be either a usize, a Range or a RangeInclusive. Note +that this method takes an array, so all indices must be of the same type. +If passed an array of usizes this method gives back an array of mutable references +to single elements, while if passed an array of ranges it gives back an array of +mutable references to slices.

+

For a safe alternative see get_disjoint_mut.

+
§Safety
+

Calling this method with overlapping or out-of-bounds indices is undefined behavior +even if the resulting references are not used.

+
§Examples
+
let x = &mut [1, 2, 4];
+
+unsafe {
+    let [a, b] = x.get_disjoint_unchecked_mut([0, 2]);
+    *a *= 10;
+    *b *= 100;
+}
+assert_eq!(x, &[10, 2, 400]);
+
+unsafe {
+    let [a, b] = x.get_disjoint_unchecked_mut([0..1, 1..3]);
+    a[0] = 8;
+    b[0] = 88;
+    b[1] = 888;
+}
+assert_eq!(x, &[8, 88, 888]);
+
+unsafe {
+    let [a, b] = x.get_disjoint_unchecked_mut([1..=2, 0..=0]);
+    a[0] = 11;
+    a[1] = 111;
+    b[0] = 1;
+}
+assert_eq!(x, &[1, 11, 111]);
1.86.0 · Source

pub fn get_disjoint_mut<I, const N: usize>( + &mut self, + indices: [I; N], +) -> Result<[&mut <I as SliceIndex<[T]>>::Output; N], GetDisjointMutError>

Returns mutable references to many indices at once.

+

An index can be either a usize, a Range or a RangeInclusive. Note +that this method takes an array, so all indices must be of the same type. +If passed an array of usizes this method gives back an array of mutable references +to single elements, while if passed an array of ranges it gives back an array of +mutable references to slices.

+

Returns an error if any index is out-of-bounds, or if there are overlapping indices. +An empty range is not considered to overlap if it is located at the beginning or at +the end of another range, but is considered to overlap if it is located in the middle.

+

This method does a O(n^2) check to check that there are no overlapping indices, so be careful +when passing many indices.

+
§Examples
+
let v = &mut [1, 2, 3];
+if let Ok([a, b]) = v.get_disjoint_mut([0, 2]) {
+    *a = 413;
+    *b = 612;
+}
+assert_eq!(v, &[413, 2, 612]);
+
+if let Ok([a, b]) = v.get_disjoint_mut([0..1, 1..3]) {
+    a[0] = 8;
+    b[0] = 88;
+    b[1] = 888;
+}
+assert_eq!(v, &[8, 88, 888]);
+
+if let Ok([a, b]) = v.get_disjoint_mut([1..=2, 0..=0]) {
+    a[0] = 11;
+    a[1] = 111;
+    b[0] = 1;
+}
+assert_eq!(v, &[1, 11, 111]);
Source

pub fn element_offset(&self, element: &T) -> Option<usize>

🔬This is a nightly-only experimental API. (substr_range)

Returns the index that an element reference points to.

+

Returns None if element does not point to the start of an element within the slice.

+

This method is useful for extending slice iterators like slice::split.

+

Note that this uses pointer arithmetic and does not compare elements. +To find the index of an element via comparison, use +.iter().position() instead.

+
§Panics
+

Panics if T is zero-sized.

+
§Examples
+

Basic usage:

+ +
#![feature(substr_range)]
+
+let nums: &[u32] = &[1, 7, 1, 1];
+let num = &nums[2];
+
+assert_eq!(num, &1);
+assert_eq!(nums.element_offset(num), Some(2));
+

Returning None with an unaligned element:

+ +
#![feature(substr_range)]
+
+let arr: &[[u32; 2]] = &[[0, 1], [2, 3]];
+let flat_arr: &[u32] = arr.as_flattened();
+
+let ok_elm: &[u32; 2] = flat_arr[0..2].try_into().unwrap();
+let weird_elm: &[u32; 2] = flat_arr[1..3].try_into().unwrap();
+
+assert_eq!(ok_elm, &[0, 1]);
+assert_eq!(weird_elm, &[1, 2]);
+
+assert_eq!(arr.element_offset(ok_elm), Some(0)); // Points to element 0
+assert_eq!(arr.element_offset(weird_elm), None); // Points between element 0 and 1
Source

pub fn subslice_range(&self, subslice: &[T]) -> Option<Range<usize>>

🔬This is a nightly-only experimental API. (substr_range)

Returns the range of indices that a subslice points to.

+

Returns None if subslice does not point within the slice or if it is not aligned with the +elements in the slice.

+

This method does not compare elements. Instead, this method finds the location in the slice that +subslice was obtained from. To find the index of a subslice via comparison, instead use +.windows().position().

+

This method is useful for extending slice iterators like slice::split.

+

Note that this may return a false positive (either Some(0..0) or Some(self.len()..self.len())) +if subslice has a length of zero and points to the beginning or end of another, separate, slice.

+
§Panics
+

Panics if T is zero-sized.

+
§Examples
+

Basic usage:

+ +
#![feature(substr_range)]
+
+let nums = &[0, 5, 10, 0, 0, 5];
+
+let mut iter = nums
+    .split(|t| *t == 0)
+    .map(|n| nums.subslice_range(n).unwrap());
+
+assert_eq!(iter.next(), Some(0..0));
+assert_eq!(iter.next(), Some(1..3));
+assert_eq!(iter.next(), Some(4..4));
+assert_eq!(iter.next(), Some(5..6));
1.79.0 · Source

pub fn utf8_chunks(&self) -> Utf8Chunks<'_>

Creates an iterator over the contiguous valid UTF-8 ranges of this +slice, and the non-UTF-8 fragments in between.

+

See the Utf8Chunk type for documentation of the items yielded by this iterator.

+
§Examples
+

This function formats arbitrary but mostly-UTF-8 bytes into Rust source +code in the form of a C-string literal (c"...").

+ +
use std::fmt::Write as _;
+
+pub fn cstr_literal(bytes: &[u8]) -> String {
+    let mut repr = String::new();
+    repr.push_str("c\"");
+    for chunk in bytes.utf8_chunks() {
+        for ch in chunk.valid().chars() {
+            // Escapes \0, \t, \r, \n, \\, \', \", and uses \u{...} for non-printable characters.
+            write!(repr, "{}", ch.escape_debug()).unwrap();
+        }
+        for byte in chunk.invalid() {
+            write!(repr, "\\x{:02X}", byte).unwrap();
+        }
+    }
+    repr.push('"');
+    repr
+}
+
+fn main() {
+    let lit = cstr_literal(b"\xferris the \xf0\x9f\xa6\x80\x07");
+    let expected = stringify!(c"\xFErris the 🦀\u{7}");
+    assert_eq!(lit, expected);
+}
1.0.0 · Source

pub fn sort(&mut self)
where + T: Ord,

Sorts the slice in ascending order, preserving initial order of equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(n * log(n)) +worst-case.

+

If the implementation of Ord for T does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

When applicable, unstable sorting is preferred because it is generally faster than stable +sorting and it doesn’t allocate auxiliary memory. See +sort_unstable. The exception are partially sorted slices, which +may be better served with slice::sort.

+

Sorting types that only implement PartialOrd such as f32 and f64 require +additional precautions. For example, f32::NAN != f32::NAN, which doesn’t fulfill the +reflexivity requirement of Ord. By using an alternative comparison function with +slice::sort_by such as f32::total_cmp or f64::total_cmp that defines a total +order users can sort slices containing floating-point values. Alternatively, if all values +in the slice are guaranteed to be in a subset for which PartialOrd::partial_cmp forms a +total order, it’s possible to sort the slice with sort_by(|a, b| a.partial_cmp(b).unwrap()).

+
§Current implementation
+

The current implementation is based on driftsort by Orson Peters and Lukas Bergdoll, which +combines the fast average case of quicksort with the fast worst case and partial run +detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs +with k distinct elements, the expected time to sort the data is O(n * log(k)).

+

The auxiliary memory allocation behavior depends on the input length. Short slices are +handled without allocation, medium sized slices allocate self.len() and beyond that it +clamps at self.len() / 2.

+
§Panics
+

May panic if the implementation of Ord for T does not implement a total order, or if +the Ord implementation itself panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+
+v.sort();
+assert_eq!(v, [-5, -3, 1, 2, 4]);
1.0.0 · Source

pub fn sort_by<F>(&mut self, compare: F)
where + F: FnMut(&T, &T) -> Ordering,

Sorts the slice in ascending order with a comparison function, preserving initial order of +equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(n * log(n)) +worst-case.

+

If the comparison function compare does not implement a total order, the function may +panic; even if the function exits normally, the resulting order of elements in the slice is +unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+
§Current implementation
+

The current implementation is based on driftsort by Orson Peters and Lukas Bergdoll, which +combines the fast average case of quicksort with the fast worst case and partial run +detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs +with k distinct elements, the expected time to sort the data is O(n * log(k)).

+

The auxiliary memory allocation behavior depends on the input length. Short slices are +handled without allocation, medium sized slices allocate self.len() and beyond that it +clamps at self.len() / 2.

+
§Panics
+

May panic if compare does not implement a total order, or if compare itself panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+v.sort_by(|a, b| a.cmp(b));
+assert_eq!(v, [-5, -3, 1, 2, 4]);
+
+// reverse sorting
+v.sort_by(|a, b| b.cmp(a));
+assert_eq!(v, [4, 2, 1, -3, -5]);
1.7.0 · Source

pub fn sort_by_key<K, F>(&mut self, f: F)
where + F: FnMut(&T) -> K, + K: Ord,

Sorts the slice in ascending order with a key extraction function, preserving initial order +of equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(m * n * log(n)) +worst-case, where the key function is O(m).

+

If the implementation of Ord for K does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+
§Current implementation
+

The current implementation is based on driftsort by Orson Peters and Lukas Bergdoll, which +combines the fast average case of quicksort with the fast worst case and partial run +detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs +with k distinct elements, the expected time to sort the data is O(n * log(k)).

+

The auxiliary memory allocation behavior depends on the input length. Short slices are +handled without allocation, medium sized slices allocate self.len() and beyond that it +clamps at self.len() / 2.

+
§Panics
+

May panic if the implementation of Ord for K does not implement a total order, or if +the Ord implementation or the key-function f panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4i32, -5, 1, -3, 2];
+
+v.sort_by_key(|k| k.abs());
+assert_eq!(v, [1, 2, -3, 4, -5]);
1.34.0 · Source

pub fn sort_by_cached_key<K, F>(&mut self, f: F)
where + F: FnMut(&T) -> K, + K: Ord,

Sorts the slice in ascending order with a key extraction function, preserving initial order +of equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(m * n + n * +log(n)) worst-case, where the key function is O(m).

+

During sorting, the key function is called at most once per element, by using temporary +storage to remember the results of key evaluation. The order of calls to the key function is +unspecified and may change in future versions of the standard library.

+

If the implementation of Ord for K does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For simple key functions (e.g., functions that are property accesses or basic operations), +sort_by_key is likely to be faster.

+
§Current implementation
+

The current implementation is based on instruction-parallel-network sort by Lukas +Bergdoll, which combines the fast average case of randomized quicksort with the fast worst +case of heapsort, while achieving linear time on fully sorted and reversed inputs. And +O(k * log(n)) where k is the number of distinct elements in the input. It leverages +superscalar out-of-order execution capabilities commonly found in CPUs, to efficiently +perform the operation.

+

In the worst case, the algorithm allocates temporary storage in a Vec<(K, usize)> the +length of the slice.

+
§Panics
+

May panic if the implementation of Ord for K does not implement a total order, or if +the Ord implementation panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4i32, -5, 1, -3, 2, 10];
+
+// Strings are sorted by lexicographical order.
+v.sort_by_cached_key(|k| k.to_string());
+assert_eq!(v, [-3, -5, 1, 10, 2, 4]);
1.0.0 · Source

pub fn to_vec(&self) -> Vec<T>
where + T: Clone,

Copies self into a new Vec.

+
§Examples
+
let s = [10, 40, 30];
+let x = s.to_vec();
+// Here, `s` and `x` can be modified independently.
Source

pub fn to_vec_in<A>(&self, alloc: A) -> Vec<T, A>
where + A: Allocator, + T: Clone,

🔬This is a nightly-only experimental API. (allocator_api)

Copies self into a new Vec with an allocator.

+
§Examples
+
#![feature(allocator_api)]
+
+use std::alloc::System;
+
+let s = [10, 40, 30];
+let x = s.to_vec_in(System);
+// Here, `s` and `x` can be modified independently.
1.40.0 · Source

pub fn repeat(&self, n: usize) -> Vec<T>
where + T: Copy,

Creates a vector by copying a slice n times.

+
§Panics
+

This function will panic if the capacity would overflow.

+
§Examples
+
assert_eq!([1, 2].repeat(3), vec![1, 2, 1, 2, 1, 2]);
+

A panic upon overflow:

+ +
// this will panic at runtime
+b"0123456789abcdef".repeat(usize::MAX);
1.0.0 · Source

pub fn concat<Item>(&self) -> <[T] as Concat<Item>>::Output
where + [T]: Concat<Item>, + Item: ?Sized,

Flattens a slice of T into a single value Self::Output.

+
§Examples
+
assert_eq!(["hello", "world"].concat(), "helloworld");
+assert_eq!([[1, 2], [3, 4]].concat(), [1, 2, 3, 4]);
1.3.0 · Source

pub fn join<Separator>( + &self, + sep: Separator, +) -> <[T] as Join<Separator>>::Output
where + [T]: Join<Separator>,

Flattens a slice of T into a single value Self::Output, placing a +given separator between each.

+
§Examples
+
assert_eq!(["hello", "world"].join(" "), "hello world");
+assert_eq!([[1, 2], [3, 4]].join(&0), [1, 2, 0, 3, 4]);
+assert_eq!([[1, 2], [3, 4]].join(&[0, 0][..]), [1, 2, 0, 0, 3, 4]);
1.0.0 · Source

pub fn connect<Separator>( + &self, + sep: Separator, +) -> <[T] as Join<Separator>>::Output
where + [T]: Join<Separator>,

👎Deprecated since 1.3.0: renamed to join

Flattens a slice of T into a single value Self::Output, placing a +given separator between each.

+
§Examples
+
assert_eq!(["hello", "world"].connect(" "), "hello world");
+assert_eq!([[1, 2], [3, 4]].connect(&0), [1, 2, 0, 3, 4]);
1.23.0 · Source

pub fn to_ascii_uppercase(&self) -> Vec<u8>

Returns a vector containing a copy of this slice where each byte +is mapped to its ASCII upper case equivalent.

+

ASCII letters ‘a’ to ‘z’ are mapped to ‘A’ to ‘Z’, +but non-ASCII letters are unchanged.

+

To uppercase the value in-place, use make_ascii_uppercase.

+
1.23.0 · Source

pub fn to_ascii_lowercase(&self) -> Vec<u8>

Returns a vector containing a copy of this slice where each byte +is mapped to its ASCII lower case equivalent.

+

ASCII letters ‘A’ to ‘Z’ are mapped to ‘a’ to ‘z’, +but non-ASCII letters are unchanged.

+

To lowercase the value in-place, use make_ascii_lowercase.

+

Trait Implementations§

Source§

impl Clone for ConnectionId

Source§

fn clone(&self) -> ConnectionId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Deref for ConnectionId

Source§

type Target = [u8]

The resulting type after dereferencing.
Source§

fn deref(&self) -> &[u8]

Dereferences the value.
Source§

impl DerefMut for ConnectionId

Source§

fn deref_mut(&mut self) -> &mut [u8]

Mutably dereferences the value.
Source§

impl Display for ConnectionId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Hash for ConnectionId

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for ConnectionId

Source§

fn cmp(&self, other: &ConnectionId) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for ConnectionId

Source§

fn eq(&self, other: &ConnectionId) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for ConnectionId

Source§

fn partial_cmp(&self, other: &ConnectionId) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for ConnectionId

Source§

impl Eq for ConnectionId

Source§

impl StructuralPartialEq for ConnectionId

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<P, T> Receiver for P
where + P: Deref<Target = T> + ?Sized, + T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.ConnectionStats.html b/pr/395/docs/iroh_quinn_proto/struct.ConnectionStats.html new file mode 100644 index 000000000..ae775d8c9 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.ConnectionStats.html @@ -0,0 +1,36 @@ +ConnectionStats in iroh_quinn_proto - Rust

ConnectionStats

Struct ConnectionStats 

Source
#[non_exhaustive]
pub struct ConnectionStats { + pub udp_tx: UdpStats, + pub udp_rx: UdpStats, + pub frame_tx: FrameStats, + pub frame_rx: FrameStats, +}
Expand description

Connection statistics

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§udp_tx: UdpStats

Statistics about UDP datagrams transmitted on a connection

+
§udp_rx: UdpStats

Statistics about UDP datagrams received on a connection

+
§frame_tx: FrameStats

Statistics about frames transmitted on a connection

+
§frame_rx: FrameStats

Statistics about frames received on a connection

+

Trait Implementations§

Source§

impl Clone for ConnectionStats

Source§

fn clone(&self) -> ConnectionStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ConnectionStats

Source§

fn default() -> ConnectionStats

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.Datagram.html b/pr/395/docs/iroh_quinn_proto/struct.Datagram.html new file mode 100644 index 000000000..7e28cdc5b --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.Datagram.html @@ -0,0 +1,31 @@ +Datagram in iroh_quinn_proto - Rust

Datagram

Struct Datagram 

Source
pub struct Datagram {
+    pub data: Bytes,
+}
Expand description

An unreliable datagram

+

Fields§

§data: Bytes

Payload

+

Trait Implementations§

Source§

impl Clone for Datagram

Source§

fn clone(&self) -> Datagram

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Datagram

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Datagram

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Encodable for Datagram

Source§

fn encode<B: BufMut>(&self, out: &mut B)

Append the encoding of self to the provided buffer.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.DatagramInfo.html b/pr/395/docs/iroh_quinn_proto/struct.DatagramInfo.html new file mode 100644 index 000000000..0b1899150 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.DatagramInfo.html @@ -0,0 +1,32 @@ +DatagramInfo in iroh_quinn_proto - Rust

DatagramInfo

Struct DatagramInfo 

Source
pub struct DatagramInfo(/* private fields */);

Trait Implementations§

Source§

impl Clone for DatagramInfo

Source§

fn clone(&self) -> DatagramInfo

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DatagramInfo

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for DatagramInfo

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for DatagramInfo

Source§

fn eq(&self, other: &DatagramInfo) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for DatagramInfo

Source§

impl Eq for DatagramInfo

Source§

impl StructuralPartialEq for DatagramInfo

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.Datagrams.html b/pr/395/docs/iroh_quinn_proto/struct.Datagrams.html new file mode 100644 index 000000000..37ae13e0f --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.Datagrams.html @@ -0,0 +1,44 @@ +Datagrams in iroh_quinn_proto - Rust

Datagrams

Struct Datagrams 

Source
pub struct Datagrams<'a> { /* private fields */ }
Expand description

API to control datagram traffic

+

Implementations§

Source§

impl Datagrams<'_>

Source

pub fn send(&mut self, data: Bytes, drop: bool) -> Result<(), SendDatagramError>

Queue an unreliable, unordered datagram for immediate transmission

+

If drop is true, previously queued datagrams which are still unsent may be discarded to +make space for this datagram, in order of oldest to newest. If drop is false, and there +isn’t enough space due to previously queued datagrams, this function will return +SendDatagramError::Blocked. Event::DatagramsUnblocked will be emitted once datagrams +have been sent.

+

Returns Err iff a len-byte datagram cannot currently be sent.

+
Source

pub fn max_size(&self) -> Option<usize>

Compute the maximum size of datagrams that may be passed to send_datagram

+

Returns None if datagrams are unsupported by the peer or disabled locally.

+

This may change over the lifetime of a connection according to variation in the path MTU +estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer’s +limit is large this is guaranteed to be a little over a kilobyte at minimum.

+

Not necessarily the maximum size of received datagrams.

+

When multipath is enabled, this is calculated using the smallest MTU across all +available paths.

+
Source

pub fn recv(&mut self) -> Option<Bytes>

Receive an unreliable, unordered datagram

+
Source

pub fn send_buffer_space(&self) -> usize

Bytes available in the outgoing datagram buffer

+

When greater than zero, sending a datagram of at most this size is +guaranteed not to cause older datagrams to be dropped.

+

Auto Trait Implementations§

§

impl<'a> Freeze for Datagrams<'a>

§

impl<'a> !RefUnwindSafe for Datagrams<'a>

§

impl<'a> Send for Datagrams<'a>

§

impl<'a> Sync for Datagrams<'a>

§

impl<'a> Unpin for Datagrams<'a>

§

impl<'a> !UnwindSafe for Datagrams<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.Endpoint.html b/pr/395/docs/iroh_quinn_proto/struct.Endpoint.html new file mode 100644 index 000000000..af842f659 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.Endpoint.html @@ -0,0 +1,78 @@ +Endpoint in iroh_quinn_proto - Rust

Endpoint

Struct Endpoint 

Source
pub struct Endpoint { /* private fields */ }
Expand description

The main entry point to the library

+

This object performs no I/O whatsoever. Instead, it consumes incoming packets and +connection-generated events via handle and handle_event.

+

Implementations§

Source§

impl Endpoint

Source

pub fn new( + config: Arc<EndpointConfig>, + server_config: Option<Arc<ServerConfig>>, + allow_mtud: bool, +) -> Self

Create a new endpoint

+

allow_mtud enables path MTU detection when requested by Connection configuration for +better performance. This requires that outgoing packets are never fragmented, which can be +achieved via e.g. the IPV6_DONTFRAG socket option.

+
Source

pub fn set_server_config(&mut self, server_config: Option<Arc<ServerConfig>>)

Replace the server configuration, affecting new incoming connections only

+
Source

pub fn handle_event( + &mut self, + ch: ConnectionHandle, + event: EndpointEvent, +) -> Option<ConnectionEvent>

Process EndpointEvents emitted from related Connections

+

In turn, processing this event may return a ConnectionEvent for the same Connection.

+
Source

pub fn handle( + &mut self, + now: Instant, + network_path: FourTuple, + ecn: Option<EcnCodepoint>, + data: BytesMut, + buf: &mut Vec<u8>, +) -> Option<DatagramEvent>

Process an incoming UDP datagram

+
Source

pub fn connect( + &mut self, + now: Instant, + config: ClientConfig, + remote: SocketAddr, + server_name: &str, +) -> Result<(ConnectionHandle, Connection), ConnectError>

Initiate a connection

+
Source

pub fn accept( + &mut self, + incoming: Incoming, + now: Instant, + buf: &mut Vec<u8>, + server_config: Option<Arc<ServerConfig>>, +) -> Result<(ConnectionHandle, Connection), Box<AcceptError>>

Attempt to accept this incoming connection (an error may still occur)

+
Source

pub fn refuse(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Transmit

Reject this incoming connection attempt

+
Source

pub fn retry( + &mut self, + incoming: Incoming, + buf: &mut Vec<u8>, +) -> Result<Transmit, RetryError>

Respond with a retry packet, requiring the client to retry with address validation

+

Errors if incoming.may_retry() is false.

+
Source

pub fn ignore(&mut self, incoming: Incoming)

Ignore this incoming connection attempt, not sending any packet in response

+

Doing this actively, rather than merely dropping the Incoming, is necessary to prevent +memory leaks due to state within Endpoint tracking the incoming connection.

+
Source

pub fn config(&self) -> &EndpointConfig

Access the configuration used by this endpoint

+
Source

pub fn open_connections(&self) -> usize

Number of connections that are currently open

+
Source

pub fn incoming_buffer_bytes(&self) -> u64

Counter for the number of bytes currently used +in the buffers for Initial and 0-RTT messages for pending incoming connections

+

Trait Implementations§

Source§

impl Debug for Endpoint

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.EndpointConfig.html b/pr/395/docs/iroh_quinn_proto/struct.EndpointConfig.html new file mode 100644 index 000000000..a8cd850a4 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.EndpointConfig.html @@ -0,0 +1,66 @@ +EndpointConfig in iroh_quinn_proto - Rust

EndpointConfig

Struct EndpointConfig 

Source
pub struct EndpointConfig { /* private fields */ }
Expand description

Global configuration for the endpoint, affecting all connections

+

Default values should be suitable for most internet applications.

+

Implementations§

Source§

impl EndpointConfig

Source

pub fn new(reset_key: Arc<dyn HmacKey>) -> Self

Create a default config with a particular reset_key

+
Source

pub fn cid_generator<F: Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync + 'static>( + &mut self, + factory: F, +) -> &mut Self

Supply a custom connection ID generator factory

+

Called once by each Endpoint constructed from this configuration to obtain the CID +generator which will be used to generate the CIDs used for incoming packets on all +connections involving that Endpoint. A custom CID generator allows applications to embed +information in local connection IDs, e.g. to support stateless packet-level load balancers.

+

Defaults to HashedConnectionIdGenerator.

+
Source

pub fn reset_key(&mut self, key: Arc<dyn HmacKey>) -> &mut Self

Private key used to send authenticated connection resets to peers who were +communicating with a previous instance of this endpoint.

+
Source

pub fn max_udp_payload_size( + &mut self, + value: u16, +) -> Result<&mut Self, ConfigError>

Maximum UDP payload size accepted from peers (excluding UDP and IP overhead).

+

Must be greater or equal than 1200.

+

Defaults to 1472, which is the largest UDP payload that can be transmitted in the typical +1500 byte Ethernet MTU. Deployments on links with larger MTUs (e.g. loopback or Ethernet +with jumbo frames) can raise this to improve performance at the cost of a linear increase in +datagram receive buffer size.

+
Source

pub fn get_max_udp_payload_size(&self) -> u64

Get the current value of max_udp_payload_size

+
Source

pub fn supported_versions(&mut self, supported_versions: Vec<u32>) -> &mut Self

Override supported QUIC versions

+
Source

pub fn grease_quic_bit(&mut self, value: bool) -> &mut Self

Whether to accept QUIC packets containing any value for the fixed bit

+

Enabled by default. Helps protect against protocol ossification and makes traffic less +identifiable to observers. Disable if helping observers identify this traffic as QUIC is +desired.

+
Source

pub fn min_reset_interval(&mut self, value: Duration) -> &mut Self

Minimum interval between outgoing stateless reset packets

+

Defaults to 20ms. Limits the impact of attacks which flood an endpoint with garbage packets, +e.g. ISAKMP/IKE amplification. Larger values provide a stronger defense, but may delay +detection of some error conditions by clients. Using a ConnectionIdGenerator with a low +rate of false positives in validate reduces the risk +incurred by a small minimum reset interval.

+
Source

pub fn rng_seed(&mut self, seed: Option<[u8; 32]>) -> &mut Self

Optional seed to be used internally for random number generation

+

By default, quinn will initialize an endpoint’s rng using a platform entropy source. +However, you can seed the rng yourself through this method (e.g. if you need to run quinn +deterministically or if you are using quinn in an environment that doesn’t have a source of +entropy available).

+

Trait Implementations§

Source§

impl Clone for EndpointConfig

Source§

fn clone(&self) -> EndpointConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EndpointConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for EndpointConfig

Available on crate features aws-lc-rs or ring only.
Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.EndpointEvent.html b/pr/395/docs/iroh_quinn_proto/struct.EndpointEvent.html new file mode 100644 index 000000000..6149c5b74 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.EndpointEvent.html @@ -0,0 +1,30 @@ +EndpointEvent in iroh_quinn_proto - Rust

EndpointEvent

Struct EndpointEvent 

Source
pub struct EndpointEvent(/* private fields */);
Expand description

Events sent from a Connection to an Endpoint

+

Implementations§

Source§

impl EndpointEvent

Source

pub fn drained() -> Self

Construct an event that indicating that a Connection will no longer emit events

+

Useful for notifying an Endpoint that a Connection has been destroyed outside of the +usual state machine flow, e.g. when being dropped by the user.

+
Source

pub fn is_drained(&self) -> bool

Determine whether this is the last event a Connection will emit

+

Useful for determining when connection-related event loop state can be freed.

+

Trait Implementations§

Source§

impl Debug for EndpointEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html b/pr/395/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html new file mode 100644 index 000000000..4be727d57 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html @@ -0,0 +1,26 @@ +FixedLengthConnectionIdParser in iroh_quinn_proto - Rust

FixedLengthConnectionIdParser

Struct FixedLengthConnectionIdParser 

Source
pub struct FixedLengthConnectionIdParser { /* private fields */ }
Expand description

A ConnectionIdParser implementation that assumes the connection ID is of fixed length

+

Implementations§

Source§

impl FixedLengthConnectionIdParser

Source

pub fn new(expected_len: usize) -> Self

Create a new instance of FixedLengthConnectionIdParser

+

Trait Implementations§

Source§

impl ConnectionIdParser for FixedLengthConnectionIdParser

Source§

fn parse(&self, buffer: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError>

Parse a connection id from given buffer

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.FourTuple.html b/pr/395/docs/iroh_quinn_proto/struct.FourTuple.html new file mode 100644 index 000000000..ed9bf2c33 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.FourTuple.html @@ -0,0 +1,58 @@ +FourTuple in iroh_quinn_proto - Rust

FourTuple

Struct FourTuple 

Source
pub struct FourTuple {
+    pub remote: SocketAddr,
+    pub local_ip: Option<IpAddr>,
+}
Expand description

Identifies a network path by the combination of remote and local addresses

+

Including the local ensures good behavior when the host has multiple IP addresses on the same +subnet and zero-length connection IDs are in use or when multipath is enabled and multiple +paths exist with the same remote, but different local IP interfaces.

+

Fields§

§remote: SocketAddr

The remote side of this tuple

+
§local_ip: Option<IpAddr>

The local side of this tuple.

+

The socket is irrelevant for our intents and purposes: +When we send, we can only specify the src_ip, not the source port. +So even if we track the port, we won’t be able to make use of it.

+

Implementations§

Source§

impl FourTuple

Source

pub fn is_probably_same_path(&self, other: &Self) -> bool

Returns whether we think the other address probably represents the same path +as ours.

+

If we have a local IP set, then we’re exact and only match if the 4-tuples are +exactly equal. +If we don’t have a local IP set, then we only check the remote addresses for equality.

+
Source

pub fn update_local_if_same_remote(&mut self, other: &Self) -> bool

Updates this tuple’s local address iff

+
    +
  • it was unset before,
  • +
  • the other tuple has the same remote, and
  • +
  • the other tuple has a local address set.
  • +
+

Returns whether this and the other remote are now fully equal.

+

Trait Implementations§

Source§

impl Clone for FourTuple

Source§

fn clone(&self) -> FourTuple

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FourTuple

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for FourTuple

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Hash for FourTuple

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for FourTuple

Source§

fn eq(&self, other: &FourTuple) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for FourTuple

Source§

impl Eq for FourTuple

Source§

impl StructuralPartialEq for FourTuple

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.FrameStats.html b/pr/395/docs/iroh_quinn_proto/struct.FrameStats.html new file mode 100644 index 000000000..99694c4fa --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.FrameStats.html @@ -0,0 +1,65 @@ +FrameStats in iroh_quinn_proto - Rust

FrameStats

Struct FrameStats 

Source
#[non_exhaustive]
pub struct FrameStats {
Show 37 fields + pub acks: u64, + pub path_acks: u64, + pub ack_frequency: u64, + pub crypto: u64, + pub connection_close: u64, + pub data_blocked: u64, + pub datagram: u64, + pub handshake_done: u8, + pub immediate_ack: u64, + pub max_data: u64, + pub max_stream_data: u64, + pub max_streams_bidi: u64, + pub max_streams_uni: u64, + pub new_connection_id: u64, + pub path_new_connection_id: u64, + pub new_token: u64, + pub path_challenge: u64, + pub path_response: u64, + pub ping: u64, + pub reset_stream: u64, + pub retire_connection_id: u64, + pub path_retire_connection_id: u64, + pub stream_data_blocked: u64, + pub streams_blocked_bidi: u64, + pub streams_blocked_uni: u64, + pub stop_sending: u64, + pub stream: u64, + pub observed_addr: u64, + pub path_abandon: u64, + pub path_status_available: u64, + pub path_status_backup: u64, + pub max_path_id: u64, + pub paths_blocked: u64, + pub path_cids_blocked: u64, + pub add_address: u64, + pub reach_out: u64, + pub remove_address: u64, +
}
Expand description

Number of frames transmitted or received of each frame type

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§acks: u64§path_acks: u64§ack_frequency: u64§crypto: u64§connection_close: u64§data_blocked: u64§datagram: u64§handshake_done: u8§immediate_ack: u64§max_data: u64§max_stream_data: u64§max_streams_bidi: u64§max_streams_uni: u64§new_connection_id: u64§path_new_connection_id: u64§new_token: u64§path_challenge: u64§path_response: u64§ping: u64§reset_stream: u64§retire_connection_id: u64§path_retire_connection_id: u64§stream_data_blocked: u64§streams_blocked_bidi: u64§streams_blocked_uni: u64§stop_sending: u64§stream: u64§observed_addr: u64§path_abandon: u64§path_status_available: u64§path_status_backup: u64§max_path_id: u64§paths_blocked: u64§path_cids_blocked: u64§add_address: u64§reach_out: u64§remove_address: u64

Trait Implementations§

Source§

impl Clone for FrameStats

Source§

fn clone(&self) -> FrameStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FrameStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for FrameStats

Source§

fn default() -> FrameStats

Returns the “default value” for a type. Read more
Source§

impl Copy for FrameStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.HashedConnectionIdGenerator.html b/pr/395/docs/iroh_quinn_proto/struct.HashedConnectionIdGenerator.html new file mode 100644 index 000000000..c79c79a48 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.HashedConnectionIdGenerator.html @@ -0,0 +1,33 @@ +HashedConnectionIdGenerator in iroh_quinn_proto - Rust

HashedConnectionIdGenerator

Struct HashedConnectionIdGenerator 

Source
pub struct HashedConnectionIdGenerator { /* private fields */ }
Expand description

Generates 8-byte connection IDs that can be efficiently +validated

+

This generator uses a non-cryptographic hash and can therefore still be spoofed, but nonetheless +helps prevents Quinn from responding to non-QUIC packets at very low cost.

+

Implementations§

Source§

impl HashedConnectionIdGenerator

Source

pub fn new() -> Self

Create a generator with a random key

+
Source

pub fn from_key(key: u64) -> Self

Create a generator with a specific key

+

Allows validate to recognize a consistent set of +connection IDs across restarts

+
Source

pub fn set_lifetime(&mut self, d: Duration) -> &mut Self

Set the lifetime of CIDs created by this generator

+

Trait Implementations§

Source§

impl ConnectionIdGenerator for HashedConnectionIdGenerator

Source§

fn generate_cid(&mut self) -> ConnectionId

Generates a new CID Read more
Source§

fn validate(&self, cid: ConnectionId) -> Result<(), InvalidCid>

Quickly determine whether cid could have been generated by this generator Read more
Source§

fn cid_len(&self) -> usize

Returns the length of a CID for connections created by this generator
Source§

fn cid_lifetime(&self) -> Option<Duration>

Returns the lifetime of generated Connection IDs Read more
Source§

impl Default for HashedConnectionIdGenerator

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.IdleTimeout.html b/pr/395/docs/iroh_quinn_proto/struct.IdleTimeout.html new file mode 100644 index 000000000..89720575f --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.IdleTimeout.html @@ -0,0 +1,49 @@ +IdleTimeout in iroh_quinn_proto - Rust

IdleTimeout

Struct IdleTimeout 

Source
pub struct IdleTimeout(/* private fields */);
Expand description

Maximum duration of inactivity to accept before timing out the connection

+

This wraps an underlying VarInt, representing the duration in milliseconds. Values can be +constructed by converting directly from VarInt, or using TryFrom<Duration>.

+ +
// A `VarInt`-encoded value in milliseconds
+let timeout = IdleTimeout::from(VarInt::from_u32(10_000));
+
+// Try to convert a `Duration` into a `VarInt`-encoded timeout
+let timeout = IdleTimeout::try_from(Duration::from_secs(10))?;

Trait Implementations§

Source§

impl Clone for IdleTimeout

Source§

fn clone(&self) -> IdleTimeout

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for IdleTimeout

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for IdleTimeout

Source§

fn default() -> IdleTimeout

Returns the “default value” for a type. Read more
Source§

impl From<VarInt> for IdleTimeout

Source§

fn from(inner: VarInt) -> Self

Converts to this type from the input type.
Source§

impl Hash for IdleTimeout

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for IdleTimeout

Source§

fn cmp(&self, other: &IdleTimeout) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for IdleTimeout

Source§

fn eq(&self, other: &IdleTimeout) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for IdleTimeout

Source§

fn partial_cmp(&self, other: &IdleTimeout) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl TryFrom<Duration> for IdleTimeout

Source§

type Error = VarIntBoundsExceeded

The type returned in the event of a conversion error.
Source§

fn try_from(timeout: Duration) -> Result<Self, Self::Error>

Performs the conversion.
Source§

impl Copy for IdleTimeout

Source§

impl Eq for IdleTimeout

Source§

impl StructuralPartialEq for IdleTimeout

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.Incoming.html b/pr/395/docs/iroh_quinn_proto/struct.Incoming.html new file mode 100644 index 000000000..41141a035 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.Incoming.html @@ -0,0 +1,36 @@ +Incoming in iroh_quinn_proto - Rust

Incoming

Struct Incoming 

Source
pub struct Incoming { /* private fields */ }
Expand description

An incoming connection for which the server has not yet begun its part of the handshake.

+

Implementations§

Source§

impl Incoming

Source

pub fn local_ip(&self) -> Option<IpAddr>

The local IP address which was used when the peer established the connection

+
Source

pub fn remote_address(&self) -> SocketAddr

The peer’s UDP address

+
Source

pub fn remote_address_validated(&self) -> bool

Whether the socket address that is initiating this connection has been validated

+

This means that the sender of the initial packet has proved that they can receive traffic +sent to self.remote_address().

+

If self.remote_address_validated() is false, self.may_retry() is guaranteed to be true. +The inverse is not guaranteed.

+
Source

pub fn may_retry(&self) -> bool

Whether it is legal to respond with a retry packet

+

If self.remote_address_validated() is false, self.may_retry() is guaranteed to be true. +The inverse is not guaranteed.

+
Source

pub fn orig_dst_cid(&self) -> ConnectionId

The original destination connection ID sent by the client

+

Trait Implementations§

Source§

impl Debug for Incoming

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.InvalidCid.html b/pr/395/docs/iroh_quinn_proto/struct.InvalidCid.html new file mode 100644 index 000000000..ac5647073 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.InvalidCid.html @@ -0,0 +1,27 @@ +InvalidCid in iroh_quinn_proto - Rust

InvalidCid

Struct InvalidCid 

Source
pub struct InvalidCid;
Expand description

The connection ID was not recognized by the ConnectionIdGenerator

+

Trait Implementations§

Source§

impl Clone for InvalidCid

Source§

fn clone(&self) -> InvalidCid

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for InvalidCid

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Copy for InvalidCid

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.InvalidFrameId.html b/pr/395/docs/iroh_quinn_proto/struct.InvalidFrameId.html new file mode 100644 index 000000000..2d0eb7ade --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.InvalidFrameId.html @@ -0,0 +1,31 @@ +InvalidFrameId in iroh_quinn_proto - Rust

InvalidFrameId

Struct InvalidFrameId 

Source
pub struct InvalidFrameId(/* private fields */);
Expand description

Encounter a frame ID that was not valid.

+

Trait Implementations§

Source§

impl Debug for InvalidFrameId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for InvalidFrameId

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for InvalidFrameId

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for InvalidFrameId

Source§

fn eq(&self, other: &InvalidFrameId) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for InvalidFrameId

Source§

impl StructuralPartialEq for InvalidFrameId

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.MtuDiscoveryConfig.html b/pr/395/docs/iroh_quinn_proto/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..348932417 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.MtuDiscoveryConfig.html @@ -0,0 +1,85 @@ +MtuDiscoveryConfig in iroh_quinn_proto - Rust

MtuDiscoveryConfig

Struct MtuDiscoveryConfig 

Source
pub struct MtuDiscoveryConfig { /* private fields */ }
Expand description

Parameters governing MTU discovery.

+

§The why of MTU discovery

+

By design, QUIC ensures during the handshake that the network path between the client and the +server is able to transmit unfragmented UDP packets with a body of 1200 bytes. In other words, +once the connection is established, we know that the network path’s maximum transmission unit +(MTU) is of at least 1200 bytes (plus IP and UDP headers). Because of this, a QUIC endpoint can +split outgoing data in packets of 1200 bytes, with confidence that the network will be able to +deliver them (if the endpoint were to send bigger packets, they could prove too big and end up +being dropped).

+

There is, however, a significant overhead associated to sending a packet. If the same +information can be sent in fewer packets, that results in higher throughput. The amount of +packets that need to be sent is inversely proportional to the MTU: the higher the MTU, the +bigger the packets that can be sent, and the fewer packets that are needed to transmit a given +amount of bytes.

+

Most networks have an MTU higher than 1200. Through MTU discovery, endpoints can detect the +path’s MTU and, if it turns out to be higher, start sending bigger packets.

+

§MTU discovery internals

+

Quinn implements MTU discovery through DPLPMTUD (Datagram Packetization Layer Path MTU +Discovery), described in section 14.3 of RFC +9000. This method consists of sending +QUIC packets padded to a particular size (called PMTU probes), and waiting to see if the remote +peer responds with an ACK. If an ACK is received, that means the probe arrived at the remote +peer, which in turn means that the network path’s MTU is of at least the packet’s size. If the +probe is lost, it is sent another 2 times before concluding that the MTU is lower than the +packet’s size.

+

MTU discovery runs on a schedule (e.g. every 600 seconds) specified through +MtuDiscoveryConfig::interval. The first run happens right after the handshake, and +subsequent discoveries are scheduled to run when the interval has elapsed, starting from the +last time when MTU discovery completed.

+

Since the search space for MTUs is quite big (the smallest possible MTU is 1200, and the highest +is 65527), Quinn performs a binary search to keep the number of probes as low as possible. The +lower bound of the search is equal to TransportConfig::initial_mtu in the +initial MTU discovery run, and is equal to the currently discovered MTU in subsequent runs. The +upper bound is determined by the minimum of MtuDiscoveryConfig::upper_bound and the +max_udp_payload_size transport parameter received from the peer during the handshake.

+

§Black hole detection

+

If, at some point, the network path no longer accepts packets of the detected size, packet loss +will eventually trigger black hole detection and reset the detected MTU to 1200. In that case, +MTU discovery will be triggered after MtuDiscoveryConfig::black_hole_cooldown (ignoring the +timer that was set based on MtuDiscoveryConfig::interval).

+

§Interaction between peers

+

There is no guarantee that the MTU on the path between A and B is the same as the MTU of the +path between B and A. Therefore, each peer in the connection needs to run MTU discovery +independently in order to discover the path’s MTU.

+

Implementations§

Source§

impl MtuDiscoveryConfig

Source

pub fn interval(&mut self, value: Duration) -> &mut Self

Specifies the time to wait after completing MTU discovery before starting a new MTU +discovery run.

+

Defaults to 600 seconds, as recommended by RFC +8899.

+
Source

pub fn upper_bound(&mut self, value: u16) -> &mut Self

Specifies the upper bound to the max UDP payload size that MTU discovery will search for.

+

Defaults to 1452, to stay within Ethernet’s MTU when using IPv4 and IPv6. The highest +allowed value is 65527, which corresponds to the maximum permitted UDP payload on IPv6.

+

It is safe to use an arbitrarily high upper bound, regardless of the network path’s MTU. The +only drawback is that MTU discovery might take more time to finish.

+
Source

pub fn black_hole_cooldown(&mut self, value: Duration) -> &mut Self

Specifies the amount of time that MTU discovery should wait after a black hole was detected +before running again. Defaults to one minute.

+

Black hole detection can be spuriously triggered in case of congestion, so it makes sense to +try MTU discovery again after a short period of time.

+
Source

pub fn minimum_change(&mut self, value: u16) -> &mut Self

Specifies the minimum MTU change to stop the MTU discovery phase. +Defaults to 20.

+

Trait Implementations§

Source§

impl Clone for MtuDiscoveryConfig

Source§

fn clone(&self) -> MtuDiscoveryConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MtuDiscoveryConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for MtuDiscoveryConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.MultipathNotNegotiated.html b/pr/395/docs/iroh_quinn_proto/struct.MultipathNotNegotiated.html new file mode 100644 index 000000000..3a789b444 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.MultipathNotNegotiated.html @@ -0,0 +1,28 @@ +MultipathNotNegotiated in iroh_quinn_proto - Rust

MultipathNotNegotiated

Struct MultipathNotNegotiated 

Source
pub struct MultipathNotNegotiated { /* private fields */ }
Expand description

Error when the multipath extension was not negotiated, but attempted to be used.

+

Trait Implementations§

Source§

impl Clone for MultipathNotNegotiated

Source§

fn clone(&self) -> MultipathNotNegotiated

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MultipathNotNegotiated

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for MultipathNotNegotiated

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for MultipathNotNegotiated

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Copy for MultipathNotNegotiated

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.NoneTokenLog.html b/pr/395/docs/iroh_quinn_proto/struct.NoneTokenLog.html new file mode 100644 index 000000000..6a9dbaaf1 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.NoneTokenLog.html @@ -0,0 +1,31 @@ +NoneTokenLog in iroh_quinn_proto - Rust

NoneTokenLog

Struct NoneTokenLog 

Source
pub struct NoneTokenLog;
Expand description

Null implementation of TokenLog, which never accepts tokens

+

Trait Implementations§

Source§

impl TokenLog for NoneTokenLog

Source§

fn check_and_insert( + &self, + _: u128, + _: SystemTime, + _: Duration, +) -> Result<(), TokenReuseError>

Record that the token was used and, ideally, return a token reuse error if the token may +have been already used previously Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.NoneTokenStore.html b/pr/395/docs/iroh_quinn_proto/struct.NoneTokenStore.html new file mode 100644 index 000000000..397a2aff9 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.NoneTokenStore.html @@ -0,0 +1,25 @@ +NoneTokenStore in iroh_quinn_proto - Rust

NoneTokenStore

Struct NoneTokenStore 

Source
pub struct NoneTokenStore;
Expand description

Null implementation of TokenStore, which does not store any tokens

+

Trait Implementations§

Source§

impl TokenStore for NoneTokenStore

Source§

fn insert(&self, _: &str, _: Bytes)

Potentially store a token for later one-time use Read more
Source§

fn take(&self, _: &str) -> Option<Bytes>

Try to find and take a token that was stored with the given server name Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.PartialDecode.html b/pr/395/docs/iroh_quinn_proto/struct.PartialDecode.html new file mode 100644 index 000000000..ff632cf9d --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.PartialDecode.html @@ -0,0 +1,42 @@ +PartialDecode in iroh_quinn_proto - Rust

PartialDecode

Struct PartialDecode 

Source
pub struct PartialDecode { /* private fields */ }
Expand description

Decodes a QUIC packet’s invariant header

+

Due to packet number encryption, it is impossible to fully decode a header +(which includes a variable-length packet number) without crypto context. +The crypto context (represented by the Crypto type in Quinn) is usually +part of the Connection, or can be derived from the destination CID for +Initial packets.

+

To cope with this, we decode the invariant header (which should be stable +across QUIC versions), which gives us the destination CID and allows us +to inspect the version and packet type (which depends on the version). +This information allows us to fully decode and decrypt the packet.

+

Implementations§

Source§

impl PartialDecode

Source

pub fn new( + bytes: BytesMut, + cid_parser: &(impl ConnectionIdParser + ?Sized), + supported_versions: &[u32], + grease_quic_bit: bool, +) -> Result<(Self, Option<BytesMut>), PacketDecodeError>

Begin decoding a QUIC packet from bytes, returning any trailing data not part of that packet

+
Source

pub fn dst_cid(&self) -> ConnectionId

The destination connection ID of the packet

+
Source

pub fn len(&self) -> usize

Length of QUIC packet being decoded

+

Trait Implementations§

Source§

impl Debug for PartialDecode

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.PathId.html b/pr/395/docs/iroh_quinn_proto/struct.PathId.html new file mode 100644 index 000000000..5f055b0e0 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.PathId.html @@ -0,0 +1,49 @@ +PathId in iroh_quinn_proto - Rust

PathId

Struct PathId 

Source
pub struct PathId(/* private fields */);
Expand description

Id representing different paths when using multipath extension

+

Implementations§

Source§

impl PathId

Source

pub const MAX: Self

The maximum path ID allowed.

+
Source

pub const ZERO: Self

The 0 path id.

+
Source

pub fn saturating_add(self, rhs: impl Into<Self>) -> Self

Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead +of overflowing.

+
Source

pub fn saturating_sub(self, rhs: impl Into<Self>) -> Self

Saturating integer subtraction. Computes self - rhs, saturating at the numeric bounds +instead of overflowing.

+

Trait Implementations§

Source§

impl Clone for PathId

Source§

fn clone(&self) -> PathId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Decodable for PathId

Source§

fn decode<B: Buf>(r: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Default for PathId

Source§

fn default() -> PathId

Returns the “default value” for a type. Read more
Source§

impl Display for PathId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Encodable for PathId

Source§

fn encode<B: BufMut>(&self, w: &mut B)

Append the encoding of self to the provided buffer.
Source§

impl<T: Into<u32>> From<T> for PathId

Source§

fn from(source: T) -> Self

Converts to this type from the input type.
Source§

impl Hash for PathId

Source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for PathId

Source§

fn cmp(&self, other: &PathId) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for PathId

Source§

fn eq(&self, other: &PathId) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for PathId

Source§

fn partial_cmp(&self, other: &PathId) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for PathId

Source§

impl Eq for PathId

Source§

impl IdentityHashable for PathId

Source§

impl StructuralPartialEq for PathId

Auto Trait Implementations§

§

impl Freeze for PathId

§

impl RefUnwindSafe for PathId

§

impl Send for PathId

§

impl Sync for PathId

§

impl Unpin for PathId

§

impl UnwindSafe for PathId

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.PathStats.html b/pr/395/docs/iroh_quinn_proto/struct.PathStats.html new file mode 100644 index 000000000..1c4d93275 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.PathStats.html @@ -0,0 +1,56 @@ +PathStats in iroh_quinn_proto - Rust

PathStats

Struct PathStats 

Source
#[non_exhaustive]
pub struct PathStats { + pub rtt: Duration, + pub udp_tx: UdpStats, + pub udp_rx: UdpStats, + pub cwnd: u64, + pub congestion_events: u64, + pub lost_packets: u64, + pub lost_bytes: u64, + pub sent_plpmtud_probes: u64, + pub lost_plpmtud_probes: u64, + pub black_holes_detected: u64, + pub current_mtu: u16, +}
Expand description

Statistics related to a transmission path

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§rtt: Duration

Current best estimate of this connection’s latency (round-trip-time)

+
§udp_tx: UdpStats

Statistics about datagrams and bytes sent on this path

+
§udp_rx: UdpStats

Statistics about datagrams and bytes received on this path

+
§cwnd: u64

Current congestion window of the connection

+
§congestion_events: u64

Congestion events on the connection

+
§lost_packets: u64

The amount of packets lost on this path

+
§lost_bytes: u64

The amount of bytes lost on this path

+
§sent_plpmtud_probes: u64

The amount of PLPMTUD probe packets sent on this path (also counted by udp_tx.datagrams)

+
§lost_plpmtud_probes: u64

The amount of PLPMTUD probe packets lost on this path (ignored by lost_packets and +lost_bytes)

+
§black_holes_detected: u64

The number of times a black hole was detected in the path

+
§current_mtu: u16

Largest UDP payload size the path currently supports

+

Trait Implementations§

Source§

impl Clone for PathStats

Source§

fn clone(&self) -> PathStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for PathStats

Source§

fn default() -> PathStats

Returns the “default value” for a type. Read more
Source§

impl PartialEq for PathStats

Source§

fn eq(&self, other: &PathStats) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathStats

Source§

impl Eq for PathStats

Source§

impl StructuralPartialEq for PathStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html b/pr/395/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html new file mode 100644 index 000000000..ed7efc941 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html @@ -0,0 +1,38 @@ +ProtectedInitialHeader in iroh_quinn_proto - Rust

ProtectedInitialHeader

Struct ProtectedInitialHeader 

Source
pub struct ProtectedInitialHeader {
+    pub dst_cid: ConnectionId,
+    pub src_cid: ConnectionId,
+    pub token_pos: Range<usize>,
+    pub len: u64,
+    pub version: u32,
+}
Expand description

Header of an Initial packet, before decryption

+

Fields§

§dst_cid: ConnectionId

Destination Connection ID

+
§src_cid: ConnectionId

Source Connection ID

+
§token_pos: Range<usize>

The position of a token in the packet buffer

+
§len: u64

Length of the packet payload

+
§version: u32

QUIC version

+

Trait Implementations§

Source§

impl Clone for ProtectedInitialHeader

Source§

fn clone(&self) -> ProtectedInitialHeader

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ProtectedInitialHeader

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.QlogConfig.html b/pr/395/docs/iroh_quinn_proto/struct.QlogConfig.html new file mode 100644 index 000000000..a29e57260 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.QlogConfig.html @@ -0,0 +1,34 @@ +QlogConfig in iroh_quinn_proto - Rust

QlogConfig

Struct QlogConfig 

Source
pub struct QlogConfig { /* private fields */ }
Expand description

Configuration for qlog trace logging.

+

This struct is returned from QlogFactory::for_connection if qlog logging should +be enabled for a connection. It allows to set metadata for the qlog trace.

+

The trace will be written to the provided writer in the [JSON-SEQ format] defined in the qlog spec.

+

JSON-SEQ format

+

Implementations§

Source§

impl QlogConfig

Source

pub fn new(writer: Box<dyn Write + Send + Sync>) -> Self

Creates a new QlogConfig that writes a qlog trace to the specified writer.

+
Source

pub fn title(&mut self, title: Option<String>) -> &mut Self

Title to record in the qlog capture

+
Source

pub fn description(&mut self, description: Option<String>) -> &mut Self

Description to record in the qlog capture

+
Source

pub fn start_time(&mut self, start_time: Instant) -> &mut Self

Epoch qlog event times are recorded relative to

+

If unset, the start of the connection is used.

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.QlogFileFactory.html b/pr/395/docs/iroh_quinn_proto/struct.QlogFileFactory.html new file mode 100644 index 000000000..0dea10ba7 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.QlogFileFactory.html @@ -0,0 +1,39 @@ +QlogFileFactory in iroh_quinn_proto - Rust

QlogFileFactory

Struct QlogFileFactory 

Source
pub struct QlogFileFactory { /* private fields */ }
Expand description

Enables writing qlog traces to a directory.

+

Implementations§

Source§

impl QlogFileFactory

Source

pub fn new(dir: PathBuf) -> Self

Creates a new qlog factory that writes files into the specified directory.

+
Source

pub fn from_env() -> Self

Creates a new qlog factory that writes files into QLOGDIR, if set.

+

If the environment variable QLOGDIR is set, qlog traces for all connections handled +by this endpoint will be written into that directory. +If the directory doesn’t exist it will be created.

+
Source

pub fn with_prefix(self, prefix: impl ToString) -> Self

Sets a prefix to the filename of the generated files.

+
Source

pub fn with_start_instant(self, start: Instant) -> Self

Override the instant relative to which all events are recorded.

+

If not set, events will be recorded relative to the start of the connection.

+

Trait Implementations§

Source§

impl Debug for QlogFileFactory

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl QlogFactory for QlogFileFactory

Source§

fn for_connection( + &self, + side: Side, + _remote: SocketAddr, + initial_dst_cid: ConnectionId, + now: Instant, +) -> Option<QlogConfig>

Returns a QlogConfig for a connection, if logging should be enabled. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.RandomConnectionIdGenerator.html b/pr/395/docs/iroh_quinn_proto/struct.RandomConnectionIdGenerator.html new file mode 100644 index 000000000..0858f5d9a --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.RandomConnectionIdGenerator.html @@ -0,0 +1,33 @@ +RandomConnectionIdGenerator in iroh_quinn_proto - Rust

RandomConnectionIdGenerator

Struct RandomConnectionIdGenerator 

Source
pub struct RandomConnectionIdGenerator { /* private fields */ }
Expand description

Generates purely random connection IDs of a specified length

+

Random CIDs can be smaller than those produced by HashedConnectionIdGenerator, but cannot be +usefully validated.

+

Implementations§

Source§

impl RandomConnectionIdGenerator

Source

pub fn new(cid_len: usize) -> Self

Initialize Random CID generator with a fixed CID length

+

The given length must be less than or equal to MAX_CID_SIZE.

+
Source

pub fn set_lifetime(&mut self, d: Duration) -> &mut Self

Set the lifetime of CIDs created by this generator

+

Trait Implementations§

Source§

impl Clone for RandomConnectionIdGenerator

Source§

fn clone(&self) -> RandomConnectionIdGenerator

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ConnectionIdGenerator for RandomConnectionIdGenerator

Source§

fn cid_len(&self) -> usize

Provide the length of dst_cid in short header packet

+
Source§

fn generate_cid(&mut self) -> ConnectionId

Generates a new CID Read more
Source§

fn cid_lifetime(&self) -> Option<Duration>

Returns the lifetime of generated Connection IDs Read more
Source§

fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid>

Quickly determine whether cid could have been generated by this generator Read more
Source§

impl Debug for RandomConnectionIdGenerator

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for RandomConnectionIdGenerator

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Copy for RandomConnectionIdGenerator

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.RecvStream.html b/pr/395/docs/iroh_quinn_proto/struct.RecvStream.html new file mode 100644 index 000000000..0111fd15a --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.RecvStream.html @@ -0,0 +1,44 @@ +RecvStream in iroh_quinn_proto - Rust

RecvStream

Struct RecvStream 

Source
pub struct RecvStream<'a> { /* private fields */ }
Expand description

Access to streams

+

Implementations§

Source§

impl RecvStream<'_>

Source

pub fn read(&mut self, ordered: bool) -> Result<Chunks<'_>, ReadableError>

Read from the given recv stream

+

max_length limits the maximum size of the returned Bytes value; passing usize::MAX +will yield the best performance. ordered will make sure the returned chunk’s offset will +have an offset exactly equal to the previously returned offset plus the previously returned +bytes’ length.

+

Yields Ok(None) if the stream was finished. Otherwise, yields a segment of data and its +offset in the stream. If ordered is false, segments may be received in any order, and +the Chunk’s offset field can be used to determine ordering in the caller.

+

While most applications will prefer to consume stream data in order, unordered reads can +improve performance when packet loss occurs and data cannot be retransmitted before the flow +control window is filled. On any given stream, you can switch from ordered to unordered +reads, but ordered reads on streams that have seen previous unordered reads will return +ReadError::IllegalOrderedRead.

+
Source

pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream>

Stop accepting data on the given receive stream

+

Discards unread data and notifies the peer to stop transmitting. Once stopped, further +attempts to operate on a stream will yield ClosedStream errors.

+
Source

pub fn received_reset(&mut self) -> Result<Option<VarInt>, ClosedStream>

Check whether this stream has been reset by the peer, returning the reset error code if so

+

After returning Ok(Some(_)) once, stream state will be discarded and all future calls will +return Err(ClosedStream).

+

Auto Trait Implementations§

§

impl<'a> Freeze for RecvStream<'a>

§

impl<'a> RefUnwindSafe for RecvStream<'a>

§

impl<'a> Send for RecvStream<'a>

§

impl<'a> Sync for RecvStream<'a>

§

impl<'a> Unpin for RecvStream<'a>

§

impl<'a> !UnwindSafe for RecvStream<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.RetryError.html b/pr/395/docs/iroh_quinn_proto/struct.RetryError.html new file mode 100644 index 000000000..378b21420 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.RetryError.html @@ -0,0 +1,27 @@ +RetryError in iroh_quinn_proto - Rust

RetryError

Struct RetryError 

Source
pub struct RetryError(/* private fields */);
Expand description

Error for attempting to retry an Incoming which already bears a token from a previous retry

+

Implementations§

Trait Implementations§

Source§

impl Debug for RetryError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for RetryError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for RetryError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.RttEstimator.html b/pr/395/docs/iroh_quinn_proto/struct.RttEstimator.html new file mode 100644 index 000000000..f09e208dc --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.RttEstimator.html @@ -0,0 +1,32 @@ +RttEstimator in iroh_quinn_proto - Rust

RttEstimator

Struct RttEstimator 

Source
pub struct RttEstimator { /* private fields */ }
Expand description

RTT estimation for a particular network path

+

Implementations§

Source§

impl RttEstimator

Source

pub fn get(&self) -> Duration

The current best RTT estimation.

+
Source

pub fn conservative(&self) -> Duration

Conservative estimate of RTT

+

Takes the maximum of smoothed and latest RTT, as recommended +in 6.1.2 of the recovery spec (draft 29).

+
Source

pub fn min(&self) -> Duration

Minimum RTT registered so far for this estimator.

+

Trait Implementations§

Source§

impl Clone for RttEstimator

Source§

fn clone(&self) -> RttEstimator

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for RttEstimator

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Copy for RttEstimator

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.SendStream.html b/pr/395/docs/iroh_quinn_proto/struct.SendStream.html new file mode 100644 index 000000000..88ebbaa61 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.SendStream.html @@ -0,0 +1,53 @@ +SendStream in iroh_quinn_proto - Rust

SendStream

Struct SendStream 

Source
pub struct SendStream<'a> { /* private fields */ }
Expand description

Access to streams

+

Implementations§

Source§

impl<'a> SendStream<'a>

Source

pub fn write(&mut self, data: &[u8]) -> Result<usize, WriteError>

Send data on the given stream

+

Returns the number of bytes successfully written.

+
Source

pub fn write_chunks( + &mut self, + data: &mut [Bytes], +) -> Result<Written, WriteError>

Send data on the given stream

+

Returns the number of bytes and chunks successfully written. +Note that this method might also write a partial chunk. In this case +Written::chunks will not count this chunk as fully written. However +the chunk will be advanced and contain only non-written data after the call.

+
Source

pub fn stopped(&self) -> Result<Option<VarInt>, ClosedStream>

Check if this stream was stopped, get the reason if it was

+
Source

pub fn finish(&mut self) -> Result<(), FinishError>

Finish a send stream, signalling that no more data will be sent.

+

If this fails, no StreamEvent::Finished will be generated.

+
Source

pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream>

Abandon transmitting data on a stream

+
§Panics
+
    +
  • when applied to a receive stream
  • +
+
Source

pub fn set_priority(&mut self, priority: i32) -> Result<(), ClosedStream>

Set the priority of a stream

+
§Panics
+
    +
  • when applied to a receive stream
  • +
+
Source

pub fn priority(&self) -> Result<i32, ClosedStream>

Get the priority of a stream

+
§Panics
+
    +
  • when applied to a receive stream
  • +
+

Auto Trait Implementations§

§

impl<'a> Freeze for SendStream<'a>

§

impl<'a> !RefUnwindSafe for SendStream<'a>

§

impl<'a> Send for SendStream<'a>

§

impl<'a> Sync for SendStream<'a>

§

impl<'a> Unpin for SendStream<'a>

§

impl<'a> !UnwindSafe for SendStream<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.ServerConfig.html b/pr/395/docs/iroh_quinn_proto/struct.ServerConfig.html new file mode 100644 index 000000000..130b45540 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.ServerConfig.html @@ -0,0 +1,102 @@ +ServerConfig in iroh_quinn_proto - Rust

ServerConfig

Struct ServerConfig 

Source
pub struct ServerConfig {
+    pub transport: Arc<TransportConfig>,
+    pub crypto: Arc<dyn ServerConfig>,
+    pub validation_token: ValidationTokenConfig,
+    /* private fields */
+}
Expand description

Parameters governing incoming connections

+

Default values should be suitable for most internet applications.

+

Fields§

§transport: Arc<TransportConfig>

Transport configuration to use for incoming connections

+
§crypto: Arc<dyn ServerConfig>

TLS configuration used for incoming connections

+

Must be set to use TLS 1.3 only.

+
§validation_token: ValidationTokenConfig

Configuration for sending and handling validation tokens

+

Implementations§

Source§

impl ServerConfig

Source

pub fn new( + crypto: Arc<dyn ServerConfig>, + token_key: Arc<dyn HandshakeTokenKey>, +) -> Self

Create a default config with a particular handshake token key

+
Source

pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self

Set a custom TransportConfig

+
Source

pub fn validation_token_config( + &mut self, + validation_token: ValidationTokenConfig, +) -> &mut Self

Set a custom ValidationTokenConfig

+
Source

pub fn token_key(&mut self, value: Arc<dyn HandshakeTokenKey>) -> &mut Self

Private key used to authenticate data included in handshake tokens

+
Source

pub fn retry_token_lifetime(&mut self, value: Duration) -> &mut Self

Duration after a retry token was issued for which it’s considered valid

+

Defaults to 15 seconds.

+
Source

pub fn migration(&mut self, value: bool) -> &mut Self

Whether to allow clients to migrate to new addresses

+

Improves behavior for clients that move between different internet connections or suffer NAT +rebinding. Enabled by default.

+
Source

pub fn preferred_address_v4( + &mut self, + address: Option<SocketAddrV4>, +) -> &mut Self

The preferred IPv4 address that will be communicated to clients during handshaking

+

If the client is able to reach this address, it will switch to it.

+
Source

pub fn preferred_address_v6( + &mut self, + address: Option<SocketAddrV6>, +) -> &mut Self

The preferred IPv6 address that will be communicated to clients during handshaking

+

If the client is able to reach this address, it will switch to it.

+
Source

pub fn max_incoming(&mut self, max_incoming: usize) -> &mut Self

Maximum number of Incoming to allow to exist at a time

+

An Incoming comes into existence when an incoming connection attempt +is received and stops existing when the application either accepts it or otherwise disposes +of it. While this limit is reached, new incoming connection attempts are immediately +refused. Larger values have greater worst-case memory consumption, but accommodate greater +application latency in handling incoming connection attempts.

+

The default value is set to 65536. With a typical Ethernet MTU of 1500 bytes, this limits +memory consumption from this to under 100 MiB–a generous amount that still prevents memory +exhaustion in most contexts.

+
Source

pub fn incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self

Maximum number of received bytes to buffer for each Incoming

+

An Incoming comes into existence when an incoming connection attempt +is received and stops existing when the application either accepts it or otherwise disposes +of it. This limit governs only packets received within that period, and does not include +the first packet. Packets received in excess of this limit are dropped, which may cause +0-RTT or handshake data to have to be retransmitted.

+

The default value is set to 10 MiB–an amount such that in most situations a client would +not transmit that much 0-RTT data faster than the server handles the corresponding +Incoming.

+
Source

pub fn incoming_buffer_size_total( + &mut self, + incoming_buffer_size_total: u64, +) -> &mut Self

Maximum number of received bytes to buffer for all Incoming +collectively

+

An Incoming comes into existence when an incoming connection attempt +is received and stops existing when the application either accepts it or otherwise disposes +of it. This limit governs only packets received within that period, and does not include +the first packet. Packets received in excess of this limit are dropped, which may cause +0-RTT or handshake data to have to be retransmitted.

+

The default value is set to 100 MiB–a generous amount that still prevents memory +exhaustion in most contexts.

+
Source

pub fn time_source(&mut self, time_source: Arc<dyn TimeSource>) -> &mut Self

Object to get current SystemTime

+

This exists to allow system time to be mocked in tests, or wherever else desired.

+

Defaults to StdSystemTime, which simply calls SystemTime::now().

+
Source§

impl ServerConfig

Source

pub fn with_single_cert( + cert_chain: Vec<CertificateDer<'static>>, + key: PrivateKeyDer<'static>, +) -> Result<Self, Error>

Create a server config with the given certificate chain to be presented to clients

+

Uses a randomized handshake token key.

+
Source§

impl ServerConfig

Source

pub fn with_crypto(crypto: Arc<dyn ServerConfig>) -> Self

Create a server config with the given crypto::ServerConfig

+

Uses a randomized handshake token key.

+

Trait Implementations§

Source§

impl Clone for ServerConfig

Source§

fn clone(&self) -> ServerConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ServerConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.ShouldTransmit.html b/pr/395/docs/iroh_quinn_proto/struct.ShouldTransmit.html new file mode 100644 index 000000000..064d98250 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.ShouldTransmit.html @@ -0,0 +1,35 @@ +ShouldTransmit in iroh_quinn_proto - Rust

ShouldTransmit

Struct ShouldTransmit 

Source
pub struct ShouldTransmit(/* private fields */);
Expand description

Indicates whether a frame needs to be transmitted

+

This type wraps around bool and uses the #[must_use] attribute in order +to prevent accidental loss of the frame transmission requirement.

+

Implementations§

Source§

impl ShouldTransmit

Source

pub fn should_transmit(self) -> bool

Returns whether a frame should be transmitted

+

Trait Implementations§

Source§

impl Clone for ShouldTransmit

Source§

fn clone(&self) -> ShouldTransmit

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ShouldTransmit

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ShouldTransmit

Source§

fn default() -> ShouldTransmit

Returns the “default value” for a type. Read more
Source§

impl PartialEq for ShouldTransmit

Source§

fn eq(&self, other: &ShouldTransmit) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for ShouldTransmit

Source§

impl Eq for ShouldTransmit

Source§

impl StructuralPartialEq for ShouldTransmit

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.StdSystemTime.html b/pr/395/docs/iroh_quinn_proto/struct.StdSystemTime.html new file mode 100644 index 000000000..d735423c6 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.StdSystemTime.html @@ -0,0 +1,26 @@ +StdSystemTime in iroh_quinn_proto - Rust

StdSystemTime

Struct StdSystemTime 

Source
pub struct StdSystemTime;
Expand description

Default implementation of TimeSource

+

Implements now by calling SystemTime::now().

+

Trait Implementations§

Source§

impl TimeSource for StdSystemTime

Source§

fn now(&self) -> SystemTime

Get SystemTime::now() or the mocked equivalent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.StreamId.html b/pr/395/docs/iroh_quinn_proto/struct.StreamId.html new file mode 100644 index 000000000..548f28393 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.StreamId.html @@ -0,0 +1,52 @@ +StreamId in iroh_quinn_proto - Rust

StreamId

Struct StreamId 

Source
pub struct StreamId(/* private fields */);
Expand description

Identifier for a stream within a particular connection

+

Implementations§

Source§

impl StreamId

Source

pub fn new(initiator: Side, dir: Dir, index: u64) -> Self

Create a new StreamId

+
Source

pub fn initiator(self) -> Side

Which side of a connection initiated the stream

+
Source

pub fn dir(self) -> Dir

Which directions data flows in

+
Source

pub fn index(self) -> u64

Distinguishes streams of the same initiator and directionality

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for StreamId

Available on crate feature arbitrary only.
Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the given unstructured data. Read more
§

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self, Error>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for StreamId

Source§

fn clone(&self) -> StreamId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for StreamId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Decodable for StreamId

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for StreamId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Encodable for StreamId

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer.
Source§

impl From<StreamId> for VarInt

Source§

fn from(x: StreamId) -> Self

Converts to this type from the input type.
Source§

impl From<StreamId> for u64

Source§

fn from(x: StreamId) -> Self

Converts to this type from the input type.
Source§

impl From<VarInt> for StreamId

Source§

fn from(v: VarInt) -> Self

Converts to this type from the input type.
Source§

impl Hash for StreamId

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for StreamId

Source§

fn cmp(&self, other: &StreamId) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for StreamId

Source§

fn eq(&self, other: &StreamId) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for StreamId

Source§

fn partial_cmp(&self, other: &StreamId) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for StreamId

Source§

impl Eq for StreamId

Source§

impl StructuralPartialEq for StreamId

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.StreamInfo.html b/pr/395/docs/iroh_quinn_proto/struct.StreamInfo.html new file mode 100644 index 000000000..b00721421 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.StreamInfo.html @@ -0,0 +1,32 @@ +StreamInfo in iroh_quinn_proto - Rust

StreamInfo

Struct StreamInfo 

Source
pub struct StreamInfo(/* private fields */);

Trait Implementations§

Source§

impl Clone for StreamInfo

Source§

fn clone(&self) -> StreamInfo

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for StreamInfo

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for StreamInfo

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for StreamInfo

Source§

fn eq(&self, other: &StreamInfo) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for StreamInfo

Source§

impl Eq for StreamInfo

Source§

impl StructuralPartialEq for StreamInfo

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.Streams.html b/pr/395/docs/iroh_quinn_proto/struct.Streams.html new file mode 100644 index 000000000..d1b3d3541 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.Streams.html @@ -0,0 +1,35 @@ +Streams in iroh_quinn_proto - Rust

Streams

Struct Streams 

Source
pub struct Streams<'a> { /* private fields */ }
Expand description

Access to streams

+

Implementations§

Source§

impl<'a> Streams<'a>

Source

pub fn open(&mut self, dir: Dir) -> Option<StreamId>

Open a single stream if possible

+

Returns None if the streams in the given direction are currently exhausted.

+
Source

pub fn accept(&mut self, dir: Dir) -> Option<StreamId>

Accept a remotely initiated stream of a certain directionality, if possible

+

Returns None if there are no new incoming streams for this connection. +Has no impact on the data flow-control or stream concurrency limits.

+
Source

pub fn send_streams(&self) -> usize

The number of streams that may have unacknowledged data.

+
Source

pub fn remote_open_streams(&self, dir: Dir) -> u64

The number of remotely initiated open streams of a certain directionality.

+

Includes remotely initiated streams, which have not been accepted via accept. +These streams count against the respective concurrency limit reported by +Connection::max_concurrent_streams.

+

Auto Trait Implementations§

§

impl<'a> Freeze for Streams<'a>

§

impl<'a> !RefUnwindSafe for Streams<'a>

§

impl<'a> Send for Streams<'a>

§

impl<'a> Sync for Streams<'a>

§

impl<'a> Unpin for Streams<'a>

§

impl<'a> !UnwindSafe for Streams<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.TokenMemoryCache.html b/pr/395/docs/iroh_quinn_proto/struct.TokenMemoryCache.html new file mode 100644 index 000000000..8cc462b6d --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.TokenMemoryCache.html @@ -0,0 +1,28 @@ +TokenMemoryCache in iroh_quinn_proto - Rust

TokenMemoryCache

Struct TokenMemoryCache 

Source
pub struct TokenMemoryCache(/* private fields */);
Expand description

TokenStore implementation that stores up to N tokens per server name for up to a +limited number of server names, in-memory

+

Implementations§

Source§

impl TokenMemoryCache

Source

pub fn new(max_server_names: u32, max_tokens_per_server: usize) -> Self

Construct empty

+

Trait Implementations§

Source§

impl Debug for TokenMemoryCache

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for TokenMemoryCache

Defaults to a maximum of 256 servers and 2 tokens per server

+
Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl TokenStore for TokenMemoryCache

Source§

fn insert(&self, server_name: &str, token: Bytes)

Potentially store a token for later one-time use Read more
Source§

fn take(&self, server_name: &str) -> Option<Bytes>

Try to find and take a token that was stored with the given server name Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.TokenReuseError.html b/pr/395/docs/iroh_quinn_proto/struct.TokenReuseError.html new file mode 100644 index 000000000..14f1677e5 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.TokenReuseError.html @@ -0,0 +1,25 @@ +TokenReuseError in iroh_quinn_proto - Rust

TokenReuseError

Struct TokenReuseError 

Source
pub struct TokenReuseError;
Expand description

Error for when a validation token may have been reused

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.Transmit.html b/pr/395/docs/iroh_quinn_proto/struct.Transmit.html new file mode 100644 index 000000000..d14fb3ee7 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.Transmit.html @@ -0,0 +1,37 @@ +Transmit in iroh_quinn_proto - Rust

Transmit

Struct Transmit 

Source
pub struct Transmit {
+    pub destination: SocketAddr,
+    pub ecn: Option<EcnCodepoint>,
+    pub size: usize,
+    pub segment_size: Option<usize>,
+    pub src_ip: Option<IpAddr>,
+}
Expand description

An outgoing packet

+

Fields§

§destination: SocketAddr

The socket this datagram should be sent to

+
§ecn: Option<EcnCodepoint>

Explicit congestion notification bits to set on the packet

+
§size: usize

Amount of data written to the caller-supplied buffer

+
§segment_size: Option<usize>

The segment size if this transmission contains multiple datagrams. +This is None if the transmit only contains a single datagram

+
§src_ip: Option<IpAddr>

Optional source IP address for the datagram

+

Trait Implementations§

Source§

impl Debug for Transmit

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.TransportConfig.html b/pr/395/docs/iroh_quinn_proto/struct.TransportConfig.html new file mode 100644 index 000000000..f001f1b13 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.TransportConfig.html @@ -0,0 +1,222 @@ +TransportConfig in iroh_quinn_proto - Rust

TransportConfig

Struct TransportConfig 

Source
pub struct TransportConfig { /* private fields */ }
Expand description

Parameters governing the core QUIC state machine

+

Default values should be suitable for most internet applications. Applications protocols which +forbid remotely-initiated streams should set max_concurrent_bidi_streams and +max_concurrent_uni_streams to zero.

+

In some cases, performance or resource requirements can be improved by tuning these values to +suit a particular application and/or network connection. In particular, data window sizes can be +tuned for a particular expected round trip time, link capacity, and memory availability. Tuning +for higher bandwidths and latencies increases worst-case memory consumption, but does not impair +performance at lower bandwidths and latencies. The default configuration is tuned for a 100Mbps +link with a 100ms round trip time.

+

Implementations§

Source§

impl TransportConfig

Source

pub fn max_concurrent_bidi_streams(&mut self, value: VarInt) -> &mut Self

Maximum number of incoming bidirectional streams that may be open concurrently

+

Must be nonzero for the peer to open any bidirectional streams.

+

Worst-case memory use is directly proportional to max_concurrent_bidi_streams * stream_receive_window, with an upper bound proportional to receive_window.

+
Source

pub fn max_concurrent_uni_streams(&mut self, value: VarInt) -> &mut Self

Variant of max_concurrent_bidi_streams affecting unidirectional streams

+
Source

pub fn max_idle_timeout(&mut self, value: Option<IdleTimeout>) -> &mut Self

Maximum duration of inactivity to accept before timing out the connection.

+

The true idle timeout is the minimum of this and the peer’s own max idle timeout. None +represents an infinite timeout. Defaults to 30 seconds.

+

WARNING: If a peer or its network path malfunctions or acts maliciously, an infinite +idle timeout can result in permanently hung futures!

+ +
let mut config = TransportConfig::default();
+
+// Set the idle timeout as `VarInt`-encoded milliseconds
+config.max_idle_timeout(Some(VarInt::from_u32(10_000).into()));
+
+// Set the idle timeout as a `Duration`
+config.max_idle_timeout(Some(Duration::from_secs(10).try_into()?));
Source

pub fn stream_receive_window(&mut self, value: VarInt) -> &mut Self

Maximum number of bytes the peer may transmit without acknowledgement on any one stream +before becoming blocked.

+

This should be set to at least the expected connection latency multiplied by the maximum +desired throughput. Setting this smaller than receive_window helps ensure that a single +stream doesn’t monopolize receive buffers, which may otherwise occur if the application +chooses not to read from a large stream for a time while still requiring data on other +streams.

+
Source

pub fn receive_window(&mut self, value: VarInt) -> &mut Self

Maximum number of bytes the peer may transmit across all streams of a connection before +becoming blocked.

+

This should be set to at least the expected connection latency multiplied by the maximum +desired throughput. Larger values can be useful to allow maximum throughput within a +stream while another is blocked.

+
Source

pub fn send_window(&mut self, value: u64) -> &mut Self

Maximum number of bytes to transmit to a peer without acknowledgment

+

Provides an upper bound on memory when communicating with peers that issue large amounts of +flow control credit. Endpoints that wish to handle large numbers of connections robustly +should take care to set this low enough to guarantee memory exhaustion does not occur if +every connection uses the entire window.

+
Source

pub fn send_fairness(&mut self, value: bool) -> &mut Self

Whether to implement fair queuing for send streams having the same priority.

+

When enabled, connections schedule data from outgoing streams having the same priority in a +round-robin fashion. When disabled, streams are scheduled in the order they are written to.

+

Note that this only affects streams with the same priority. Higher priority streams always +take precedence over lower priority streams.

+

Disabling fairness can reduce fragmentation and protocol overhead for workloads that use +many small streams.

+
Source

pub fn packet_threshold(&mut self, value: u32) -> &mut Self

Maximum reordering in packet number space before FACK style loss detection considers a +packet lost. Should not be less than 3, per RFC5681.

+
Source

pub fn time_threshold(&mut self, value: f32) -> &mut Self

Maximum reordering in time space before time based loss detection considers a packet lost, +as a factor of RTT

+
Source

pub fn initial_rtt(&mut self, value: Duration) -> &mut Self

The RTT used before an RTT sample is taken

+
Source

pub fn initial_mtu(&mut self, value: u16) -> &mut Self

The initial value to be used as the maximum UDP payload size before running MTU discovery +(see TransportConfig::mtu_discovery_config).

+

Must be at least 1200, which is the default, and known to be safe for typical internet +applications. Larger values are more efficient, but increase the risk of packet loss due to +exceeding the network path’s IP MTU. If the provided value is higher than what the network +path actually supports, packet loss will eventually trigger black hole detection and bring +it down to TransportConfig::min_mtu.

+
Source

pub fn min_mtu(&mut self, value: u16) -> &mut Self

The maximum UDP payload size guaranteed to be supported by the network.

+

Must be at least 1200, which is the default, and lower than or equal to +TransportConfig::initial_mtu.

+

Real-world MTUs can vary according to ISP, VPN, and properties of intermediate network links +outside of either endpoint’s control. Extreme care should be used when raising this value +outside of private networks where these factors are fully controlled. If the provided value +is higher than what the network path actually supports, the result will be unpredictable and +catastrophic packet loss, without a possibility of repair. Prefer +TransportConfig::initial_mtu together with +TransportConfig::mtu_discovery_config to set a maximum UDP payload size that robustly +adapts to the network.

+
Source

pub fn mtu_discovery_config( + &mut self, + value: Option<MtuDiscoveryConfig>, +) -> &mut Self

Specifies the MTU discovery config (see MtuDiscoveryConfig for details).

+

Enabled by default.

+
Source

pub fn pad_to_mtu(&mut self, value: bool) -> &mut Self

Pad UDP datagrams carrying application data to current maximum UDP payload size

+

Disabled by default. UDP datagrams containing loss probes are exempt from padding.

+

Enabling this helps mitigate traffic analysis by network observers, but it increases +bandwidth usage. Without this mitigation precise plain text size of application datagrams as +well as the total size of stream write bursts can be inferred by observers under certain +conditions. This analysis requires either an uncongested connection or application datagrams +too large to be coalesced.

+
Source

pub fn ack_frequency_config( + &mut self, + value: Option<AckFrequencyConfig>, +) -> &mut Self

Specifies the ACK frequency config (see AckFrequencyConfig for details)

+

The provided configuration will be ignored if the peer does not support the acknowledgement +frequency QUIC extension.

+

Defaults to None, which disables controlling the peer’s acknowledgement frequency. Even +if set to None, the local side still supports the acknowledgement frequency QUIC +extension and may use it in other ways.

+
Source

pub fn persistent_congestion_threshold(&mut self, value: u32) -> &mut Self

Number of consecutive PTOs after which network is considered to be experiencing persistent congestion.

+
Source

pub fn keep_alive_interval(&mut self, value: Option<Duration>) -> &mut Self

Period of inactivity before sending a keep-alive packet

+

Keep-alive packets prevent an inactive but otherwise healthy connection from timing out.

+

None to disable, which is the default. Only one side of any given connection needs keep-alive +enabled for the connection to be preserved. Must be set lower than the idle_timeout of both +peers to be effective.

+
Source

pub fn crypto_buffer_size(&mut self, value: usize) -> &mut Self

Maximum quantity of out-of-order crypto layer data to buffer

+
Source

pub fn allow_spin(&mut self, value: bool) -> &mut Self

Whether the implementation is permitted to set the spin bit on this connection

+

This allows passive observers to easily judge the round trip time of a connection, which can +be useful for network administration but sacrifices a small amount of privacy.

+
Source

pub fn datagram_receive_buffer_size( + &mut self, + value: Option<usize>, +) -> &mut Self

Maximum number of incoming application datagram bytes to buffer, or None to disable +incoming datagrams

+

The peer is forbidden to send single datagrams larger than this size. If the aggregate size +of all datagrams that have been received from the peer but not consumed by the application +exceeds this value, old datagrams are dropped until it is no longer exceeded.

+
Source

pub fn datagram_send_buffer_size(&mut self, value: usize) -> &mut Self

Maximum number of outgoing application datagram bytes to buffer

+

While datagrams are sent ASAP, it is possible for an application to generate data faster +than the link, or even the underlying hardware, can transmit them. This limits the amount of +memory that may be consumed in that case. When the send buffer is full and a new datagram is +sent, older datagrams are dropped until sufficient space is available.

+
Source

pub fn congestion_controller_factory( + &mut self, + factory: Arc<dyn ControllerFactory + Send + Sync + 'static>, +) -> &mut Self

How to construct new congestion::Controllers

+

Typically the refcounted configuration of a congestion::Controller, +e.g. a congestion::NewRenoConfig.

+
§Example
+
let mut config = TransportConfig::default();
+config.congestion_controller_factory(Arc::new(congestion::NewRenoConfig::default()));
Source

pub fn enable_segmentation_offload(&mut self, enabled: bool) -> &mut Self

Whether to use “Generic Segmentation Offload” to accelerate transmits, when supported by the +environment

+

Defaults to true.

+

GSO dramatically reduces CPU consumption when sending large numbers of packets with the same +headers, such as when transmitting bulk data on a connection. However, it is not supported +by all network interface drivers or packet inspection tools. quinn-udp will attempt to +disable GSO automatically when unavailable, but this can lead to spurious packet loss at +startup, temporarily degrading performance.

+
Source

pub fn send_observed_address_reports(&mut self, enabled: bool) -> &mut Self

Whether to send observed address reports to peers.

+

This will aid peers in inferring their reachable address, which in most NATd networks +will not be easily available to them.

+
Source

pub fn receive_observed_address_reports(&mut self, enabled: bool) -> &mut Self

Whether to receive observed address reports from other peers.

+

Peers with the address discovery extension enabled that are willing to provide observed +address reports will do so if this transport parameter is set. In general, observed address +reports cannot be trusted. This, however, can aid the current endpoint in inferring its +reachable address, which in most NATd networks will not be easily available.

+
Source

pub fn max_concurrent_multipath_paths( + &mut self, + max_concurrent: u32, +) -> &mut Self

Enables the Multipath Extension for QUIC.

+

Setting this to any nonzero value will enable the Multipath Extension for QUIC, +https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/.

+

The value provided specifies the number maximum number of paths this endpoint may open +concurrently when multipath is negotiated. For any path to be opened, the remote must +enable multipath as well.

+
Source

pub fn default_path_max_idle_timeout( + &mut self, + timeout: Option<Duration>, +) -> &mut Self

Sets a default per-path maximum idle timeout

+

If the path is idle for this long the path will be abandoned. Bear in mind this will +interact with the TransportConfig::max_idle_timeout, if the last path is +abandoned the entire connection will be closed.

+

You can also change this using Connection::set_path_max_idle_timeout for +existing paths.

+
Source

pub fn default_path_keep_alive_interval( + &mut self, + interval: Option<Duration>, +) -> &mut Self

Sets a default per-path keep alive interval

+

Note that this does not interact with the connection-wide +TransportConfig::keep_alive_interval. This setting will keep this path active, +TransportConfig::keep_alive_interval will keep the connection active, with no +control over which path is used for this.

+

You can also change this using Connection::set_path_keep_alive_interval for +existing path.

+
Source

pub fn set_max_remote_nat_traversal_addresses( + &mut self, + max_addresses: u8, +) -> &mut Self

Sets the maximum number of nat traversal addresses this endpoint allows the remote to +advertise

+

Setting this to any nonzero value will enable Iroh’s holepunching, loosely based in the Nat +Traversal Extension for QUIC, see +https://www.ietf.org/archive/id/draft-seemann-quic-nat-traversal-02.html

+

This implementation expects the multipath extension to be enabled as well. if not yet +enabled via Self::max_concurrent_multipath_paths, a default value of +DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED will be used.

+
Source

pub fn qlog_factory(&mut self, factory: Arc<dyn QlogFactory>) -> &mut Self

Configures qlog capturing by setting a QlogFactory.

+

This assigns a QlogFactory that produces qlog capture configurations for +individual connections.

+
Source

pub fn qlog_from_env(&mut self, prefix: &str) -> &mut Self

Configures qlog capturing through the QLOGDIR environment variable.

+

This uses QlogFileFactory::from_env to create a factory to write qlog traces +into the directory set through the QLOGDIR environment variable.

+

If QLOGDIR is not set, no traces will be written. If QLOGDIR is set to a path +that does not exist, it will be created.

+

The files will be prefixed with prefix.

+
Source

pub fn qlog_from_path( + &mut self, + path: impl AsRef<Path>, + prefix: &str, +) -> &mut Self

Configures qlog capturing into a directory.

+

This uses QlogFileFactory to create a factory to write qlog traces into +the specified directory. The files will be prefixed with prefix.

+

Trait Implementations§

Source§

impl Clone for TransportConfig

Source§

fn clone(&self) -> TransportConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TransportConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for TransportConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.TransportError.html b/pr/395/docs/iroh_quinn_proto/struct.TransportError.html new file mode 100644 index 000000000..863404f28 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.TransportError.html @@ -0,0 +1,45 @@ +TransportError in iroh_quinn_proto - Rust

TransportError

Struct TransportError 

Source
#[non_exhaustive]
pub struct TransportError { + pub code: Code, + pub frame: MaybeFrame, + pub reason: String, + pub crypto: Option<Arc<dyn Error + Send + Sync>>, +}
Expand description

Transport-level errors occur when a peer violates the protocol specification

+

§Note

+

The PartialEq implementation for this type performs comparison on the code field only

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§code: Code

Type of error

+
§frame: MaybeFrame

Frame type that triggered the error

+
§reason: String

Human-readable explanation of the reason

+
§crypto: Option<Arc<dyn Error + Send + Sync>>

An underlying crypto (e.g. TLS) layer error

+

Implementations§

Source§

impl Error

Source

pub fn new(code: Code, reason: String) -> Self

Construct an error with a code and a reason

+

Trait Implementations§

Source§

impl Clone for Error

Source§

fn clone(&self) -> Error

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Error

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Error

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for Error

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<Error> for ConnectionClose

Source§

fn from(x: TransportError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: TransportError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for TransportError

Source§

fn from(e: Error) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for Error

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for Error

Auto Trait Implementations§

§

impl Freeze for Error

§

impl !RefUnwindSafe for Error

§

impl Send for Error

§

impl Sync for Error

§

impl Unpin for Error

§

impl !UnwindSafe for Error

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.TransportErrorCode.html b/pr/395/docs/iroh_quinn_proto/struct.TransportErrorCode.html new file mode 100644 index 000000000..a0d9c2c2d --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.TransportErrorCode.html @@ -0,0 +1,55 @@ +TransportErrorCode in iroh_quinn_proto - Rust

TransportErrorCode

Struct TransportErrorCode 

Source
pub struct TransportErrorCode(/* private fields */);
Expand description

Transport-level error code

+

Implementations§

Source§

impl Code

Source

pub fn crypto(code: u8) -> Self

Create QUIC error code from TLS alert code

+
Source§

impl Code

Source

pub const NO_ERROR: Self

the connection is being closed abruptly in the absence of any error

+
Source

pub const INTERNAL_ERROR: Self

the endpoint encountered an internal error and cannot continue with the connection

+
Source

pub const CONNECTION_REFUSED: Self

the server refused to accept a new connection

+
Source

pub const FLOW_CONTROL_ERROR: Self

received more data than permitted in advertised data limits

+
Source

pub const STREAM_LIMIT_ERROR: Self

received a frame for a stream identifier that exceeded advertised the stream limit for the corresponding stream type

+
Source

pub const STREAM_STATE_ERROR: Self

received a frame for a stream that was not in a state that permitted that frame

+
Source

pub const FINAL_SIZE_ERROR: Self

received a STREAM frame or a RESET_STREAM frame containing a different final size to the one already established

+
Source

pub const FRAME_ENCODING_ERROR: Self

received a frame that was badly formatted

+
Source

pub const TRANSPORT_PARAMETER_ERROR: Self

received transport parameters that were badly formatted, included an invalid value, was absent even though it is mandatory, was present though it is forbidden, or is otherwise in error

+
Source

pub const CONNECTION_ID_LIMIT_ERROR: Self

the number of connection IDs provided by the peer exceeds the advertised active_connection_id_limit

+
Source

pub const PROTOCOL_VIOLATION: Self

detected an error with protocol compliance that was not covered by more specific error codes

+
Source

pub const INVALID_TOKEN: Self

received an invalid Retry Token in a client Initial

+
Source

pub const APPLICATION_ERROR: Self

the application or application protocol caused the connection to be closed during the handshake

+
Source

pub const CRYPTO_BUFFER_EXCEEDED: Self

received more data in CRYPTO frames than can be buffered

+
Source

pub const KEY_UPDATE_ERROR: Self

key update error

+
Source

pub const AEAD_LIMIT_REACHED: Self

the endpoint has reached the confidentiality or integrity limit for the AEAD algorithm

+
Source

pub const NO_VIABLE_PATH: Self

no viable network path exists

+
Source

pub const APPLICATION_ABANDON_PATH: Self

Path abandoned at the application’s request

+
Source

pub const PATH_RESOURCE_LIMIT_REACHED: Self

Path abandoned due to resource limitations in the transport

+
Source

pub const PATH_UNSTABLE_OR_POOR: Self

Path abandoned due to unstable interfaces

+
Source

pub const NO_CID_AVAILABLE_FOR_PATH: Self

Path abandoned due to no available connection IDs for the path

+

Trait Implementations§

Source§

impl Clone for Code

Source§

fn clone(&self) -> Code

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Code

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Decodable for Code

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for Code

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Encodable for Code

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer.
Source§

impl From<Code> for VarInt

Source§

fn from(value: Code) -> Self

Converts to this type from the input type.
Source§

impl From<Code> for u64

Source§

fn from(x: Code) -> Self

Converts to this type from the input type.
Source§

impl From<VarInt> for Code

Source§

fn from(value: VarInt) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for Code

Source§

fn eq(&self, other: &Code) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for Code

Source§

impl Eq for Code

Source§

impl StructuralPartialEq for Code

Auto Trait Implementations§

§

impl Freeze for Code

§

impl RefUnwindSafe for Code

§

impl Send for Code

§

impl Sync for Code

§

impl Unpin for Code

§

impl UnwindSafe for Code

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.UdpStats.html b/pr/395/docs/iroh_quinn_proto/struct.UdpStats.html new file mode 100644 index 000000000..38d268cb8 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.UdpStats.html @@ -0,0 +1,41 @@ +UdpStats in iroh_quinn_proto - Rust

UdpStats

Struct UdpStats 

Source
#[non_exhaustive]
pub struct UdpStats { + pub datagrams: u64, + pub bytes: u64, + pub ios: u64, +}
Expand description

Statistics about UDP datagrams transmitted or received on a connection

+

All QUIC packets are carried by UDP datagrams. Hence, these statistics cover all traffic on a connection.

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§datagrams: u64

The amount of UDP datagrams observed

+
§bytes: u64

The total amount of bytes which have been transferred inside UDP datagrams

+
§ios: u64

The amount of I/O operations executed

+

Can be less than datagrams when GSO, GRO, and/or batched system calls are in use.

+

Trait Implementations§

Source§

impl Clone for UdpStats

Source§

fn clone(&self) -> UdpStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for UdpStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for UdpStats

Source§

fn default() -> UdpStats

Returns the “default value” for a type. Read more
Source§

impl PartialEq for UdpStats

Source§

fn eq(&self, other: &UdpStats) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for UdpStats

Source§

impl Eq for UdpStats

Source§

impl StructuralPartialEq for UdpStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.ValidationTokenConfig.html b/pr/395/docs/iroh_quinn_proto/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..50049ad30 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.ValidationTokenConfig.html @@ -0,0 +1,74 @@ +ValidationTokenConfig in iroh_quinn_proto - Rust

ValidationTokenConfig

Struct ValidationTokenConfig 

Source
pub struct ValidationTokenConfig { /* private fields */ }
Expand description

Configuration for sending and handling validation tokens in incoming connections

+

Default values should be suitable for most internet applications.

+

§QUIC Tokens

+

The QUIC protocol defines a concept of “address validation”. Essentially, one side of a +QUIC connection may appear to be receiving QUIC packets from a particular remote UDP address, +but it will only consider that remote address “validated” once it has convincing evidence that +the address is not being spoofed.

+

Validation is important primarily because of QUIC’s “anti-amplification limit.” This limit +prevents a QUIC server from sending a client more than three times the number of bytes it has +received from the client on a given address until that address is validated. This is designed +to mitigate the ability of attackers to use QUIC-based servers as reflectors in amplification +attacks.

+

A path may become validated in several ways. The server is always considered validated by the +client. The client usually begins in an unvalidated state upon first connecting or migrating, +but then becomes validated through various mechanisms that usually take one network round trip. +However, in some cases, a client which has previously attempted to connect to a server may have +been given a one-time use cryptographically secured “token” that it can send in a subsequent +connection attempt to be validated immediately.

+

There are two ways these tokens can originate:

+
    +
  • If the server responds to an incoming connection with retry, a “retry token” is minted and +sent to the client, which the client immediately uses to attempt to connect again. Retry +tokens operate on short timescales, such as 15 seconds.
  • +
  • If a client’s path within an active connection is validated, the server may send the client +one or more “validation tokens,” which the client may store for use in later connections to +the same server. Validation tokens may be valid for much longer lifetimes than retry token.
  • +
+

The usage of validation tokens is most impactful in situations where 0-RTT data is also being +used–in particular, in situations where the server sends the client more than three times more +0.5-RTT data than it has received 0-RTT data. Since the successful completion of a connection +handshake implicitly causes the client’s address to be validated, transmission of 0.5-RTT data +is the main situation where a server might be sending application data to an address that could +be validated by token usage earlier than it would become validated without token usage.

+

These tokens should not be confused with “stateless reset tokens,” which are similarly named +but entirely unrelated.

+

Implementations§

Source§

impl ValidationTokenConfig

Source

pub fn lifetime(&mut self, value: Duration) -> &mut Self

Duration after an address validation token was issued for which it’s considered valid

+

This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.

+

Defaults to 2 weeks.

+
Source

pub fn log(&mut self, log: Arc<dyn TokenLog>) -> &mut Self

Set a custom TokenLog

+

If the bloom feature is enabled (which it is by default), defaults to a default +BloomTokenLog, which is suitable for most internet applications.

+

If the bloom feature is disabled, defaults to NoneTokenLog, +which makes the server ignore all address validation tokens (that is, tokens originating +from NEW_TOKEN frames–retry tokens are not affected).

+
Source

pub fn sent(&mut self, value: u32) -> &mut Self

Number of address validation tokens sent to a client when its path is validated

+

This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.

+

If the bloom feature is enabled (which it is by default), defaults to 2. Otherwise, +defaults to 0.

+

Trait Implementations§

Source§

impl Clone for ValidationTokenConfig

Source§

fn clone(&self) -> ValidationTokenConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ValidationTokenConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ValidationTokenConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.VarInt.html b/pr/395/docs/iroh_quinn_proto/struct.VarInt.html new file mode 100644 index 000000000..3e86d31c6 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.VarInt.html @@ -0,0 +1,62 @@ +VarInt in iroh_quinn_proto - Rust

VarInt

Struct VarInt 

Source
pub struct VarInt(/* private fields */);
Expand description

An integer less than 2^62

+

Values of this type are suitable for encoding as QUIC variable-length integer.

+

Implementations§

Source§

impl VarInt

Source

pub const MAX: Self

The largest representable value

+
Source

pub const MAX_SIZE: usize = 8usize

The largest encoded value length

+
Source

pub const fn from_u32(x: u32) -> Self

Construct a VarInt infallibly

+
Source

pub fn from_u64(x: u64) -> Result<Self, VarIntBoundsExceeded>

Succeeds iff x < 2^62

+
Source

pub const unsafe fn from_u64_unchecked(x: u64) -> Self

Create a VarInt without ensuring it’s in range

+
§Safety
+

x must be less than 2^62.

+
Source

pub const fn into_inner(self) -> u64

Extract the integer value

+
Source

pub fn saturating_add(self, rhs: impl Into<Self>) -> Self

Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead +of overflowing.

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for VarInt

Available on crate feature arbitrary only.
Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the given unstructured data. Read more
§

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self, Error>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for VarInt

Source§

fn clone(&self) -> VarInt

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for VarInt

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Decodable for VarInt

Source§

fn decode<B: Buf>(r: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Default for VarInt

Source§

fn default() -> VarInt

Returns the “default value” for a type. Read more
Source§

impl Display for VarInt

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Encodable for VarInt

Source§

fn encode<B: BufMut>(&self, w: &mut B)

Append the encoding of self to the provided buffer.
Source§

impl From<Code> for VarInt

Source§

fn from(value: Code) -> Self

Converts to this type from the input type.
Source§

impl From<StreamId> for VarInt

Source§

fn from(x: StreamId) -> Self

Converts to this type from the input type.
Source§

impl From<VarInt> for Code

Source§

fn from(value: VarInt) -> Self

Converts to this type from the input type.
Source§

impl From<VarInt> for IdleTimeout

Source§

fn from(inner: VarInt) -> Self

Converts to this type from the input type.
Source§

impl From<VarInt> for StreamId

Source§

fn from(v: VarInt) -> Self

Converts to this type from the input type.
Source§

impl From<VarInt> for u64

Source§

fn from(x: VarInt) -> Self

Converts to this type from the input type.
Source§

impl From<u16> for VarInt

Source§

fn from(x: u16) -> Self

Converts to this type from the input type.
Source§

impl From<u32> for VarInt

Source§

fn from(x: u32) -> Self

Converts to this type from the input type.
Source§

impl From<u8> for VarInt

Source§

fn from(x: u8) -> Self

Converts to this type from the input type.
Source§

impl Hash for VarInt

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for VarInt

Source§

fn cmp(&self, other: &VarInt) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for VarInt

Source§

fn eq(&self, other: &VarInt) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for VarInt

Source§

fn partial_cmp(&self, other: &VarInt) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl TryFrom<u128> for VarInt

Source§

fn try_from(x: u128) -> Result<Self, VarIntBoundsExceeded>

Succeeds iff x < 2^62

+
Source§

type Error = VarIntBoundsExceeded

The type returned in the event of a conversion error.
Source§

impl TryFrom<u64> for VarInt

Source§

fn try_from(x: u64) -> Result<Self, VarIntBoundsExceeded>

Succeeds iff x < 2^62

+
Source§

type Error = VarIntBoundsExceeded

The type returned in the event of a conversion error.
Source§

impl TryFrom<usize> for VarInt

Source§

fn try_from(x: usize) -> Result<Self, VarIntBoundsExceeded>

Succeeds iff x < 2^62

+
Source§

type Error = VarIntBoundsExceeded

The type returned in the event of a conversion error.
Source§

impl Copy for VarInt

Source§

impl Eq for VarInt

Source§

impl StructuralPartialEq for VarInt

Auto Trait Implementations§

§

impl Freeze for VarInt

§

impl RefUnwindSafe for VarInt

§

impl Send for VarInt

§

impl Sync for VarInt

§

impl Unpin for VarInt

§

impl UnwindSafe for VarInt

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.VarIntBoundsExceeded.html b/pr/395/docs/iroh_quinn_proto/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..0dfc8e7d0 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.VarIntBoundsExceeded.html @@ -0,0 +1,33 @@ +VarIntBoundsExceeded in iroh_quinn_proto - Rust

VarIntBoundsExceeded

Struct VarIntBoundsExceeded 

Source
pub struct VarIntBoundsExceeded;
Expand description

Error returned when constructing a VarInt from a value >= 2^62

+

Trait Implementations§

Source§

impl Clone for VarIntBoundsExceeded

Source§

fn clone(&self) -> VarIntBoundsExceeded

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for VarIntBoundsExceeded

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for VarIntBoundsExceeded

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for VarIntBoundsExceeded

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<VarIntBoundsExceeded> for ConfigError

Source§

fn from(_: VarIntBoundsExceeded) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for VarIntBoundsExceeded

Source§

fn eq(&self, other: &VarIntBoundsExceeded) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for VarIntBoundsExceeded

Source§

impl Eq for VarIntBoundsExceeded

Source§

impl StructuralPartialEq for VarIntBoundsExceeded

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/struct.Written.html b/pr/395/docs/iroh_quinn_proto/struct.Written.html new file mode 100644 index 000000000..1e51245e8 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/struct.Written.html @@ -0,0 +1,38 @@ +Written in iroh_quinn_proto - Rust

Written

Struct Written 

Source
pub struct Written {
+    pub bytes: usize,
+    pub chunks: usize,
+}
Expand description

Indicates how many bytes and chunks had been transferred in a write operation

+

Fields§

§bytes: usize

The amount of bytes which had been written

+
§chunks: usize

The amount of full chunks which had been written

+

If a chunk was only partially written, it will not be counted by this field.

+

Trait Implementations§

Source§

impl Clone for Written

Source§

fn clone(&self) -> Written

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Written

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Written

Source§

fn default() -> Written

Returns the “default value” for a type. Read more
Source§

impl PartialEq for Written

Source§

fn eq(&self, other: &Written) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for Written

Source§

impl Eq for Written

Source§

impl StructuralPartialEq for Written

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/token/struct.NoneTokenLog.html b/pr/395/docs/iroh_quinn_proto/token/struct.NoneTokenLog.html new file mode 100644 index 000000000..8b2bbfeeb --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/token/struct.NoneTokenLog.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.NoneTokenLog.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/token/struct.NoneTokenStore.html b/pr/395/docs/iroh_quinn_proto/token/struct.NoneTokenStore.html new file mode 100644 index 000000000..db4ca3380 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/token/struct.NoneTokenStore.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.NoneTokenStore.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/token/struct.TokenReuseError.html b/pr/395/docs/iroh_quinn_proto/token/struct.TokenReuseError.html new file mode 100644 index 000000000..9439eab73 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/token/struct.TokenReuseError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.TokenReuseError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/token/trait.TokenLog.html b/pr/395/docs/iroh_quinn_proto/token/trait.TokenLog.html new file mode 100644 index 000000000..f296a9087 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/token/trait.TokenLog.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/trait.TokenLog.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/token/trait.TokenStore.html b/pr/395/docs/iroh_quinn_proto/token/trait.TokenStore.html new file mode 100644 index 000000000..a8e1acc4d --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/token/trait.TokenStore.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/trait.TokenStore.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/token_memory_cache/struct.TokenMemoryCache.html b/pr/395/docs/iroh_quinn_proto/token_memory_cache/struct.TokenMemoryCache.html new file mode 100644 index 000000000..339118bbf --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/token_memory_cache/struct.TokenMemoryCache.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.TokenMemoryCache.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/trait.ConnectionIdGenerator.html b/pr/395/docs/iroh_quinn_proto/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..39adf3b6b --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/trait.ConnectionIdGenerator.html @@ -0,0 +1,21 @@ +ConnectionIdGenerator in iroh_quinn_proto - Rust

ConnectionIdGenerator

Trait ConnectionIdGenerator 

Source
pub trait ConnectionIdGenerator: Send + Sync {
+    // Required methods
+    fn generate_cid(&mut self) -> ConnectionId;
+    fn cid_len(&self) -> usize;
+    fn cid_lifetime(&self) -> Option<Duration>;
+
+    // Provided method
+    fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid> { ... }
+}
Expand description

Generates connection IDs for incoming connections

+

Required Methods§

Source

fn generate_cid(&mut self) -> ConnectionId

Generates a new CID

+

Connection IDs MUST NOT contain any information that can be used by +an external observer (that is, one that does not cooperate with the +issuer) to correlate them with other connection IDs for the same +connection. They MUST have high entropy, e.g. due to encrypted data +or cryptographic-grade random data.

+
Source

fn cid_len(&self) -> usize

Returns the length of a CID for connections created by this generator

+
Source

fn cid_lifetime(&self) -> Option<Duration>

Returns the lifetime of generated Connection IDs

+

Connection IDs will be retired after the returned Duration, if any. Assumed to be constant.

+

Provided Methods§

Source

fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid>

Quickly determine whether cid could have been generated by this generator

+

False positives are permitted, but increase the cost of handling invalid packets.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/trait.ConnectionIdParser.html b/pr/395/docs/iroh_quinn_proto/trait.ConnectionIdParser.html new file mode 100644 index 000000000..fb43b54db --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/trait.ConnectionIdParser.html @@ -0,0 +1,9 @@ +ConnectionIdParser in iroh_quinn_proto - Rust

ConnectionIdParser

Trait ConnectionIdParser 

Source
pub trait ConnectionIdParser {
+    // Required method
+    fn parse(
+        &self,
+        buf: &mut dyn Buf,
+    ) -> Result<ConnectionId, PacketDecodeError>;
+}
Expand description

Parse connection id in short header packet

+

Required Methods§

Source

fn parse(&self, buf: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError>

Parse a connection id from given buffer

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/trait.QlogFactory.html b/pr/395/docs/iroh_quinn_proto/trait.QlogFactory.html new file mode 100644 index 000000000..b2ce90b98 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/trait.QlogFactory.html @@ -0,0 +1,23 @@ +QlogFactory in iroh_quinn_proto - Rust

QlogFactory

Trait QlogFactory 

Source
pub trait QlogFactory:
+    Send
+    + Sync
+    + 'static {
+    // Required method
+    fn for_connection(
+        &self,
+        side: Side,
+        remote: SocketAddr,
+        initial_dst_cid: ConnectionId,
+        now: Instant,
+    ) -> Option<QlogConfig>;
+}
Expand description

Constructs a QlogConfig for individual connections.

+

This is set via TransportConfig::qlog_factory.

+

Required Methods§

Source

fn for_connection( + &self, + side: Side, + remote: SocketAddr, + initial_dst_cid: ConnectionId, + now: Instant, +) -> Option<QlogConfig>

Returns a QlogConfig for a connection, if logging should be enabled.

+

If None is returned, qlog capture is disabled for the connection.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/trait.TimeSource.html b/pr/395/docs/iroh_quinn_proto/trait.TimeSource.html new file mode 100644 index 000000000..71f05ca10 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/trait.TimeSource.html @@ -0,0 +1,7 @@ +TimeSource in iroh_quinn_proto - Rust

TimeSource

Trait TimeSource 

Source
pub trait TimeSource: Send + Sync {
+    // Required method
+    fn now(&self) -> SystemTime;
+}
Expand description

Object to get current SystemTime

+

This exists to allow system time to be mocked in tests, or wherever else desired.

+

Required Methods§

Source

fn now(&self) -> SystemTime

Get SystemTime::now() or the mocked equivalent

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/trait.TokenLog.html b/pr/395/docs/iroh_quinn_proto/trait.TokenLog.html new file mode 100644 index 000000000..ae35feedf --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/trait.TokenLog.html @@ -0,0 +1,45 @@ +TokenLog in iroh_quinn_proto - Rust

TokenLog

Trait TokenLog 

Source
pub trait TokenLog: Send + Sync {
+    // Required method
+    fn check_and_insert(
+        &self,
+        nonce: u128,
+        issued: SystemTime,
+        lifetime: Duration,
+    ) -> Result<(), TokenReuseError>;
+}
Expand description

Responsible for limiting clients’ ability to reuse validation tokens

+

RFC 9000 § 8.1.4:

+
+

Attackers could replay tokens to use servers as amplifiers in DDoS attacks. To protect +against such attacks, servers MUST ensure that replay of tokens is prevented or limited. +Servers SHOULD ensure that tokens sent in Retry packets are only accepted for a short time, +as they are returned immediately by clients. Tokens that are provided in NEW_TOKEN frames +(Section 19.7) need to be valid for longer but SHOULD NOT be accepted multiple times. +Servers are encouraged to allow tokens to be used only once, if possible; tokens MAY include +additional information about clients to further narrow applicability or reuse.

+
+

TokenLog pertains only to tokens provided in NEW_TOKEN frames.

+

Required Methods§

Source

fn check_and_insert( + &self, + nonce: u128, + issued: SystemTime, + lifetime: Duration, +) -> Result<(), TokenReuseError>

Record that the token was used and, ideally, return a token reuse error if the token may +have been already used previously

+

False negatives and false positives are both permissible. Called when a client uses an +address validation token.

+

Parameters:

+
    +
  • nonce: A server-generated random unique value for the token.
  • +
  • issued: The time the server issued the token.
  • +
  • lifetime: The expiration time of address validation tokens sent via NEW_TOKEN frames, +as configured by ServerValidationTokenConfig::lifetime.
  • +
+
§Security & Performance
+

To the extent that it is possible to repeatedly trigger false negatives (returning Ok for +a token which has been reused), an attacker could use the server to perform amplification +attacks. The QUIC specification requires that this be limited, if not prevented fully.

+

A false positive (returning Err for a token which has never been used) is not a security +vulnerability; it is permissible for a TokenLog to always return Err. A false positive +causes the token to be ignored, which may cause the transmission of some 0.5-RTT data to be +delayed until the handshake completes, if a sufficient amount of 0.5-RTT data it sent.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/trait.TokenStore.html b/pr/395/docs/iroh_quinn_proto/trait.TokenStore.html new file mode 100644 index 000000000..68452d6fd --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/trait.TokenStore.html @@ -0,0 +1,13 @@ +TokenStore in iroh_quinn_proto - Rust

TokenStore

Trait TokenStore 

Source
pub trait TokenStore: Send + Sync {
+    // Required methods
+    fn insert(&self, server_name: &str, token: Bytes);
+    fn take(&self, server_name: &str) -> Option<Bytes>;
+}
Expand description

Responsible for storing validation tokens received from servers and retrieving them for use in +subsequent connections

+

Required Methods§

Source

fn insert(&self, server_name: &str, token: Bytes)

Potentially store a token for later one-time use

+

Called when a NEW_TOKEN frame is received from the server.

+
Source

fn take(&self, server_name: &str) -> Option<Bytes>

Try to find and take a token that was stored with the given server name

+

The same token must never be returned from take twice, as doing so can be used to +de-anonymize a client’s traffic.

+

Called when trying to connect to a server. It is always ok for this to return None.

+

Implementors§

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/transport_error/struct.Code.html b/pr/395/docs/iroh_quinn_proto/transport_error/struct.Code.html new file mode 100644 index 000000000..402d12b55 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/transport_error/struct.Code.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.TransportErrorCode.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/transport_error/struct.Error.html b/pr/395/docs/iroh_quinn_proto/transport_error/struct.Error.html new file mode 100644 index 000000000..a6bb9ca48 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/transport_error/struct.Error.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.TransportError.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/transport_parameters/enum.Error.html b/pr/395/docs/iroh_quinn_proto/transport_parameters/enum.Error.html new file mode 100644 index 000000000..f6af8f7a6 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/transport_parameters/enum.Error.html @@ -0,0 +1,38 @@ +Error in iroh_quinn_proto::transport_parameters - Rust

Error

Enum Error 

Source
pub enum Error {
+    IllegalValue,
+    Malformed,
+}
Expand description

Errors encountered while decoding TransportParameters

+

Variants§

§

IllegalValue

Parameters that are semantically invalid

+
§

Malformed

Catch-all error for problems while decoding transport parameters

+

Trait Implementations§

Source§

impl Clone for Error

Source§

fn clone(&self) -> Error

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Error

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Error

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for Error

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<Error> for TransportError

Source§

fn from(e: Error) -> Self

Converts to this type from the input type.
Source§

impl From<UnexpectedEnd> for Error

Source§

fn from(_: UnexpectedEnd) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for Error

Source§

fn eq(&self, other: &Error) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for Error

Source§

impl Eq for Error

Source§

impl StructuralPartialEq for Error

Auto Trait Implementations§

§

impl Freeze for Error

§

impl RefUnwindSafe for Error

§

impl Send for Error

§

impl Sync for Error

§

impl Unpin for Error

§

impl UnwindSafe for Error

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/transport_parameters/index.html b/pr/395/docs/iroh_quinn_proto/transport_parameters/index.html new file mode 100644 index 000000000..2f4de1262 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/transport_parameters/index.html @@ -0,0 +1,7 @@ +iroh_quinn_proto::transport_parameters - Rust

Module transport_parameters

Module transport_parameters 

Source
Expand description

QUIC connection transport parameters

+

The TransportParameters type is used to represent the transport parameters +negotiated by peers while establishing a QUIC connection. This process +happens as part of the establishment of the TLS session. As such, the types +contained in this modules should generally only be referred to by custom +implementations of the crypto::Session trait.

+

Structs§

TransportParameters
Transport parameters used to negotiate connection-level preferences between peers

Enums§

Error
Errors encountered while decoding TransportParameters
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/transport_parameters/sidebar-items.js b/pr/395/docs/iroh_quinn_proto/transport_parameters/sidebar-items.js new file mode 100644 index 000000000..be0904c68 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/transport_parameters/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Error"],"struct":["TransportParameters"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/transport_parameters/struct.TransportParameters.html b/pr/395/docs/iroh_quinn_proto/transport_parameters/struct.TransportParameters.html new file mode 100644 index 000000000..e4dafa7fb --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/transport_parameters/struct.TransportParameters.html @@ -0,0 +1,38 @@ +TransportParameters in iroh_quinn_proto::transport_parameters - Rust

TransportParameters

Struct TransportParameters 

Source
pub struct TransportParameters {
+    pub max_remote_nat_traversal_addresses: Option<NonZeroU8>,
+    /* private fields */
+}
Expand description

Transport parameters used to negotiate connection-level preferences between peers

+

Fields§

§max_remote_nat_traversal_addresses: Option<NonZeroU8>

Nat traversal draft

+

Implementations§

Source§

impl TransportParameters

Source

pub fn write<W: BufMut>(&self, w: &mut W)

Encode TransportParameters into buffer

+
Source

pub fn read<R: Buf>(side: Side, r: &mut R) -> Result<Self, Error>

Decode TransportParameters from buffer

+

Trait Implementations§

Source§

impl Clone for TransportParameters

Source§

fn clone(&self) -> TransportParameters

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TransportParameters

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for TransportParameters

Source§

fn eq(&self, other: &TransportParameters) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for TransportParameters

Source§

impl Eq for TransportParameters

Source§

impl StructuralPartialEq for TransportParameters

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/varint/struct.VarInt.html b/pr/395/docs/iroh_quinn_proto/varint/struct.VarInt.html new file mode 100644 index 000000000..678acb585 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/varint/struct.VarInt.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.VarInt.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_proto/varint/struct.VarIntBoundsExceeded.html b/pr/395/docs/iroh_quinn_proto/varint/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..95add4901 --- /dev/null +++ b/pr/395/docs/iroh_quinn_proto/varint/struct.VarIntBoundsExceeded.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.VarIntBoundsExceeded.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_udp/all.html b/pr/395/docs/iroh_quinn_udp/all.html new file mode 100644 index 000000000..0b8c27cea --- /dev/null +++ b/pr/395/docs/iroh_quinn_udp/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_udp/constant.BATCH_SIZE.html b/pr/395/docs/iroh_quinn_udp/constant.BATCH_SIZE.html new file mode 100644 index 000000000..73cf5cb79 --- /dev/null +++ b/pr/395/docs/iroh_quinn_udp/constant.BATCH_SIZE.html @@ -0,0 +1,2 @@ +BATCH_SIZE in iroh_quinn_udp - Rust

BATCH_SIZE

Constant BATCH_SIZE 

Source
pub const BATCH_SIZE: usize = imp::BATCH_SIZE; // 32usize
Expand description

Number of UDP packets to send/receive at a time

+
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_udp/enum.EcnCodepoint.html b/pr/395/docs/iroh_quinn_udp/enum.EcnCodepoint.html new file mode 100644 index 000000000..59328a477 --- /dev/null +++ b/pr/395/docs/iroh_quinn_udp/enum.EcnCodepoint.html @@ -0,0 +1,28 @@ +EcnCodepoint in iroh_quinn_udp - Rust

EcnCodepoint

Enum EcnCodepoint 

Source
#[repr(u8)]
pub enum EcnCodepoint { + Ect0 = 2, + Ect1 = 1, + Ce = 3, +}
Expand description

Explicit congestion notification codepoint

+

Variants§

§

Ect0 = 2

The ECT(0) codepoint, indicating that an endpoint is ECN-capable

+
§

Ect1 = 1

The ECT(1) codepoint, indicating that an endpoint is ECN-capable

+
§

Ce = 3

The CE codepoint, signalling that congestion was experienced

+

Implementations§

Source§

impl EcnCodepoint

Source

pub fn from_bits(x: u8) -> Option<Self>

Create new object from the given bits

+

Trait Implementations§

Source§

impl Clone for EcnCodepoint

Source§

fn clone(&self) -> EcnCodepoint

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EcnCodepoint

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for EcnCodepoint

Source§

fn eq(&self, other: &EcnCodepoint) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for EcnCodepoint

Source§

impl Eq for EcnCodepoint

Source§

impl StructuralPartialEq for EcnCodepoint

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_udp/imp/struct.UdpSocketState.html b/pr/395/docs/iroh_quinn_udp/imp/struct.UdpSocketState.html new file mode 100644 index 000000000..ef7f29570 --- /dev/null +++ b/pr/395/docs/iroh_quinn_udp/imp/struct.UdpSocketState.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_udp/struct.UdpSocketState.html...

+ + + \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_udp/index.html b/pr/395/docs/iroh_quinn_udp/index.html new file mode 100644 index 000000000..c07f7349a --- /dev/null +++ b/pr/395/docs/iroh_quinn_udp/index.html @@ -0,0 +1,22 @@ +iroh_quinn_udp - Rust

Crate iroh_quinn_udp

Crate iroh_quinn_udp 

Source
Expand description

Uniform interface to send and receive UDP packets with advanced features useful for QUIC

+

This crate exposes kernel UDP stack features available on most modern systems which are required +for an efficient and conformant QUIC implementation. As of this writing, these are not available +in std or major async runtimes, and their niche character and complexity are a barrier to adding +them. Hence, a dedicated crate.

+

Exposed features include:

+
    +
  • Segmentation offload for bulk send and receive operations, reducing CPU load.
  • +
  • Reporting the exact destination address of received packets and specifying explicit source +addresses for sent packets, allowing responses to be sent from the address that the peer +expects when there are multiple possibilities. This is common when bound to a wildcard address +in IPv6 due to RFC 8981 temporary addresses.
  • +
  • Explicit Congestion Notification, which is required by QUIC to prevent packet loss and reduce +latency on congested links when supported by the network path.
  • +
  • Disabled IP-layer fragmentation, which allows the true physical MTU to be detected and reduces +risk of QUIC packet loss.
  • +
+

Some features are unavailable in some environments. This can be due to an outdated operating +system or drivers. Some operating systems may not implement desired features at all, or may not +yet be supported by the crate. When support is unavailable, functionality will gracefully +degrade.

+

Structs§

RecvMeta
Metadata for a single buffer filled with bytes received from the network
Transmit
An outgoing packet
UdpSockRef
A borrowed UDP socket
UdpSocketState
Tokio-compatible UDP socket with some useful specializations.

Enums§

EcnCodepoint
Explicit congestion notification codepoint

Constants§

BATCH_SIZE
Number of UDP packets to send/receive at a time
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_udp/sidebar-items.js b/pr/395/docs/iroh_quinn_udp/sidebar-items.js new file mode 100644 index 000000000..d1db6b942 --- /dev/null +++ b/pr/395/docs/iroh_quinn_udp/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"constant":["BATCH_SIZE"],"enum":["EcnCodepoint"],"struct":["RecvMeta","Transmit","UdpSockRef","UdpSocketState"]}; \ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_udp/struct.RecvMeta.html b/pr/395/docs/iroh_quinn_udp/struct.RecvMeta.html new file mode 100644 index 000000000..3b50cf93d --- /dev/null +++ b/pr/395/docs/iroh_quinn_udp/struct.RecvMeta.html @@ -0,0 +1,41 @@ +RecvMeta in iroh_quinn_udp - Rust

RecvMeta

Struct RecvMeta 

Source
#[non_exhaustive]
pub struct RecvMeta { + pub addr: SocketAddr, + pub len: usize, + pub stride: usize, + pub ecn: Option<EcnCodepoint>, + pub dst_ip: Option<IpAddr>, + pub interface_index: Option<u32>, +}
Expand description

Metadata for a single buffer filled with bytes received from the network

+

This associated buffer can contain one or more datagrams, see stride.

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§addr: SocketAddr

The source address of the datagram(s) contained in the buffer

+
§len: usize

The number of bytes the associated buffer has

+
§stride: usize

The size of a single datagram in the associated buffer

+

When GRO (Generic Receive Offload) is used this indicates the size of a single +datagram inside the buffer. If the buffer is larger, that is if len is greater +then this value, then the individual datagrams contained have their boundaries at +stride increments from the start. The last datagram could be smaller than +stride.

+
§ecn: Option<EcnCodepoint>

The Explicit Congestion Notification bits for the datagram(s) in the buffer

+
§dst_ip: Option<IpAddr>

The destination IP address which was encoded in this datagram

+

Populated on platforms: Windows, Linux, Android (API level > 25), +FreeBSD, OpenBSD, NetBSD, macOS, and iOS.

+
§interface_index: Option<u32>

The interface index of the interface on which the datagram was received

+

Trait Implementations§

Source§

impl Clone for RecvMeta

Source§

fn clone(&self) -> RecvMeta

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for RecvMeta

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for RecvMeta

Source§

fn default() -> Self

Constructs a value with arbitrary fields, intended to be overwritten

+
Source§

impl Copy for RecvMeta

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_udp/struct.Transmit.html b/pr/395/docs/iroh_quinn_udp/struct.Transmit.html new file mode 100644 index 000000000..14b719ca8 --- /dev/null +++ b/pr/395/docs/iroh_quinn_udp/struct.Transmit.html @@ -0,0 +1,31 @@ +Transmit in iroh_quinn_udp - Rust

Transmit

Struct Transmit 

Source
pub struct Transmit<'a> {
+    pub destination: SocketAddr,
+    pub ecn: Option<EcnCodepoint>,
+    pub contents: &'a [u8],
+    pub segment_size: Option<usize>,
+    pub src_ip: Option<IpAddr>,
+}
Expand description

An outgoing packet

+

Fields§

§destination: SocketAddr

The socket this datagram should be sent to

+
§ecn: Option<EcnCodepoint>

Explicit congestion notification bits to set on the packet

+
§contents: &'a [u8]

Contents of the datagram

+
§segment_size: Option<usize>

The segment size if this transmission contains multiple datagrams. +This is None if the transmit only contains a single datagram

+
§src_ip: Option<IpAddr>

Optional source IP address for the datagram

+

Trait Implementations§

Source§

impl<'a> Clone for Transmit<'a>

Source§

fn clone(&self) -> Transmit<'a>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<'a> Debug for Transmit<'a>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'a> Freeze for Transmit<'a>

§

impl<'a> RefUnwindSafe for Transmit<'a>

§

impl<'a> Send for Transmit<'a>

§

impl<'a> Sync for Transmit<'a>

§

impl<'a> Unpin for Transmit<'a>

§

impl<'a> UnwindSafe for Transmit<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_udp/struct.UdpSockRef.html b/pr/395/docs/iroh_quinn_udp/struct.UdpSockRef.html new file mode 100644 index 000000000..2d24cad04 --- /dev/null +++ b/pr/395/docs/iroh_quinn_udp/struct.UdpSockRef.html @@ -0,0 +1,19 @@ +UdpSockRef in iroh_quinn_udp - Rust

UdpSockRef

Struct UdpSockRef 

Source
pub struct UdpSockRef<'a>(/* private fields */);
Expand description

A borrowed UDP socket

+

On Unix, constructible via From<T: AsFd>. On Windows, constructible via From<T: AsSocket>.

+

Trait Implementations§

Source§

impl<'s, S> From<&'s S> for UdpSockRef<'s>
where + S: AsFd,

Available on Unix only.
Source§

fn from(socket: &'s S) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl<'a> Freeze for UdpSockRef<'a>

§

impl<'a> RefUnwindSafe for UdpSockRef<'a>

§

impl<'a> Send for UdpSockRef<'a>

§

impl<'a> Sync for UdpSockRef<'a>

§

impl<'a> Unpin for UdpSockRef<'a>

§

impl<'a> UnwindSafe for UdpSockRef<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/iroh_quinn_udp/struct.UdpSocketState.html b/pr/395/docs/iroh_quinn_udp/struct.UdpSocketState.html new file mode 100644 index 000000000..bcb20e232 --- /dev/null +++ b/pr/395/docs/iroh_quinn_udp/struct.UdpSocketState.html @@ -0,0 +1,62 @@ +UdpSocketState in iroh_quinn_udp - Rust

UdpSocketState

Struct UdpSocketState 

Source
pub struct UdpSocketState { /* private fields */ }
Expand description

Tokio-compatible UDP socket with some useful specializations.

+

Unlike a standard tokio UDP socket, this allows ECN bits to be read and written on some +platforms.

+

Implementations§

Source§

impl UdpSocketState

Source

pub fn new(sock: UdpSockRef<'_>) -> Result<Self>

Source

pub fn send( + &self, + socket: UdpSockRef<'_>, + transmit: &Transmit<'_>, +) -> Result<()>

Sends a Transmit on the given socket.

+

This function will only ever return errors of kind io::ErrorKind::WouldBlock. +All other errors will be logged and converted to Ok.

+

UDP transmission errors are considered non-fatal because higher-level protocols must +employ retransmits and timeouts anyway in order to deal with UDP’s unreliable nature. +Thus, logging is most likely the only thing you can do with these errors.

+

If you would like to handle these errors yourself, use UdpSocketState::try_send +instead.

+
Source

pub fn try_send( + &self, + socket: UdpSockRef<'_>, + transmit: &Transmit<'_>, +) -> Result<()>

Sends a Transmit on the given socket without any additional error handling.

+
Source

pub fn recv( + &self, + socket: UdpSockRef<'_>, + bufs: &mut [IoSliceMut<'_>], + meta: &mut [RecvMeta], +) -> Result<usize>

Source

pub fn max_gso_segments(&self) -> NonZeroUsize

The maximum amount of segments which can be transmitted if a platform +supports Generic Send Offload (GSO).

+

This is 1 if the platform doesn’t support GSO. Subject to change if errors are detected +while using GSO.

+
Source

pub fn gro_segments(&self) -> NonZeroUsize

The number of segments to read when GRO is enabled. Used as a factor to +compute the receive buffer size.

+

Returns 1 if the platform doesn’t support GRO.

+
Source

pub fn set_send_buffer_size( + &self, + socket: UdpSockRef<'_>, + bytes: usize, +) -> Result<()>

Resize the send buffer of socket to bytes

+
Source

pub fn set_recv_buffer_size( + &self, + socket: UdpSockRef<'_>, + bytes: usize, +) -> Result<()>

Resize the receive buffer of socket to bytes

+
Source

pub fn send_buffer_size(&self, socket: UdpSockRef<'_>) -> Result<usize>

Get the size of the socket send buffer

+
Source

pub fn recv_buffer_size(&self, socket: UdpSockRef<'_>) -> Result<usize>

Get the size of the socket receive buffer

+
Source

pub fn may_fragment(&self) -> bool

Whether transmitted datagrams might get fragmented by the IP layer

+

Returns false on targets which employ e.g. the IPV6_DONTFRAG socket option.

+

Trait Implementations§

Source§

impl Debug for UdpSocketState

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/perf/all.html b/pr/395/docs/perf/all.html new file mode 100644 index 000000000..e395c4e55 --- /dev/null +++ b/pr/395/docs/perf/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

\ No newline at end of file diff --git a/pr/395/docs/perf/client/fn.run.html b/pr/395/docs/perf/client/fn.run.html new file mode 100644 index 000000000..4e3af1a2f --- /dev/null +++ b/pr/395/docs/perf/client/fn.run.html @@ -0,0 +1 @@ +run in perf::client - Rust

run

Function run 

Source
pub async fn run(opt: Opt) -> Result<()>
\ No newline at end of file diff --git a/pr/395/docs/perf/client/index.html b/pr/395/docs/perf/client/index.html new file mode 100644 index 000000000..35b1a32ed --- /dev/null +++ b/pr/395/docs/perf/client/index.html @@ -0,0 +1 @@ +perf::client - Rust

Module client

Module client 

Source

Structs§

Opt
Connects to a QUIC perf server and maintains a specified pattern of requests until interrupted

Functions§

run
\ No newline at end of file diff --git a/pr/395/docs/perf/client/sidebar-items.js b/pr/395/docs/perf/client/sidebar-items.js new file mode 100644 index 000000000..3dceadfac --- /dev/null +++ b/pr/395/docs/perf/client/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["run"],"struct":["Opt"]}; \ No newline at end of file diff --git a/pr/395/docs/perf/client/struct.Opt.html b/pr/395/docs/perf/client/struct.Opt.html new file mode 100644 index 000000000..d8ae2462b --- /dev/null +++ b/pr/395/docs/perf/client/struct.Opt.html @@ -0,0 +1,43 @@ +Opt in perf::client - Rust

Opt

Struct Opt 

Source
pub struct Opt { /* private fields */ }
Expand description

Connects to a QUIC perf server and maintains a specified pattern of requests until interrupted

+

Trait Implementations§

Source§

impl Args for Opt

Source§

fn group_id() -> Option<Id>

Report the [ArgGroup::id][crate::ArgGroup::id] for this set of arguments
Source§

fn augment_args<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate Self via +[FromArgMatches::from_arg_matches_mut] Read more
Source§

fn augment_args_for_update<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate self via +[FromArgMatches::update_from_arg_matches_mut] Read more
Source§

impl CommandFactory for Opt

Source§

fn command<'b>() -> Command

Build a [Command] that can instantiate Self. Read more
Source§

fn command_for_update<'b>() -> Command

Build a [Command] that can update self. Read more
Source§

impl FromArgMatches for Opt

Source§

fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn from_arg_matches_mut( + __clap_arg_matches: &mut ArgMatches, +) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn update_from_arg_matches( + &mut self, + __clap_arg_matches: &ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

fn update_from_arg_matches_mut( + &mut self, + __clap_arg_matches: &mut ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

impl Parser for Opt

§

fn parse() -> Self

Parse from std::env::args_os(), [exit][Error::exit] on error.
§

fn try_parse() -> Result<Self, Error>

Parse from std::env::args_os(), return Err on error.
§

fn parse_from<I, T>(itr: I) -> Self
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, [exit][Error::exit] on error.
§

fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, return Err on error.
§

fn update_from<I, T>(&mut self, itr: I)
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, [exit][Error::exit] on error. Read more
§

fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, return Err on error.

Auto Trait Implementations§

§

impl Freeze for Opt

§

impl RefUnwindSafe for Opt

§

impl Send for Opt

§

impl Sync for Opt

§

impl Unpin for Opt

§

impl UnwindSafe for Opt

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/perf/enum.CongestionAlgorithm.html b/pr/395/docs/perf/enum.CongestionAlgorithm.html new file mode 100644 index 000000000..11f3251a3 --- /dev/null +++ b/pr/395/docs/perf/enum.CongestionAlgorithm.html @@ -0,0 +1,30 @@ +CongestionAlgorithm in perf - Rust

CongestionAlgorithm

Enum CongestionAlgorithm 

Source
pub enum CongestionAlgorithm {
+    Cubic,
+    Bbr,
+    NewReno,
+}

Variants§

§

Cubic

§

Bbr

§

NewReno

Implementations§

Source§

impl CongestionAlgorithm

Source

pub fn build(self) -> Arc<dyn ControllerFactory + Send + Sync + 'static>

Trait Implementations§

Source§

impl Clone for CongestionAlgorithm

Source§

fn clone(&self) -> CongestionAlgorithm

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ValueEnum for CongestionAlgorithm

Source§

fn value_variants<'a>() -> &'a [Self]

All possible argument values, in display order.
Source§

fn to_possible_value<'a>(&self) -> Option<PossibleValue>

The canonical argument value. Read more
§

fn from_str(input: &str, ignore_case: bool) -> Result<Self, String>

Parse an argument into Self.
Source§

impl Copy for CongestionAlgorithm

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/perf/fn.parse_byte_size.html b/pr/395/docs/perf/fn.parse_byte_size.html new file mode 100644 index 000000000..89aa88fa3 --- /dev/null +++ b/pr/395/docs/perf/fn.parse_byte_size.html @@ -0,0 +1 @@ +parse_byte_size in perf - Rust

parse_byte_size

Function parse_byte_size 

Source
pub fn parse_byte_size(s: &str) -> Result<u64, ParseIntError>
\ No newline at end of file diff --git a/pr/395/docs/perf/index.html b/pr/395/docs/perf/index.html new file mode 100644 index 000000000..7a3b1be19 --- /dev/null +++ b/pr/395/docs/perf/index.html @@ -0,0 +1 @@ +perf - Rust

Crate perf

\ No newline at end of file diff --git a/pr/395/docs/perf/noprotection/index.html b/pr/395/docs/perf/noprotection/index.html new file mode 100644 index 000000000..36fb17ad6 --- /dev/null +++ b/pr/395/docs/perf/noprotection/index.html @@ -0,0 +1 @@ +perf::noprotection - Rust

Module noprotection

Module noprotection 

Source

Structs§

NoProtectionClientConfig
NoProtectionServerConfig
\ No newline at end of file diff --git a/pr/395/docs/perf/noprotection/sidebar-items.js b/pr/395/docs/perf/noprotection/sidebar-items.js new file mode 100644 index 000000000..7ed1562e0 --- /dev/null +++ b/pr/395/docs/perf/noprotection/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["NoProtectionClientConfig","NoProtectionServerConfig"]}; \ No newline at end of file diff --git a/pr/395/docs/perf/noprotection/struct.NoProtectionClientConfig.html b/pr/395/docs/perf/noprotection/struct.NoProtectionClientConfig.html new file mode 100644 index 000000000..c08f386b2 --- /dev/null +++ b/pr/395/docs/perf/noprotection/struct.NoProtectionClientConfig.html @@ -0,0 +1,29 @@ +NoProtectionClientConfig in perf::noprotection - Rust

NoProtectionClientConfig

Struct NoProtectionClientConfig 

Source
pub struct NoProtectionClientConfig { /* private fields */ }

Implementations§

Trait Implementations§

Source§

impl ClientConfig for NoProtectionClientConfig

Source§

fn start_session( + self: Arc<Self>, + version: u32, + server_name: &str, + params: &TransportParameters, +) -> Result<Box<dyn Session>, ConnectError>

Start a client session with this configuration

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/perf/noprotection/struct.NoProtectionServerConfig.html b/pr/395/docs/perf/noprotection/struct.NoProtectionServerConfig.html new file mode 100644 index 000000000..b48da3739 --- /dev/null +++ b/pr/395/docs/perf/noprotection/struct.NoProtectionServerConfig.html @@ -0,0 +1,37 @@ +NoProtectionServerConfig in perf::noprotection - Rust

NoProtectionServerConfig

Struct NoProtectionServerConfig 

Source
pub struct NoProtectionServerConfig { /* private fields */ }

Implementations§

Trait Implementations§

Source§

impl ServerConfig for NoProtectionServerConfig

Source§

fn initial_keys( + &self, + version: u32, + dst_cid: ConnectionId, +) -> Result<Keys, UnsupportedVersion>

Create the initial set of keys given the client’s initial destination ConnectionId
Source§

fn retry_tag( + &self, + version: u32, + orig_dst_cid: ConnectionId, + packet: &[u8], +) -> [u8; 16]

Generate the integrity tag for a retry packet Read more
Source§

fn start_session( + self: Arc<Self>, + version: u32, + params: &TransportParameters, +) -> Box<dyn Session>

Start a server session with this configuration Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/perf/server/fn.run.html b/pr/395/docs/perf/server/fn.run.html new file mode 100644 index 000000000..d244218b7 --- /dev/null +++ b/pr/395/docs/perf/server/fn.run.html @@ -0,0 +1 @@ +run in perf::server - Rust

run

Function run 

Source
pub async fn run(opt: Opt) -> Result<()>
\ No newline at end of file diff --git a/pr/395/docs/perf/server/index.html b/pr/395/docs/perf/server/index.html new file mode 100644 index 000000000..88db7d540 --- /dev/null +++ b/pr/395/docs/perf/server/index.html @@ -0,0 +1 @@ +perf::server - Rust

Module server

Module server 

Source

Structs§

Opt

Functions§

run
\ No newline at end of file diff --git a/pr/395/docs/perf/server/sidebar-items.js b/pr/395/docs/perf/server/sidebar-items.js new file mode 100644 index 000000000..3dceadfac --- /dev/null +++ b/pr/395/docs/perf/server/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"fn":["run"],"struct":["Opt"]}; \ No newline at end of file diff --git a/pr/395/docs/perf/server/struct.Opt.html b/pr/395/docs/perf/server/struct.Opt.html new file mode 100644 index 000000000..b12408eff --- /dev/null +++ b/pr/395/docs/perf/server/struct.Opt.html @@ -0,0 +1,42 @@ +Opt in perf::server - Rust

Opt

Struct Opt 

Source
pub struct Opt { /* private fields */ }

Trait Implementations§

Source§

impl Args for Opt

Source§

fn group_id() -> Option<Id>

Report the [ArgGroup::id][crate::ArgGroup::id] for this set of arguments
Source§

fn augment_args<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate Self via +[FromArgMatches::from_arg_matches_mut] Read more
Source§

fn augment_args_for_update<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate self via +[FromArgMatches::update_from_arg_matches_mut] Read more
Source§

impl CommandFactory for Opt

Source§

fn command<'b>() -> Command

Build a [Command] that can instantiate Self. Read more
Source§

fn command_for_update<'b>() -> Command

Build a [Command] that can update self. Read more
Source§

impl FromArgMatches for Opt

Source§

fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn from_arg_matches_mut( + __clap_arg_matches: &mut ArgMatches, +) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn update_from_arg_matches( + &mut self, + __clap_arg_matches: &ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

fn update_from_arg_matches_mut( + &mut self, + __clap_arg_matches: &mut ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

impl Parser for Opt

§

fn parse() -> Self

Parse from std::env::args_os(), [exit][Error::exit] on error.
§

fn try_parse() -> Result<Self, Error>

Parse from std::env::args_os(), return Err on error.
§

fn parse_from<I, T>(itr: I) -> Self
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, [exit][Error::exit] on error.
§

fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, return Err on error.
§

fn update_from<I, T>(&mut self, itr: I)
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, [exit][Error::exit] on error. Read more
§

fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, return Err on error.

Auto Trait Implementations§

§

impl Freeze for Opt

§

impl RefUnwindSafe for Opt

§

impl Send for Opt

§

impl Sync for Opt

§

impl Unpin for Opt

§

impl UnwindSafe for Opt

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/perf/sidebar-items.js b/pr/395/docs/perf/sidebar-items.js new file mode 100644 index 000000000..317d83630 --- /dev/null +++ b/pr/395/docs/perf/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["CongestionAlgorithm"],"fn":["parse_byte_size"],"mod":["client","noprotection","server","stats"],"static":["PERF_CIPHER_SUITES"],"struct":["CommonOpt"]}; \ No newline at end of file diff --git a/pr/395/docs/perf/static.PERF_CIPHER_SUITES.html b/pr/395/docs/perf/static.PERF_CIPHER_SUITES.html new file mode 100644 index 000000000..4e1443460 --- /dev/null +++ b/pr/395/docs/perf/static.PERF_CIPHER_SUITES.html @@ -0,0 +1 @@ +PERF_CIPHER_SUITES in perf - Rust

PERF_CIPHER_SUITES

Static PERF_CIPHER_SUITES 

Source
pub static PERF_CIPHER_SUITES: &[SupportedCipherSuite]
\ No newline at end of file diff --git a/pr/395/docs/perf/stats/index.html b/pr/395/docs/perf/stats/index.html new file mode 100644 index 000000000..15fd04ede --- /dev/null +++ b/pr/395/docs/perf/stats/index.html @@ -0,0 +1 @@ +perf::stats - Rust

Module stats

Module stats 

Source

Structs§

OpenStreamStats
Statistics for the currently open streams
Stats
StreamStats
\ No newline at end of file diff --git a/pr/395/docs/perf/stats/sidebar-items.js b/pr/395/docs/perf/stats/sidebar-items.js new file mode 100644 index 000000000..5c7b90d68 --- /dev/null +++ b/pr/395/docs/perf/stats/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["OpenStreamStats","Stats","StreamStats"]}; \ No newline at end of file diff --git a/pr/395/docs/perf/stats/struct.OpenStreamStats.html b/pr/395/docs/perf/stats/struct.OpenStreamStats.html new file mode 100644 index 000000000..bdf2eb565 --- /dev/null +++ b/pr/395/docs/perf/stats/struct.OpenStreamStats.html @@ -0,0 +1,35 @@ +OpenStreamStats in perf::stats - Rust

OpenStreamStats

Struct OpenStreamStats 

Source
pub struct OpenStreamStats(/* private fields */);
Expand description

Statistics for the currently open streams

+

Implementations§

Source§

impl OpenStreamStats

Source

pub fn new_sender( + &self, + stream: &SendStream, + upload_size: u64, +) -> Arc<StreamStats>

Source

pub fn new_receiver( + &self, + stream: &RecvStream, + download_size: u64, +) -> Arc<StreamStats>

Trait Implementations§

Source§

impl Clone for OpenStreamStats

Source§

fn clone(&self) -> OpenStreamStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Default for OpenStreamStats

Source§

fn default() -> OpenStreamStats

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/perf/stats/struct.Stats.html b/pr/395/docs/perf/stats/struct.Stats.html new file mode 100644 index 000000000..ba457be9b --- /dev/null +++ b/pr/395/docs/perf/stats/struct.Stats.html @@ -0,0 +1,24 @@ +Stats in perf::stats - Rust

Stats

Struct Stats 

Source
pub struct Stats { /* private fields */ }

Implementations§

Source§

impl Stats

Source

pub fn on_interval(&mut self, start: Instant, stream_stats: &OpenStreamStats)

Source

pub fn print(&self)

Source

pub fn print_json(&self, path: &Path) -> Result<()>

Trait Implementations§

Source§

impl Default for Stats

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for Stats

§

impl RefUnwindSafe for Stats

§

impl Send for Stats

§

impl Sync for Stats

§

impl Unpin for Stats

§

impl UnwindSafe for Stats

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/perf/stats/struct.StreamStats.html b/pr/395/docs/perf/stats/struct.StreamStats.html new file mode 100644 index 000000000..ebf766fcc --- /dev/null +++ b/pr/395/docs/perf/stats/struct.StreamStats.html @@ -0,0 +1,24 @@ +StreamStats in perf::stats - Rust

StreamStats

Struct StreamStats 

Source
pub struct StreamStats { /* private fields */ }

Implementations§

Source§

impl StreamStats

Source

pub fn on_first_byte(&self, latency: Duration)

Source

pub fn on_bytes(&self, bytes: usize)

Source

pub fn finish(&self, duration: Duration)

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/perf/struct.CommonOpt.html b/pr/395/docs/perf/struct.CommonOpt.html new file mode 100644 index 000000000..ff111011d --- /dev/null +++ b/pr/395/docs/perf/struct.CommonOpt.html @@ -0,0 +1,84 @@ +CommonOpt in perf - Rust

CommonOpt

Struct CommonOpt 

Source
pub struct CommonOpt {
Show 14 fields + pub send_buffer_size: u64, + pub recv_buffer_size: u64, + pub conn_stats: bool, + pub keylog: bool, + pub initial_mtu: u16, + pub no_protection: bool, + pub initial_rtt: Option<u64>, + pub ack_frequency: bool, + pub cong_alg: Option<CongestionAlgorithm>, + pub stream_receive_window: Option<u64>, + pub receive_window: Option<u64>, + pub send_window: Option<u64>, + pub max_udp_payload_size: u16, + pub qlog_dir: Option<PathBuf>, +
}

Fields§

§send_buffer_size: u64

Send buffer size in bytes

+

This can use SI suffixes for sizes. For example, 1M will request +1MiB, 10G will request 10GiB.

+
§recv_buffer_size: u64

Receive buffer size in bytes

+

This can use SI suffixes for sizes. For example, 1M will request +1MiB, 10G will request 10GiB.

+
§conn_stats: bool

Whether to print connection statistics

+
§keylog: bool

Perform NSS-compatible TLS key logging to the file specified in SSLKEYLOGFILE.

+
§initial_mtu: u16

UDP payload size that the network must be capable of carrying

+
§no_protection: bool

Disable packet encryption/decryption (for debugging purpose)

+
§initial_rtt: Option<u64>

The initial round-trip-time (in msecs)

+
§ack_frequency: bool

Ack Frequency mode

+
§cong_alg: Option<CongestionAlgorithm>

Congestion algorithm to use

+
§stream_receive_window: Option<u64>

Maximum number of bytes the peer may transmit without acknowledgement on any one stream +before becoming blocked.

+

This can use SI suffixes for sizes. For example, 1M will limit to +1MiB, 10G will limit to 10GiB.

+
§receive_window: Option<u64>

Maximum number of bytes the peer may transmit across all streams of a connection before +becoming blocked.

+

This can use SI suffixes for sizes. For example, 1M will limit to +1MiB, 10G will limit to 10GiB.

+
§send_window: Option<u64>

Maximum number of bytes to transmit to a peer without acknowledgment

+

This can use SI suffixes for sizes. For example, 1M will limit to +1MiB, 10G will limit to 10GiB.

+
§max_udp_payload_size: u16

Max UDP payload size in bytes

+
§qlog_dir: Option<PathBuf>

qlog output directory

+

Alternatively you can set the QLOGDIR environment variable.

+

Implementations§

Trait Implementations§

Source§

impl Args for CommonOpt

Source§

fn group_id() -> Option<Id>

Report the [ArgGroup::id][crate::ArgGroup::id] for this set of arguments
Source§

fn augment_args<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate Self via +[FromArgMatches::from_arg_matches_mut] Read more
Source§

fn augment_args_for_update<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate self via +[FromArgMatches::update_from_arg_matches_mut] Read more
Source§

impl CommandFactory for CommonOpt

Source§

fn command<'b>() -> Command

Build a [Command] that can instantiate Self. Read more
Source§

fn command_for_update<'b>() -> Command

Build a [Command] that can update self. Read more
Source§

impl FromArgMatches for CommonOpt

Source§

fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn from_arg_matches_mut( + __clap_arg_matches: &mut ArgMatches, +) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn update_from_arg_matches( + &mut self, + __clap_arg_matches: &ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

fn update_from_arg_matches_mut( + &mut self, + __clap_arg_matches: &mut ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

impl Parser for CommonOpt

§

fn parse() -> Self

Parse from std::env::args_os(), [exit][Error::exit] on error.
§

fn try_parse() -> Result<Self, Error>

Parse from std::env::args_os(), return Err on error.
§

fn parse_from<I, T>(itr: I) -> Self
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, [exit][Error::exit] on error.
§

fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, return Err on error.
§

fn update_from<I, T>(&mut self, itr: I)
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, [exit][Error::exit] on error. Read more
§

fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, return Err on error.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/quinn_perf/all.html b/pr/395/docs/quinn_perf/all.html new file mode 100644 index 000000000..0c76accc7 --- /dev/null +++ b/pr/395/docs/quinn_perf/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

List of all items

Structs

Enums

Functions

\ No newline at end of file diff --git a/pr/395/docs/quinn_perf/enum.Commands.html b/pr/395/docs/quinn_perf/enum.Commands.html new file mode 100644 index 000000000..ba7c9e146 --- /dev/null +++ b/pr/395/docs/quinn_perf/enum.Commands.html @@ -0,0 +1,40 @@ +Commands in quinn_perf - Rust

Commands

Enum Commands 

Source
pub(crate) enum Commands {
+    Server(Opt),
+    Client(Opt),
+}

Variants§

§

Server(Opt)

Run as a perf server

+
§

Client(Opt)

Run as a perf client

+

Trait Implementations§

Source§

impl FromArgMatches for Commands

Source§

fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn from_arg_matches_mut( + __clap_arg_matches: &mut ArgMatches, +) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn update_from_arg_matches( + &mut self, + __clap_arg_matches: &ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

fn update_from_arg_matches_mut<'b>( + &mut self, + __clap_arg_matches: &mut ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

impl Subcommand for Commands

Source§

fn augment_subcommands<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate Self via +[FromArgMatches::from_arg_matches_mut] Read more
Source§

fn augment_subcommands_for_update<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate self via +[FromArgMatches::update_from_arg_matches_mut] Read more
Source§

fn has_subcommand(__clap_name: &str) -> bool

Test whether Self can parse a specific subcommand

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> IntoRequest<T> for T

§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
§

impl<L> LayerExt<L> for L

§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where + L: Layer<S>,

Applies the layer to a service and wraps it in [Layered].
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/quinn_perf/fn.main.html b/pr/395/docs/quinn_perf/fn.main.html new file mode 100644 index 000000000..d02fe8057 --- /dev/null +++ b/pr/395/docs/quinn_perf/fn.main.html @@ -0,0 +1 @@ +main in quinn_perf - Rust

main

Function main 

Source
pub(crate) fn main()
\ No newline at end of file diff --git a/pr/395/docs/quinn_perf/index.html b/pr/395/docs/quinn_perf/index.html new file mode 100644 index 000000000..520445aa4 --- /dev/null +++ b/pr/395/docs/quinn_perf/index.html @@ -0,0 +1 @@ +quinn_perf - Rust

Crate quinn_perf

Crate quinn_perf 

Source

Structs§

Cli 🔒

Enums§

Commands 🔒

Functions§

main 🔒
\ No newline at end of file diff --git a/pr/395/docs/quinn_perf/sidebar-items.js b/pr/395/docs/quinn_perf/sidebar-items.js new file mode 100644 index 000000000..9a5c272f9 --- /dev/null +++ b/pr/395/docs/quinn_perf/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Commands"],"fn":["main"],"struct":["Cli"]}; \ No newline at end of file diff --git a/pr/395/docs/quinn_perf/struct.Cli.html b/pr/395/docs/quinn_perf/struct.Cli.html new file mode 100644 index 000000000..a634bf9f3 --- /dev/null +++ b/pr/395/docs/quinn_perf/struct.Cli.html @@ -0,0 +1,45 @@ +Cli in quinn_perf - Rust

Cli

Struct Cli 

Source
pub(crate) struct Cli {
+    pub(crate) command: Commands,
+}

Fields§

§command: Commands

Trait Implementations§

Source§

impl Args for Cli

Source§

fn group_id() -> Option<Id>

Report the [ArgGroup::id][crate::ArgGroup::id] for this set of arguments
Source§

fn augment_args<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate Self via +[FromArgMatches::from_arg_matches_mut] Read more
Source§

fn augment_args_for_update<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate self via +[FromArgMatches::update_from_arg_matches_mut] Read more
Source§

impl CommandFactory for Cli

Source§

fn command<'b>() -> Command

Build a [Command] that can instantiate Self. Read more
Source§

fn command_for_update<'b>() -> Command

Build a [Command] that can update self. Read more
Source§

impl FromArgMatches for Cli

Source§

fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn from_arg_matches_mut( + __clap_arg_matches: &mut ArgMatches, +) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn update_from_arg_matches( + &mut self, + __clap_arg_matches: &ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

fn update_from_arg_matches_mut( + &mut self, + __clap_arg_matches: &mut ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

impl Parser for Cli

§

fn parse() -> Self

Parse from std::env::args_os(), [exit][Error::exit] on error.
§

fn try_parse() -> Result<Self, Error>

Parse from std::env::args_os(), return Err on error.
§

fn parse_from<I, T>(itr: I) -> Self
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, [exit][Error::exit] on error.
§

fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, return Err on error.
§

fn update_from<I, T>(&mut self, itr: I)
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, [exit][Error::exit] on error. Read more
§

fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, return Err on error.

Auto Trait Implementations§

§

impl Freeze for Cli

§

impl RefUnwindSafe for Cli

§

impl Send for Cli

§

impl Sync for Cli

§

impl Unpin for Cli

§

impl UnwindSafe for Cli

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> +if into_left is true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where + F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> +if into_left(&self) returns true. +Converts self into a Right variant of Either<Self, Self> +otherwise. Read more
§

impl<T> IntoRequest<T> for T

§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
§

impl<L> LayerExt<L> for L

§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where + L: Layer<S>,

Applies the layer to a service and wraps it in [Layered].
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/395/docs/search.index/0022edf8729b.js b/pr/395/docs/search.index/0022edf8729b.js new file mode 100644 index 000000000..8ac3934e3 --- /dev/null +++ b/pr/395/docs/search.index/0022edf8729b.js @@ -0,0 +1 @@ +rn_("AQUA9LIQAAABAAEAAQATgAKgsAAAE3ZjcwEEAPOADwAAAQABAAGAAqBQAAAQhqBgAAAQ9GlyAYACoCAAAAyJoGAAABDzZWgBAQGgIAAAEPJi84UMAAABAAEAE4ECoBAAAAyIYWMBgAKgEAAAE0KwMA0bAAFlcyMAAqBAAAAT1WV38vQTAAABAAEGAPSeEgAAAQB3AQEAEwACsGASoAABZm3y7QwAAAEAAQEA9L8QAAABAKkAAQAjgAKgsAAAE11kZwGCAqCgAAASzKBAAAAQLm9284ADZ25yAYACoEAAABKZoDAAAA99aGw3PwAAirBAD34AAaAwAAAMhDRgVzswAAABAAAaAAQA4QEAAB8EDgATDAkA3gwAAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/0051e5fe15da.js b/pr/395/docs/search.index/0051e5fe15da.js new file mode 100644 index 000000000..64268f86d --- /dev/null +++ b/pr/395/docs/search.index/0051e5fe15da.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAYAAQAqFAYA8wYBZfTfEwAAAQAuAAEAEwMDsEAUBwABoGAAABOIYXJ1ATMQAAAjgAKAESgAEAFkcg==") \ No newline at end of file diff --git a/pr/395/docs/search.index/009757435c6f.js b/pr/395/docs/search.index/009757435c6f.js new file mode 100644 index 000000000..33d20eccf --- /dev/null +++ b/pr/395/docs/search.index/009757435c6f.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgALEwAAbhMFACOIArBAE9wAAWVzAQQBsDAS4gABY/MzEQAAAQABAAEFADswAAABAAAIAAMAohECAJASBACdEwAA8wEDY2Vw89oMAAABAAEAAQIA9nQJAABQAHUGAQCvAAEA84ACbnI=") \ No newline at end of file diff --git a/pr/395/docs/search.index/00aa835204e9.js b/pr/395/docs/search.index/00aa835204e9.js new file mode 100644 index 000000000..d86afde20 --- /dev/null +++ b/pr/395/docs/search.index/00aa835204e9.js @@ -0,0 +1 @@ +rn_("AYYCoAAAABLcoAAAABLdZHIBgAKgcAAAE2KgMAAAEMZmdAELAaBAAAATrmXy2BMAAAEAI4ECsFAQnQABY3YBAAGwgBOwAAFw8qwTAAABAEMGA6AgAAARDaBAAAASSmRlc/O1DwAAGAMBAPMABGlyc3Y7MAAAAQAADwAFAGEHBQBsEgUAlxIBAAITAADjEwAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/01c8653e6e9f.js b/pr/395/docs/search.index/01c8653e6e9f.js new file mode 100644 index 000000000..8a3949ab8 --- /dev/null +++ b/pr/395/docs/search.index/01c8653e6e9f.js @@ -0,0 +1 @@ +rn_("AYMCsQAUJgABsNAUGAABa20BBAGgoAAAFDF38iEUAAABAAELAPOWEwAAAQABACOAAqCAAAATvGVvYwQDoGAAABNVZW5wAUAMAAABCgDzeRMAAAEAbQATgAKiEAAAFDFsb3cAAACFoGAAABIqoQAAABPpBIEDOzAAAAEAADsABAAzBgUAuQcAAAkIMwAyEQAAQ4ADsIARMQEKoAAAAAHvZGt0") \ No newline at end of file diff --git a/pr/395/docs/search.index/02bba2f3bb15.js b/pr/395/docs/search.index/02bba2f3bb15.js new file mode 100644 index 000000000..0f63fd6f4 --- /dev/null +++ b/pr/395/docs/search.index/02bba2f3bb15.js @@ -0,0 +1 @@ +rn_("AQMBsEARDgABZfIuDAAASQIBBQA7MAAAAQAABgABABkQBgABAwD05AwAAAEAAQABAPMAAnBzOjAAAAEAAAAAAA4AEAAAACEGIgYjBi4HbAlyDH4MshCzELQQtRBLEkwSnBKdEjMAA7BQD5EAxmRwcjswAAABAAAPAAYALgUAAOIFAwB9DwAAihEDAEYSAwB1EgEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/0515792bb474.js b/pr/395/docs/search.index/0515792bb474.js new file mode 100644 index 000000000..052b06aad --- /dev/null +++ b/pr/395/docs/search.index/0515792bb474.js @@ -0,0 +1 @@ +rn_("AQQA9hUNAAABAO8CAQAgAQEAAQoA82YSAAABAJoAAQEA93oJAAABALoGAQBpAAEADQEjgAKwYBNnAAFpbwECAPTXDAAAAQDKBQEAAQUA9H0TAAABAEYAAQD3CAAAiLCQEzcAAaAgAAAM2bCQEaAAAQTcAzowAAABAAAAAAAIABAAAAAOBmoJawl8DH0MrxCwEJUSlhI=") \ No newline at end of file diff --git a/pr/395/docs/search.index/056473b4cdef.js b/pr/395/docs/search.index/056473b4cdef.js new file mode 100644 index 000000000..5f7879db0 --- /dev/null +++ b/pr/395/docs/search.index/056473b4cdef.js @@ -0,0 +1 @@ +rn_("AYMCoFAAABDCoKAAABLVa3ABAAGwQBItAAFs9L8JAAABANEGAQABCAD0QRIAAAEAAQABAAEEAbAAEJsAAXMBDxAAAAECAaBQAAASmWMB3gwAALcAAACFsEANGAABoDAAAAwxlQABOzAAAAEAAAgABABTBQAAEgYCAOEMAQCADwIAJwEAgIWwMAmEAAGgEAAAEwWwIAfhAAGRAEQBEAD0CRQAAAEAAQABAAEDAPS/CQAAAQDRBgEAY4ADoAAAAAwxbnJ1AQACsNATRAABoDAAAA+DYmr29wUAAAEAAQBJAAEAZAEBAAGwgBNjAAF0OzAAAAEAAEAABABABgAAbgkAAHkJAACZDD0AAQkA818SAAABAAEA84ADY2d0AYMCoJAAABOqoJAAABOrMjMBgAKgUAAAE9KggAAAEwJjZfMAAnN08qgHAAABAAEAAaCwAAATRmLz3RIAABQBAQAjgAKgAAAAEtxkciOAAqBQAAATnGNl84ACaXkBBAD17RAAAAEAAQABAAEA8wEBbgGeBwAAAQEAOzAAAAEAAAkAAgBOCAcAoBIBAAEGAPQ5EwAAAQABAAEAAQ8A9BIUAAABAAEAAQDzgQJobQEAAbBAE9YAAWX1nhMAAAEAAQABAAEAU4ADoCAAABGJbG50120AgMugIAAAFASwYBCCAAGwQA+5AAHAGzgK") \ No newline at end of file diff --git a/pr/395/docs/search.index/06205a12fd8a.js b/pr/395/docs/search.index/06205a12fd8a.js new file mode 100644 index 000000000..f856b0dfb --- /dev/null +++ b/pr/395/docs/search.index/06205a12fd8a.js @@ -0,0 +1 @@ +rn_("AQMA9JQMAAABAAEAAQABAAGwUBOyAAFyAU8FAABTAYSwQBJRAAGwQBPrAAGYAAQ6MAAAAQAAAAAAKAAQAAAAQARBBEIEEQZmCWcJjQm7CeMMjA/5D/oPBxA0EIsQjBCNENUQNBKMEo0SjhKPErQSwhLDEu8S8BJTE1QTWBNZE1oTahNrE8oTyxPhE+ITBRQGFA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/062983155a37.js b/pr/395/docs/search.index/062983155a37.js new file mode 100644 index 000000000..449878db6 --- /dev/null +++ b/pr/395/docs/search.index/062983155a37.js @@ -0,0 +1 @@ +rn_("AQUA9H0TAAABAEYAAQDzAwFj8pUSAAABACOAArBwFCQAAWhvAYICsAAScwABsAARkgABZXNDAAOgoAAAE8mgIAAAEw9laXT1hRIAAAEAeQABAOkAAYACsHASagABoLAAABORZW8BAgA7MAAAAQAABgABABERBgABAgGgQAAAE65l8tgTAAABAPOAAmlvAQgA9HQTAAABAH4ABQATAQOwkBPTAAGwUBLGAUtnbHA7MAAAAQAAEgAJACMQAgCVEQEAtRIBANYSAQAGEwEAZRMBAJkTAQCoEwEA5RMBAAGAArBAEk0A+qAgAAAQ1mFpAYECsDAThAABoDAAABJpY2kBAwGwABKwAAFzOzAAAAEAAA0AAwCQEQEAIRMHAAkUAwABAwD1jhEAAAEACwEBAEMB84ACcnYBAAD0SxIAAAEAUAABAPMCAWQ7MAAAAQAACQADAIoRAwBGEgMAdRIBACOAAqCQAAATyGlvE4ACsPAUFgABbG8BBQD05BMAAAsAHgAPACOAAqBgAAATBWdzI4ACsJATxgABYmz3FwMAj7BwEaYAAbBgE/kAAbBwE3cAAbBgEsQAAbBQErIAAbX5FzswAAABAAA7ABcALwYAANQHAQBMCQUAXAkAAIoJAAAxDAAAdwwBAOoMAgATDQEAeA4DAH0OAQC5DwQAgBABALoQAADdEAAA/RAJADoRAAA8EQMAiBEAALwSAADTEgEA5BIFAJwTAAA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/06edb61cc89f.js b/pr/395/docs/search.index/06edb61cc89f.js new file mode 100644 index 000000000..ce4607f8b --- /dev/null +++ b/pr/395/docs/search.index/06edb61cc89f.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAHwAAgClEAAArhF7AAEAAbBwE0QAAWXynw8AAAEA8wABc/QIEAAAAQD+AwEAM4ADoGAAABFYZGlv") \ No newline at end of file diff --git a/pr/395/docs/search.index/070e1accd154.js b/pr/395/docs/search.index/070e1accd154.js new file mode 100644 index 000000000..773e5d7eb --- /dev/null +++ b/pr/395/docs/search.index/070e1accd154.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAYAAQAZEAYAAQMA9OQMAAABAAEAAQBjAAOgkAAAEvpocHM6MAAAAQAAAAAAEAAQAAAAIQYiBiMGLgdsCXIMfgyyELMQtBC1EEsSTBKcEp0SxBLFEg==") \ No newline at end of file diff --git a/pr/395/docs/search.index/07329e64a5f0.js b/pr/395/docs/search.index/07329e64a5f0.js new file mode 100644 index 000000000..c53d8f8fb --- /dev/null +++ b/pr/395/docs/search.index/07329e64a5f0.js @@ -0,0 +1 @@ +rn_("AYACsFATjwABoAAAAAlkY3QBAQD0ERMAAAEAAQABAAEDAPT7DAAASwaWAAEAAYACoBAAAAyMoFAAABI3Y2QBAgDzWQkAAA8DAQDzAAFt8zMFAAC0AAEAAYECoBAAABLMoHAAABMMYWPzAAFy88QHAACUCRkAZw8AgIiwMA+RAMagMAAAFAMpMCM=") \ No newline at end of file diff --git a/pr/395/docs/search.index/077a08ac2280.js b/pr/395/docs/search.index/077a08ac2280.js new file mode 100644 index 000000000..c408a690e --- /dev/null +++ b/pr/395/docs/search.index/077a08ac2280.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABGfZfMQDQAAtwMBAAEGAPTEEAAAAQC4AAEAE4EDoAAAABELsSAUDgABYWRuARQA9SwUAAABAAEAAQABAAEGAbCAE88AAXY7MAAAAQAABwACAHgRBAA9EwIAVwAAAYagwAAAEwqgoAAAEyqgkAAAEvuwUBA+AAEF4AL0fwcAAAEAAQCCDAEGAPNmEgAAAQCaAPOAA3J0dgEDADswAAABAAAHAAIAORMDABIUAwDzAAFzAdITAADzAAFzAZ4RAABnAQCAhqAwAAAMW7AAEvYAAaAQAAAM9hJADg==") \ No newline at end of file diff --git a/pr/395/docs/search.index/085d7a8021d6.js b/pr/395/docs/search.index/085d7a8021d6.js new file mode 100644 index 000000000..1e3b211e7 --- /dev/null +++ b/pr/395/docs/search.index/085d7a8021d6.js @@ -0,0 +1 @@ +rn_("AQEBsIAS8QABdfKaCQAAAQAjgAKwEAmYAAEzNgEGAPOfEAAAAQANAROAArAAD3gAAW91AQMA818SAAABAAEAI4ACsAARawABaGkBAAA7MAAAAQAAEwABAO8HEwAjgAKgMAAADOhhZfOAAnJ0E4ACsDATNwABYWwBAAGggAAAFCBm8lUJAADACQEHAPXqEgAAAQABAAEAAQAjgAKgUAAAERBhb/MCAmZzOzAAAAEAAAgABAC3BwAA6QcFAA8NAABJEQAAE4ACoEAAAAwvYW8BAwGwQBPrAAF08lMTAAABACOFAqBAAAATgWJzEwACoFAAABEdYmb3YgkAAAEAQwDTBgEAXwABABOBAqBQAAARpWd0AYACoEAAABGUoCAAABNiYWkBAgD0lAkAAAEAYAcBAAECAPNqDAAAAQABABOAA7AQCZIAAbAwE5QAAWl1eQECADswAAABAAALAAIATBMFAP0TBQDzCAFtOzAAAAEAAAwAAwAaEQAAeRIFAL4TBQABgQKggAAAEmigcAAAE6Nhc/OAAm5yt7UBAI2hQAAAE/iwMBE3AAGgIAAACZawIAe7Ady3lQ47MAAAAQAAFQIFANQBAAD5ASIBdQcAAN4HAABFCu8A") \ No newline at end of file diff --git a/pr/395/docs/search.index/097a8c9da4ad.js b/pr/395/docs/search.index/097a8c9da4ad.js new file mode 100644 index 000000000..8ad1e305b --- /dev/null +++ b/pr/395/docs/search.index/097a8c9da4ad.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABLSc/SPDwAAsQMBAAIAAQECsGATsgABsEAT6wABZHQ7MAAAAQAADQAGAIwSAwDvEgEAUxMBAGoTAQDhEwEABRQBAAEBA7BwEaYAAbCAEmoAAYAS/wAQ6W1uczswAAABAAALAAIATAkFAOQSBQCjgISgsAAAE0KxcBQkAAEIQIIBAISgcAAAEjSgcAAAEL2xABOyAAGwABLGAUsCIQHy8wUAAAEAAQEAOzAAAAEAAAYAAQAqFAYA8wYBZfTfEwAAAQAuAAEAEwEDsEAUBwABoGAAABOIYXJ1ATMQAAB3BAAAh6AQAAAGJrAwErkBMaBAAAAQ4BHBDDswAAABAAAGAAIACxMAAG4TBQA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/09c34cd06079.js b/pr/395/docs/search.index/09c34cd06079.js new file mode 100644 index 000000000..1497bd5fd --- /dev/null +++ b/pr/395/docs/search.index/09c34cd06079.js @@ -0,0 +1 @@ +rn_("AQkA8+0TAAABADUAAQ0BoKAAABQxd/IhFAAAAQABgQKwgBP7AAGgYAAAFB9kczcBAIaFoEAAABPRsGATpQBLApAK") \ No newline at end of file diff --git a/pr/395/docs/search.index/0a0d55e0322f.js b/pr/395/docs/search.index/0a0d55e0322f.js new file mode 100644 index 000000000..d0514ad13 --- /dev/null +++ b/pr/395/docs/search.index/0a0d55e0322f.js @@ -0,0 +1 @@ +rn_("AQABsIATYwABdAFuCQAAAQkA818SAAABAAEA84ACY2cBgwKgkAAAE6qgkAAAE6syMwGAAqBQAAAT0qCAAAATAmNl8wACc3TyqAcAAAEAAQABoLAAABNGYvPdEgAAFAEBACOAAqAAAAAS3GRyI4ACoFAAABOcY2XzgAJpeQEGAPXtEAAAAQABAAEAAQABAQGwQBPWAAFl9Z4TAAABAAEAAQABAPcAAICFoBAAAAwxKWAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/0ab68eae012d.js b/pr/395/docs/search.index/0ab68eae012d.js new file mode 100644 index 000000000..9cf9bdd58 --- /dev/null +++ b/pr/395/docs/search.index/0ab68eae012d.js @@ -0,0 +1 @@ +rn_("AQQBsEAT1gABZfWeEwAAAQABAAEAAQABAgA7MAAAAQAACwACAEwTBQD9EwUA8wEBbTswAAABAAANAAQA3wwAABoRAAB5EgUAvhMFAPMBAWXyphEAAAEAM4CEsBAQQAABoCAAAAw1FEAE") \ No newline at end of file diff --git a/pr/395/docs/search.index/0c67f8980f27.js b/pr/395/docs/search.index/0c67f8980f27.js new file mode 100644 index 000000000..772a32391 --- /dev/null +++ b/pr/395/docs/search.index/0c67f8980f27.js @@ -0,0 +1 @@ +rn_("AQMBoAAAAA/zc/NFDAAAAQABAAECAPNEDAAAygQBACMAAqBAAAAM+2Vy8kQFAAABAAGAAqBAAAATVbAwEocAAWFzAQIBoEAAABPVZfL0EwAAAQDzgARsbnJ2") \ No newline at end of file diff --git a/pr/395/docs/search.index/0c6d6b68fc28.js b/pr/395/docs/search.index/0c6d6b68fc28.js new file mode 100644 index 000000000..3aca57ffb --- /dev/null +++ b/pr/395/docs/search.index/0c6d6b68fc28.js @@ -0,0 +1 @@ +rn_("AQkA8+0TAAABADUAAYACsCATkgABoCAAAA+UaXkBCQGggAAAFCBmARUTAAABAwGwABOlAEtzAVITAABDgAOwIA+QARiwUBJPAAFhb3IBAgD1ixAAAAEAAQA1AgEA8wADaXR1OjAAAAEAAAAAAA4AEAAAAOgHhwnzD2kQiRGAErASsRJWE1cTXROGE4cToxOkEwEGAPSeEgAAAQB3AQEAEwICsGASoAABZm3y7QwAAAEAI4ICsEASyAABZWwBgAKgMAAAE9GwsBMvAAFhbAEGAPOeDwAA1wABAAEFAPR9EwAAAQBGAAEA8wIBY/KVEgAAAQABgQKwUBDZAAGwIA9uAAFjbgEFADswAAABAAAOAAIA8wwAAMUPDQABAgA7MAAAAQAABwABAKkJBwDnBQAACaAwAAAMb6CwAAATo6CAAAARWLDQE7IAATBhZmlrbXJ1djswAAABAAA5AgUA1gEAADED3QDfBwAANQvdAPsNewABDAGgoAAAFDF38iEUAAABAAEBAPO2DwAAAQABAPOAAnR1AQMA81MSAAABAAEAAQEA9e0QAAABAAEAAQABAAEBAPbSBwAAAQCMAEoHowDeAWOAA7CQE38AAWFtdAEDAaBAAAAT1WXy9BMAAAEAE4ACsGAUJAABZXIBAwGgAAAAE11zAacQAAABAgA6MAAAAQAAAAAACgAQAAAApwmoCdwP9BL1Eh0THhO9ExAUJhQnFIMABKCAAAASL7BwERsAAbAgEL0A6GZucnYBXggAAAGBAqAAAAAJh6AwAAAPlHN0xwEAgMawABOwAAGwkBItAAGgEAAAB7gAEogoAQIAOzAAAAEAAA0ABgATEAUAGBEBAGISAQB5EwEAyBMAAOcTAAATggKw8BQkAAFmZwGAArCAE/sAAaBgAAAUH2RzE4ACsMATRAABaW8BCAD1thMAAAEAAQABAAEA8wIBZvVOEAAAAQABAAEAAQAjgAOgIAAAD46woBP5AAFkcnT3+wcAj6AwAAAPg5fXDzswAAABAAAtARwAOgQBAEAFAgD3BQIAHgYCAEIGAQApBwQApwcAAMEHAABYCAMAXQkCAIsJAQBFDAIAcAwBAJQMAwDvDAEAfw7mAKwPAQApEAIAahABAHsQAgCmEAEAOREAAJARAQA3EgAAchIAAAMTAQAhEwcACRQDAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/0ca033ef2465.js b/pr/395/docs/search.index/0ca033ef2465.js new file mode 100644 index 000000000..6ec2aae73 --- /dev/null +++ b/pr/395/docs/search.index/0ca033ef2465.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAHwAAgDcBwAAyQl7AAGEArBwE6wAAaBAAAAQxmlzAQcA858QAAABAA0BI4ACoLAAABJoYXQBAAOwMAl8AAGwMBMWAAGgIAAAB91hZG8B7gEAADcDAICGoMAAABOksAATewABAJgV") \ No newline at end of file diff --git a/pr/395/docs/search.index/0d27f25fe856.js b/pr/395/docs/search.index/0d27f25fe856.js new file mode 100644 index 000000000..d3d9f4a79 --- /dev/null +++ b/pr/395/docs/search.index/0d27f25fe856.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAOgAAgDvDAEAfw7mAAEBAaAAAAAS0nP1UgUAAD0KsQMBAAIAAYACoCAAAA+EoDAAABJ/ZXITAQKgIAAAEVlwdQFwDwAAAQECoGAAABNGoOAAABOdZXQ7MAAAAQAACAABAFYFCAABgAOwIBIrAAGwsBJiAAGwsBJkAAFiY3PzgMUAAQoK") \ No newline at end of file diff --git a/pr/395/docs/search.index/0d76b2553bfd.js b/pr/395/docs/search.index/0d76b2553bfd.js new file mode 100644 index 000000000..c97105e9f --- /dev/null +++ b/pr/395/docs/search.index/0d76b2553bfd.js @@ -0,0 +1 @@ +rn_("AQgA9bYTAAABAAEAAQABAPMAAWY7MAAAAQAABQACAMsBAABOEAQAAQIAOzAAAAEAAAsAAgBMEwUA/RMFAPMGAW07MAAAAQAADQAEAN8MAAAaEQAAeRIFAL4TBQBjgAOwAAl8AAFlbXM=") \ No newline at end of file diff --git a/pr/395/docs/search.index/0d7e3bfebb98.js b/pr/395/docs/search.index/0d7e3bfebb98.js new file mode 100644 index 000000000..ba30ac9b7 --- /dev/null +++ b/pr/395/docs/search.index/0d7e3bfebb98.js @@ -0,0 +1 @@ +rn_("AQABoCAAAAz2bQH/BQAAAQQBsAASnAABZPRGEgAAAQABAAEAI4SEsKATxgABsEASmgABsHATdwABFYAAM4ADsNATtAABYWVp") \ No newline at end of file diff --git a/pr/395/docs/search.index/0dec9e618431.js b/pr/395/docs/search.index/0dec9e618431.js new file mode 100644 index 000000000..6270f85a8 --- /dev/null +++ b/pr/395/docs/search.index/0dec9e618431.js @@ -0,0 +1 @@ +rn_("AQ0A9VgTAAABAAEAcAABAAECAPS4EAAAAQB5AQEAAQMA9CIRAAABAGABAQDzAAJidQHVDwAAAQMA9LYQAAABAHkBAQDzhAJkcwECAPNmCQAAAQBUAPOAAml0ARgA8y4UAAABAAEAI4ECsMATVgABY20BBQGgYAAAElhy9D4MAAABAJQDAQABCQD2gRIAAAEA5wB/ADAAAQATgAKwYBB5AAFkbgEDAaBwAAAStGf0xQcAAAEA8wEBAAEJAPPtEwAAAQA1AAGBArCAE/sAAaBgAAAUH2RzFwEAiYWw0BQhAAGgQAAAE9GwYBOlAEsCkApnXgAAi7BwENsAAbCwEsIAAaCgAAASgLDwE4YAAW2BD/R5AAAAAQABAAEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/0f04eaaf1f31.js b/pr/395/docs/search.index/0f04eaaf1f31.js new file mode 100644 index 000000000..f78519b09 --- /dev/null +++ b/pr/395/docs/search.index/0f04eaaf1f31.js @@ -0,0 +1 @@ +rn_("AQEAOjAAAAEAAAAAAAkAEAAAAJwPnQ+GEEARgRKCEmkT6BMYFBkUAYACsOATYwABsDATewABZHDDgISxQBQoAAGgkAAAEyoCgAk=") \ No newline at end of file diff --git a/pr/395/docs/search.index/0f80e54f3f36.js b/pr/395/docs/search.index/0f80e54f3f36.js new file mode 100644 index 000000000..88d9a3ae4 --- /dev/null +++ b/pr/395/docs/search.index/0f80e54f3f36.js @@ -0,0 +1 @@ +rn_("AQcA9LIQAAABAAEAAQABAAD1tAkAAAEAAQABAAEAI4ACoKAAABQDZnABAwGgkAAAE4Nl8nMPAAABANcAAACFoFAAABAxsUAUKgABgSkA8vEFAAABAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/10696d58fab9.js b/pr/395/docs/search.index/10696d58fab9.js new file mode 100644 index 000000000..7fed67013 --- /dev/null +++ b/pr/395/docs/search.index/10696d58fab9.js @@ -0,0 +1 @@ +rn_("AYACsAARKwABsAARLQABNDYBBAGgoAAAFDF38iEUAAABAAELAPOWEwAAAQABACOAAqCAAAATvGVvYwIDoGAAABNVZW5wAUAMAADzAAJhdgFLBwAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/106bd6af91ad.js b/pr/395/docs/search.index/106bd6af91ad.js new file mode 100644 index 000000000..f0598259a --- /dev/null +++ b/pr/395/docs/search.index/106bd6af91ad.js @@ -0,0 +1 @@ +rn_("AQgA9GMTAAABAFAAAQABAgGgYAAAFARz8oMRAAABAAGAArAgD90AAaCQAAATu2FlAQcBsEAT1gABZfWeEwAAAQABAAEAAQABAwGwQBPrAAF08lMTAAABACOFAqBAAAATgWJzAQIA9H8RAAABAAEAAQA3dAAAi7BAEzUAAaBQAAARHaCQAAATKbBQEpcAAaAwAAAMg7OYBzswAAABAABdABEA3wEBABsEAwBABgAALwcBAMsHAQBiCQEAbgkAAHkJAACmCQAAmQw9AOkMAACqDwEAeRABAJMQAQDZEAEAWhEEAE8SAQA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/10dca90977e7.js b/pr/395/docs/search.index/10dca90977e7.js new file mode 100644 index 000000000..de207ba8d --- /dev/null +++ b/pr/395/docs/search.index/10dca90977e7.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAkAAQD9EAkAI4ADsGATAwABsDAPiQABaXN0AQgA9bYTAAABAAEAAQABAPMBAWb1ThAAAAEAAQABAAEAY4ADoDAAABLLZWd0") \ No newline at end of file diff --git a/pr/395/docs/search.index/10e28db8e153.js b/pr/395/docs/search.index/10e28db8e153.js new file mode 100644 index 000000000..34bd845be --- /dev/null +++ b/pr/395/docs/search.index/10e28db8e153.js @@ -0,0 +1 @@ +rn_("AQACoCAAABMPsDARkgABaXTyhRIAAAEAAQMAOzAAAAEAAAYAAQAREQYAAQgA9HQTAAABAH4ABQATAQOwkBPTAAGwUBLGAUtnbHA7MAAAAQAAEgAJACMQAgCVEQEAtRIBANYSAQAGEwEAZRMBAJkTAQCoEwEA5RMBAAGAArAwEY4AAbAwEZAAAXJ2AQQBsAASSwABZDswAAABAAAFAAIAihEDAHUSAQC3BQABh7BQEk0A+rBgEsQAAZQJAzswAAABAAAKAAUA6gwCAH0OAQC6EAAAPBEDALwSAAAjgAKgEAAADOlubwEDAPMgEAAAAQABACOAAqAAAAAMMGRyYwCEoFAAABE5oDAAABBYFAEQ8gMIAAABAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/10fa6d29f4f2.js b/pr/395/docs/search.index/10fa6d29f4f2.js new file mode 100644 index 000000000..9c93afff1 --- /dev/null +++ b/pr/395/docs/search.index/10fa6d29f4f2.js @@ -0,0 +1 @@ +rn_("AQAAOjAAAAEAAAAAAAoAEAAAAKMPpA+HEIgQEBGcEZ0RnhKfEhYUFxTzAQF58oQTAAABACMAA6BAAAARL6BQAAATzmVvc/KHDwAArgI=") \ No newline at end of file diff --git a/pr/395/docs/search.index/115053be1aee.js b/pr/395/docs/search.index/115053be1aee.js new file mode 100644 index 000000000..cda4d453a --- /dev/null +++ b/pr/395/docs/search.index/115053be1aee.js @@ -0,0 +1 @@ +rn_("AYICoFAAABJyoQAAABQEY3QjAAKwQA+SAAFldTswAAABAAA3ABYAMQcBACsMAQABDQEAlw8BABMQBQBDEAUAuxABABgRAQCiEQIAYhIDAJASBAC6EgEAwBIBAM8SAQBIEwEAWxMBAHkTAQB9EwEAnRMAAMQTAQDIEwEA5xMAAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/11ecde640804.js b/pr/395/docs/search.index/11ecde640804.js new file mode 100644 index 000000000..aa7a5a76e --- /dev/null +++ b/pr/395/docs/search.index/11ecde640804.js @@ -0,0 +1 @@ +rn_("AQUA9BENAAABAPECAQABAAD0eQwAAAEAAQBlAAECADowAAABAAAAAAAJABAAAACcD50PhhBAEYESghJpE+gTGBQZFMcBAACGsDAJdwABoEAAABGfsIARhgABGEAHOjAAAAEAAAAAABcAEAAAANIH0wdfCIMJsgmzCRANiQ+KD6kPTBBTEFQQVRBWEI8QkBDHEMgQKhJnE2gTkhOTEw==") \ No newline at end of file diff --git a/pr/395/docs/search.index/122837952335.js b/pr/395/docs/search.index/122837952335.js new file mode 100644 index 000000000..b98115f72 --- /dev/null +++ b/pr/395/docs/search.index/122837952335.js @@ -0,0 +1 @@ +rn_("AQQA9hUNAAABAO8CAQAgAQEAAQoA82YSAAABAJoAAQEA93oJAAABALoGAQBpAAEADQEjgAKwYBNnAAFpbwECAPTXDAAAAQDKBQEAAQUA9H0TAAABAEYAAQD3BAAAh6AgAAAM2bCQEaAAAQScAzowAAABAAAAAAAIABAAAAAOBmoJawl8DH0MrxCwEJUSlhI=") \ No newline at end of file diff --git a/pr/395/docs/search.index/1257c4864088.js b/pr/395/docs/search.index/1257c4864088.js new file mode 100644 index 000000000..c79e8c082 --- /dev/null +++ b/pr/395/docs/search.index/1257c4864088.js @@ -0,0 +1 @@ +rn_("AQUA9BETAAABAAEAAQABAAD1jwwAAAEAAQABAAEA84ACZ2sTgAKwsBNnAAFhbwGFArBwE48AAbAwEu8AAWVzEwADsEAPlQABoGAAABE6YmVzOzAAAAEAABIABAAkBgEAMwcKAAsQAQBdEAMAAQMA9CcQAAABAAQAAQABCwGgQAAAE9Vl8vQTAAABAPOAAmRy8wACZW87MAAAAQAABwACAAUIAwBGCAMAAQIAOzAAAAEAAAkAAQATDAkA84ACb3QBAwA7MAAAAQAA3gACAN8HAAA1C90AAQIBoHAAABGlaTswAAABAADwAAIA3gcAAEUK7wATgAKgQAAADE1ydQEEAPTPEgAAAQCLAAEAAQMA82YSAAABAJoA84ACYW/XBQAAiLBADQcAAaAwAAAMHbEwE/EAATSBFjswAAABAAAeAA0ASwUBAP0FAQAlDAAAUgwHAHUMAQCjDwEAhxABABARAABfEQMAnBEBAEUSAACeEgEAFhQBAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/1298ab4d6c58.js b/pr/395/docs/search.index/1298ab4d6c58.js new file mode 100644 index 000000000..f561dd68a --- /dev/null +++ b/pr/395/docs/search.index/1298ab4d6c58.js @@ -0,0 +1 @@ +rn_("AYQCsAARKwABsAARLQABNDbzAAFh8ooMAAABACOAArCQETEBCmx0AYACsHATFgABsIATSgABY3MBAAA6MAAAAQAAAAAACQAQAAAAhhGHEYkSihJjE2QTtBO1EyQUJRTzgAJkZScBAICFoNAAABOJsBAFTQABoDAAABAxAggL") \ No newline at end of file diff --git a/pr/395/docs/search.index/12e5c196480d.js b/pr/395/docs/search.index/12e5c196480d.js new file mode 100644 index 000000000..0b16628a5 --- /dev/null +++ b/pr/395/docs/search.index/12e5c196480d.js @@ -0,0 +1 @@ +rn_("AQIBoKAAABLzZPJfBwAAAQABgQKgMAAAE+OgUAAAE3ZjcwEEA7AgEPsAAaBQAAARnqBAAAATr2xzdvKqEwAAAQDzAANhZW/yPgQAAAEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/13c59bf23082.js b/pr/395/docs/search.index/13c59bf23082.js new file mode 100644 index 000000000..a124ed558 --- /dev/null +++ b/pr/395/docs/search.index/13c59bf23082.js @@ -0,0 +1 @@ +rn_("AQoA9B8TAAABAA8AAQABgwKgIAAAEAegMAAAEJZpdAEEAbAAEpwAAWT0RhIAAAEAAQABACOGhLCgE8YAAbBAEpoAAbBwE3cAARWAAAEAArAwD/kAAbAQEzcAAXN08hAGAAAmBgGAArDAE/EAAaAAAAAHunR584CFEcEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/14148cabdb79.js b/pr/395/docs/search.index/14148cabdb79.js new file mode 100644 index 000000000..a1d617871 --- /dev/null +++ b/pr/395/docs/search.index/14148cabdb79.js @@ -0,0 +1 @@ +rn_("AQAEoAAAABBNsCAQ+wABoFAAABGeoEAAABOvZGxzdjswAAABAAATAAYAJwYAAE0MAADzDAAAxQ8NAB0RAACqEwEAAQIA9H8RAAABAAEAAQAThAKwIBGDAAFhc/OAAmFl") \ No newline at end of file diff --git a/pr/395/docs/search.index/14351f4f78de.js b/pr/395/docs/search.index/14351f4f78de.js new file mode 100644 index 000000000..63b3c76ec --- /dev/null +++ b/pr/395/docs/search.index/14351f4f78de.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwByEQIA/RIBAIwTAgABBADzdREAAAEAAQAjAAKgYAAAEothcvOdEAAAAQCwAQGAAqEQAAAUDbCAFAUAAWl1NwQAgYexUBQoAAGACXEAFw2wcBNWAAGhMAAAFBwKQBsjgAKgMAAAEi9udg==") \ No newline at end of file diff --git a/pr/395/docs/search.index/1490375c9199.js b/pr/395/docs/search.index/1490375c9199.js new file mode 100644 index 000000000..d6925870b --- /dev/null +++ b/pr/395/docs/search.index/1490375c9199.js @@ -0,0 +1 @@ +rn_("AQIA9zcMAAABADQDAQBBAV8CAQABAwD0pQ8AAAEA4wABAAGBAqAAAAARNqAQAAAPiGV184ACYXIBgQKgkAAAE2uwUBI5AAFidxOAArCQEroAAW5y8wACZXTydxAAAAEAAQEA9Y8MAAABAAEAAQABABOAArCwE2cAAWFvIwACsEANBwABZXQ7MAAAAQAACQAEAEsFAQD9BQEAdQwBAF8RAwABAgA7MAAAAQAACQACAE4IBwCgEgEAAYACsJAS/QABoKAAABNqZXYTgAKxgBQsAAFjbQGCArBAE5QAAaBQAAARlGJmE4ACoEAAABO7ZHMjgAKg0AAAFARjbvOAhBEBQAEFAbBAEt4AAWUBOBIAAEOCA6EAAAAUEKDQAAAT6GttcxOAArBgEJEAAWFyAQUAOzAAAAEAAHwAAgClEAAArhF7AAGCA7AgEqIAAbAgEDUAAbBAESYAAWtsc1OAA7AgE+sAAWt0dQGJhKCAAAAUH6BAAAAT0aBgAAAT8KCQAAAUIwKAChOChKCgAAATabGgFC8AAbDQE8oAAQQBCQGAArFwFCoAAaDQAAATpGNvI4ACsGAQjwABaW8BAAD0QQwAAAEASwABACOBAqDgAAATvWV5AQ0AOjAAAAEAAAAAAAcAEAAAANYS1xJlE2YTmROaE+UT5hMBgQKwQBL0AAGgwAAAE6NlbwECADowAAABAAAAAAAIABAAAACcD50PQBGBEoISaRPoExgUGRTzAQFvOzAAAAEAAAkABACyCQEAUxADAI8QAQBnEwEA84ADbW5yAQAAOzAAAAEAAAYAAgALEwAAbhMFAEOAA7BAE9wAAaAgAAANBmVycyMAArAwE0oAAWNy8jcTAAABAAGBArCAEqYAAaEAAAAT52NmAYACoAAAABBJoAAAABBKNDbzAgF2AQ0NAADzAAFkAXMJAADX/iEAkrCgE5IAAbBwEYYAAbCAEokAAaCwAAASvKBwAAAQfrAACZAAAZ3dfzswAAABAAAWAAsAhgEAAEcFAwD6BQIAaAcBAHIHAgBPDAAAtA8AAAwRAAB1EQIAPhICAM4TAAA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/14bb3c65ae19.js b/pr/395/docs/search.index/14bb3c65ae19.js new file mode 100644 index 000000000..504071bbf --- /dev/null +++ b/pr/395/docs/search.index/14bb3c65ae19.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwByEQIA/RIBAIwTAgABBADzdREAAAEAAQAjAAKgYAAAEothcvOdEAAAAQCwAQGAAqEQAAAUDbCAFAUAAWl1NwQAgoexUBQoAAGACXEAFw2wcBNWAAGhMAAAFBwKQBsTgAKwEBKXAAFpcg==") \ No newline at end of file diff --git a/pr/395/docs/search.index/173afb185531.js b/pr/395/docs/search.index/173afb185531.js new file mode 100644 index 000000000..adc251d77 --- /dev/null +++ b/pr/395/docs/search.index/173afb185531.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgALEwAAbhMFACOIArBAE9wAAWVzAQQBsDAS4gABY/MzEQAAAQABAAEFADswAAABAAAIAAMAohECAJASBACdEwAA8wMDY2Vw89oMAAABAAEAAQABsGAThgABZQG7EwAA8wIBc/Z+CQAAAQABAHQHPQAKAQECAaBQAAARMnIB1BAAAPOAA2ZtcA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/1754ac96d54a.js b/pr/395/docs/search.index/1754ac96d54a.js new file mode 100644 index 000000000..bcb5682d6 --- /dev/null +++ b/pr/395/docs/search.index/1754ac96d54a.js @@ -0,0 +1 @@ +rn_("AQACoDAAABDKoJAAABMQMGj1qBEAAHABAQABAAEAE4ACoDAAAA+NZHIjAISwEAlSAv+wIAwkAPagMAAAD7ESABj1FQYAAC8AAQABAAEAAQIA9B0UAAABAAoAAQABAgDzKRAAAAEAAQDzgAJpcgEBArBAEQ4AAaBQAAAS+2Vz9S4MAABJAq0CAQBdAgELAPYdEwAAAQCfAFMAFgABAPOAAmx004CEoJAAABLVAUQE") \ No newline at end of file diff --git a/pr/395/docs/search.index/175b4e0f835c.js b/pr/395/docs/search.index/175b4e0f835c.js new file mode 100644 index 000000000..078e8c5a6 --- /dev/null +++ b/pr/395/docs/search.index/175b4e0f835c.js @@ -0,0 +1 @@ +rn_("AYMCsQAUJgABsNAUGAABa20BCgDzeRMAAAEAbQATgAKiEAAAFDFsbzMChKBgAAASKqEAAAAT6QSAAzswAAABAAA0AAIAuQcAAAkIMwA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/176725a3f611.js b/pr/395/docs/search.index/176725a3f611.js new file mode 100644 index 000000000..2d4afbb97 --- /dev/null +++ b/pr/395/docs/search.index/176725a3f611.js @@ -0,0 +1 @@ +rn_("AQAA9s4QAAABAE4DAQAKAAEAAQAAOzAAAAEAAAYAAgALEwAAbhMFACOIArBAE9wAAWVzAQ0BsEAT1gABZfWeEwAAAQABAAEAAQABBQA7MAAAAQAACAADAKIRAgCQEgQAnRMAANcBAACFHr2hQAX1FIQC89oMAAABAAEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/1779ad771958.js b/pr/395/docs/search.index/1779ad771958.js new file mode 100644 index 000000000..545bcbb52 --- /dev/null +++ b/pr/395/docs/search.index/1779ad771958.js @@ -0,0 +1 @@ +rn_("AQEA9WAIAAABAAEAAQABAAEAAqAwAAAQyqCQAAATEDBo9agRAABwAQEAAQABABOAAqAwAAAPjWRyIwKEsBAJUgL/sCAMJAD2oDAAAA+xEgAY9RUGAAAvAAEAAQABADOAhLCgEmIAAbBAD5cAARBJAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/17830149a792.js b/pr/395/docs/search.index/17830149a792.js new file mode 100644 index 000000000..d389d4f56 --- /dev/null +++ b/pr/395/docs/search.index/17830149a792.js @@ -0,0 +1 @@ +rn_("AYADsAATXgABsAATYAABsHATzwABNDZhAQQA9B0UAAABAAoAAQAjgQKgQAAAEQttcAEBADswAAABAAAIAAMAaBIAANESAAAqFAYAhwMAAMagQAAAEBKgYAAAE4OggAAAEkVAB4AIOzAAAAEAABYACACyDwEAxBABAAcRAwB4EQYAPRMCAIkTAADfEwEADhQBAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/182b6e2dbd4a.js b/pr/395/docs/search.index/182b6e2dbd4a.js new file mode 100644 index 000000000..adab692e3 --- /dev/null +++ b/pr/395/docs/search.index/182b6e2dbd4a.js @@ -0,0 +1 @@ +rn_("AQMA8yAQAAABAAEAAYACoLAAABObsDAS9gABbnRDAAOgIAAAEMmwUBGaAAFlaGk7MAAAAQAABgADADAMAACpEQMAShMBAPOAAmRy") \ No newline at end of file diff --git a/pr/395/docs/search.index/1889fd721cb5.js b/pr/395/docs/search.index/1889fd721cb5.js new file mode 100644 index 000000000..b0006389b --- /dev/null +++ b/pr/395/docs/search.index/1889fd721cb5.js @@ -0,0 +1 @@ +rn_("AQUA9F0SAAABAJoAAQABCAD0CBMAAAEAgQABADMBA7BAEt4AAWFiZfX/DAAAAQD0AkQCwwABAAD1SAwAAAEAMQMBAAEA84ACc3UjgQKwgBMDAAFydAGAArBwEpcAAaAQAAAM4256AQYA9DsQAAABAK8AAQBzAISgoAAAFDECART19QUAAAEA2w1QAAEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/18ccfd1e3472.js b/pr/395/docs/search.index/18ccfd1e3472.js new file mode 100644 index 000000000..f17f3ae32 --- /dev/null +++ b/pr/395/docs/search.index/18ccfd1e3472.js @@ -0,0 +1 @@ +rn_("AQEA9aAJAAABAAEAAQABAPMAAWnz+QwAACEHAQABBAKgIAAAEYWgMAAAEj1iaAGLDwAApwAAgMWgEAAADPqwEBQHAAGgMAAAEDQEECgB") \ No newline at end of file diff --git a/pr/395/docs/search.index/196b6ab52bc1.js b/pr/395/docs/search.index/196b6ab52bc1.js new file mode 100644 index 000000000..df2ef9eaf --- /dev/null +++ b/pr/395/docs/search.index/196b6ab52bc1.js @@ -0,0 +1 @@ +rn_("AQAA9C8QAAABAKcAAQABhAKwABErAAGwABEtAAE0NvMCAWHyigwAAAEAAQEA9VgSAABmAAEANwEBAPOAAmVv84ACaGs=") \ No newline at end of file diff --git a/pr/395/docs/search.index/1a14b9a7d2f6.js b/pr/395/docs/search.index/1a14b9a7d2f6.js new file mode 100644 index 000000000..3cb950feb --- /dev/null +++ b/pr/395/docs/search.index/1a14b9a7d2f6.js @@ -0,0 +1 @@ +rn_("AYQCsCARMQEKoJAAABOdbXABgAKgMAAAEL6gUAAAE0ZjcpcAAACFsCATFgABsMATzAABoGAAABGIEEAOOzAAAAEAABIABgBWBQgAhhEBAIkSAQBjEwEAtBMBACQUAQA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/1b113bad2321.js b/pr/395/docs/search.index/1b113bad2321.js new file mode 100644 index 000000000..b0eb406a7 --- /dev/null +++ b/pr/395/docs/search.index/1b113bad2321.js @@ -0,0 +1 @@ +rn_("AYICsCAQ9QABsDARawABZXABBQD0ow8AAAEA4wABAFOAA7CQEZwAAWFpcgGBArCgE5IAAbCgE5QAAWNyxwAAAIWwUA0BAAGgIAAACcOgIAAADDw8AAE6MAAAAQAAAAAACgAQAAAAjwGQAS4EZwd6CXsJNRA2EJ8QoBCtEQ==") \ No newline at end of file diff --git a/pr/395/docs/search.index/1b5c0368d221.js b/pr/395/docs/search.index/1b5c0368d221.js new file mode 100644 index 000000000..39f1850a0 --- /dev/null +++ b/pr/395/docs/search.index/1b5c0368d221.js @@ -0,0 +1 @@ +rn_("AQcA9VgTAAABAAEAcAABAAEHAPN7EAAAAQABAAGAAqAAAAAHxKAQAAAJZGR48wEBZfLWDwAAAQDnAAAAhbBgDPcAAbBADHUAATIIAfRUBQAAZgIhCQEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/1c404dc1a453.js b/pr/395/docs/search.index/1c404dc1a453.js new file mode 100644 index 000000000..adfcfd497 --- /dev/null +++ b/pr/395/docs/search.index/1c404dc1a453.js @@ -0,0 +1 @@ +rn_("AYECsDAP+QABsBATNwABc3QBAwDzXxIAAAEAAQAjgAKwABFrAAFoaQEAADswAAABAAATAAEA7wcTACOAAqAwAAAM6GFl84ACcnTzAAJhbDswAAABAAAoAAMAhwATAIgBAADfDxMA") \ No newline at end of file diff --git a/pr/395/docs/search.index/1d8c01ecbe8e.js b/pr/395/docs/search.index/1d8c01ecbe8e.js new file mode 100644 index 000000000..e334b8c59 --- /dev/null +++ b/pr/395/docs/search.index/1d8c01ecbe8e.js @@ -0,0 +1 @@ +rn_("AQIA9F8RAAABAAEAAQABCgDzPRMAAAEAAQDzgQJtdAEGAPMfEQAAAQABAAGCArBgEs0AAbDQE9gAAWlyE4ACsBAM9AABZWkBBQD0fRMAAAEARgABAPMEAWPylRIAAAEAI4ICoFAAABJpZnQBAAGwABPPAAFk9B4MAAABAAEAAQDzgQJlaTcGAACJsTAT9AABoGAAABEeoFAAABCxsJASpAABsEAQrwABtRAHAa8TAAA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/1e253efce535.js b/pr/395/docs/search.index/1e253efce535.js new file mode 100644 index 000000000..f9c55b406 --- /dev/null +++ b/pr/395/docs/search.index/1e253efce535.js @@ -0,0 +1 @@ +rn_("AQAA9LgQAAABAHkBAQAjgQKgYAAAEsdiaQEAAPSuDwAAAQDmAAIBI4ECsFAS2gABZXMTgAKwAAe7AdxhcgGJArAgE+EAAaAwAAAT6mlwAQIA9KwSAAABAIYAAQABAwD04BIAAAEAiwABAPOFAmJ1AQIA9CIRAAABAGABAQDzgAJpbLcBAACFoCAAABK0EgEMOjAAAAEAAAAAABYAEAAAANsHZwhoCGkIaggyDGQMZQxmDGcM1Q+OEPYS9xIqE6UT7RPuE/AT+xP8Ex8UIxQ=") \ No newline at end of file diff --git a/pr/395/docs/search.index/1e45b5c47ee4.js b/pr/395/docs/search.index/1e45b5c47ee4.js new file mode 100644 index 000000000..fd745841c --- /dev/null +++ b/pr/395/docs/search.index/1e45b5c47ee4.js @@ -0,0 +1 @@ +rn_("AQMA9gwNAACcAlkAAQCiAgEAAQQAOzAAAAEAAAoABABDEAUAZBIBALoSAQDJEwAAAYYCoAAAABLcoAAAABLdZHLzAgF2AQITAADzgAJlbycBAADHoDAAAA+noHAAABI3oIAAABOcoNAAABNCoLAAABOcQEUICTswAAABAAAJAAMAOQYCAIgHBABzDAEAI4ACYDepXPmrZHIBAAA6MAAAAQAAAAAACgAQAAAApwmoCdwP9BL1Eh0THhO9ExAUJhQnFBOAAqAwAAAQWWx084ACYWU=") \ No newline at end of file diff --git a/pr/395/docs/search.index/1e521ee4a588.js b/pr/395/docs/search.index/1e521ee4a588.js new file mode 100644 index 000000000..54475ce77 --- /dev/null +++ b/pr/395/docs/search.index/1e521ee4a588.js @@ -0,0 +1 @@ +rn_("AQIA9KwSAAABAIYAAQABAwD04BIAAAEAiwABAPMCAmJ1OjAAAAEAAAAAABMAEAAAALQHtQe2B5kPmg87EDwQWhBbEOsQ7BCYEZkRqhKrErcSuBLHEjETMhM=") \ No newline at end of file diff --git a/pr/395/docs/search.index/1ebda14005f5.js b/pr/395/docs/search.index/1ebda14005f5.js new file mode 100644 index 000000000..f4a3c50db --- /dev/null +++ b/pr/395/docs/search.index/1ebda14005f5.js @@ -0,0 +1 @@ +rn_("AQMBsDAS4gABY/MzEQAAAQABAAEDAYAS/wAQ6XM7MAAAAQAABQABAOQSBQAjggKgkAAAE9JpcwcBAIDFsFASrgABoKAAABNDsHATDQABoNAAABPegAEYCPOAAmRy") \ No newline at end of file diff --git a/pr/395/docs/search.index/1f4506df5023.js b/pr/395/docs/search.index/1f4506df5023.js new file mode 100644 index 000000000..8e954b1b8 --- /dev/null +++ b/pr/395/docs/search.index/1f4506df5023.js @@ -0,0 +1 @@ +rn_("AYQCsAARKwABsAARLQABNDbzAgFh8ooMAAABAAEAAPVYEgAAZgABADcBAQDzAAFk82YSAAABAJoAY4ADoCAAAAlUYWVv") \ No newline at end of file diff --git a/pr/395/docs/search.index/220c15e98816.js b/pr/395/docs/search.index/220c15e98816.js new file mode 100644 index 000000000..5f4e012d1 --- /dev/null +++ b/pr/395/docs/search.index/220c15e98816.js @@ -0,0 +1 @@ +rn_("AQMA9gwNAACcAlkAAQCiAgEAAQQAOzAAAAEAAAoABABDEAUAZBIBALoSAQDJEwAAAYYCoAAAABLcoAAAABLdZHLzAgF2AQITAADzgAJlbycBAADHoDAAAA+noHAAABI3oIAAABOcoNAAABNCoLAAABOcQEUICTswAAABAAAJAAMAOQYCAIgHBABzDAEAI4ACYDepXPmrZHIBAAA6MAAAAQAAAAAACgAQAAAApwmoCdwP9BL1Eh0THhO9ExAUJhQnFBOAAqAwAAAQWWx0w4AEoAAAABBJoAAAABBKNDZhZQ==") \ No newline at end of file diff --git a/pr/395/docs/search.index/225e079aa968.js b/pr/395/docs/search.index/225e079aa968.js new file mode 100644 index 000000000..6702d25d2 --- /dev/null +++ b/pr/395/docs/search.index/225e079aa968.js @@ -0,0 +1 @@ +rn_("AYMCsQAUJgABsNAUGAABa20BCgDzeRMAAAEAbQATgAKiEAAAFDFsbzMBhKBgAAASKqEAAAAT6QSAAzswAAABAAA0AAIAuQcAAAkIMwA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/25add3d45510.js b/pr/395/docs/search.index/25add3d45510.js new file mode 100644 index 000000000..af7492703 --- /dev/null +++ b/pr/395/docs/search.index/25add3d45510.js @@ -0,0 +1 @@ +rn_("AYACoIAAABJosHATowABYXMBAAGggAAAFCBm8lUJAADACQEHAPXqEgAAAQABAAEAAQAjgAKgUAAAERBhb/MAAmZzOzAAAAEAAAgABAC3BwAA6QcFAA8NAABJEQAA84ACZHk=") \ No newline at end of file diff --git a/pr/395/docs/search.index/25c8d0469b0f.js b/pr/395/docs/search.index/25c8d0469b0f.js new file mode 100644 index 000000000..dbb3084d7 --- /dev/null +++ b/pr/395/docs/search.index/25c8d0469b0f.js @@ -0,0 +1 @@ +rn_("AQQA9OQTAAALAB4ADwABAgGwQBJRAAFlAYwPAAABgAKgkAAAE6qgkAAAE6syM5cCAADGoCAAAAyJoGAAABDzsGANEwABAWQAAzswAAABAAAGAAIAggcFAFoMAAA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/263fb2099b3d.js b/pr/395/docs/search.index/263fb2099b3d.js new file mode 100644 index 000000000..c8d46b57e --- /dev/null +++ b/pr/395/docs/search.index/263fb2099b3d.js @@ -0,0 +1 @@ +rn_("AQEA9RoGAABDDAEAmgABAAEAADswAAABAAAHAAMA6gEBAIEMAQDYDwMAAYMCsHATFgABsIATSgABY3MBBQD0wBIAAAEAhwABAPMIAWP1EBAAAAEAugABAAEAAQABoDAAABE2dPJ3EAAAAQABgAKgAAAAEEmgAAAAEEo0NvMDAXYBDQ0AAJcAAACFsKASpgABsCAMQQABsIASiQABCQQGOjAAAAEAAAAAAAkAEAAAAHIHcwd0B08MtA8MET4SPxJAEs4TAQIA9KwSAAABAIYAAQABAwD04BIAAAEAiwABAPMAAmJ1OjAAAAEAAAAAABUAEAAAALQHtQe2B5kPmg87EDwQWhBbEOsQ7BCYEZkRqhKrErcSuBLHEvES8hIxEzITAQEA9BYGAAABAOYGAQC3AACAhbEAE7QAAbAAB74AAQwIRAEIADswAAABAAAFAAEAbBIFANcNAACIsEAQ2wABoCAAAA0XuQALOzAAAAEAAA8ABQBsAAEAagcHAM0HAQAvEAEA1xABAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/269534fbdbd0.js b/pr/395/docs/search.index/269534fbdbd0.js new file mode 100644 index 000000000..335d0bfa9 --- /dev/null +++ b/pr/395/docs/search.index/269534fbdbd0.js @@ -0,0 +1 @@ +rn_("AQYA9DkTAAABAAEAAQABDwD0EhQAAAEAAQABAPOHAmhtAQQBsAASnAABZPRGEgAAAQABAAEAI4eEsKATxgABsEASmgABsHATdwABFYAAAQABoEAAABBLZTswAAABAAAGAAIA1gcEAGUIAQATgAKwUBAIAAFkcgEJAPVYEwAAAQABAHAAAQC3AgCAhrAgEk8AAWTkZlDf5BFJAQ==") \ No newline at end of file diff --git a/pr/395/docs/search.index/283059613ad2.js b/pr/395/docs/search.index/283059613ad2.js new file mode 100644 index 000000000..cadacffca --- /dev/null +++ b/pr/395/docs/search.index/283059613ad2.js @@ -0,0 +1 @@ +rn_("AQIA9F8RAAABAAEAAQABAQDzdREAAAEAAQATgAIXgwFJp5JlcwEKAPM9EwAAAQABAAGAArAgE7IAAaBgAAATDG92AYACoDAAABJYoAAAABLMY2TzgIUFEAY=") \ No newline at end of file diff --git a/pr/395/docs/search.index/287195228062.js b/pr/395/docs/search.index/287195228062.js new file mode 100644 index 000000000..377a5c060 --- /dev/null +++ b/pr/395/docs/search.index/287195228062.js @@ -0,0 +1 @@ +rn_("AQQA84APAAABAAEAAYACoFAAABCGoGAAABD0aXIBgAKgIAAADImgYAAAEPNlaAEBAaAgAAAQ8mLzhQwAAAEAAQATgQKgEAAADIhhYwEGAPSeEgAAAQB3AQEAEwACsGASoAABZm3y7QwAAAEAI4ACoMAAABNdbnInDwAAiKBAAAAPfbBAD34AAaAwAAAMhDQgFzswAAABAAAZAAMA4QEAAB8EDgATDAkAAYADsHATFgABsDASyAABsIATSgABY3JzAQABsCATFgABbzowAAABAAAAAAAJABAAAACGEYcRiRKKEmMTZBO0E7UTJBQlFAGAArAgCXwAAbAwEtMAAXJ09wAAAMegIAAAB92gYAAAD6mwgBLTAAFABpII9u4BAABVAn4FAQC1CQEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/288ad6e859a7.js b/pr/395/docs/search.index/288ad6e859a7.js new file mode 100644 index 000000000..7f6c38d80 --- /dev/null +++ b/pr/395/docs/search.index/288ad6e859a7.js @@ -0,0 +1 @@ +rn_("AYACoGAAABOCoKAAABN2YWkBCwD0GBMAAAEAAQABAAEEAPRdEAAAAQABAAEAAQQA9LIQAAABAAEAAQDzgAJscvMAA2NkcwGpEAAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/2937f2ef82d6.js b/pr/395/docs/search.index/2937f2ef82d6.js new file mode 100644 index 000000000..4991a5a10 --- /dev/null +++ b/pr/395/docs/search.index/2937f2ef82d6.js @@ -0,0 +1 @@ +rn_("AQQA83URAAABAAEAAYICsEASWQABsEASWwABNDYBBgD0HRQAAAEACgABAPMCAXIBiRMAAPMCAWX0NxAAAAEApgABABOAAqBQAAASi2R00wCEsLATpgABEQEB950QAAABAG0AHQABAAEAJAE=") \ No newline at end of file diff --git a/pr/395/docs/search.index/29d3c2222f61.js b/pr/395/docs/search.index/29d3c2222f61.js new file mode 100644 index 000000000..08f3746c4 --- /dev/null +++ b/pr/395/docs/search.index/29d3c2222f61.js @@ -0,0 +1 @@ +rn_("AQIBsEAT6wABdDswAAABAAANAAYAjBIDAO8SAQBTEwEAahMBAOETAQAFFAEAE4ACsDASuQExaXABBQDzKxMAAAEAAQATgAKgMAAAE4FleVMAA7BwE48AAWJlcwH7DwAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/2a562ad3f29f.js b/pr/395/docs/search.index/2a562ad3f29f.js new file mode 100644 index 000000000..79f660959 --- /dev/null +++ b/pr/395/docs/search.index/2a562ad3f29f.js @@ -0,0 +1 @@ +rn_("AQYA9GMTAAABAFAAAQAjgAKhEAAAFBFjchOAAqAgAAAJZWh5AQEBoGAAABQEc/KDEQAAAQAjgAKwIAnFAAFldQGAArAgD90AAaCQAAATu2FlAQcBsEAT1gABZfWeEwAAAQABAAEAAQABBAGwABKwAAFzOzAAAAEAAAsAAgAhEwcACRQDAEcAAASGoIAAABMFsQAUFgABsEAScwABsIAUJAABsFASsgABFQAW9HgOAAABAAEAAQCDAISgAAAAE4OwQBQaAAGwMBM1AAFIAAP3RgUAAHkLAQCjAAEABQABAAEDAbBAE+sAAXTyUxMAAAEAI4UCoEAAABOBYnMBAQD0fxEAAAEAAQABABOAAqAQAAAJhmRst+gAAIyg0AAAEw+gUAAAER2gkAAAEymwUBKXAAGgMAAADIO3mAc7MAAAAQAAYAATANkBAQDfAQEAGwQDAEAGAAAvBwEAywcBAN0HAABiCQEAbgkAAHkJAACmCQAAmQw9AOkMAACqDwEAeRABAJMQAQDZEAEAWhEEAE8SAQA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/2aa2debf8ed5.js b/pr/395/docs/search.index/2aa2debf8ed5.js new file mode 100644 index 000000000..46099207f --- /dev/null +++ b/pr/395/docs/search.index/2aa2debf8ed5.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABLSc/SPDwAAsQMBAAIAAQECsGATsgABsEAT6wABZHQ7MAAAAQAADQAGAIwSAwDvEgEAUxMBAGoTAQDhEwEABRQBAAEBA7BwEaYAAbCAEmoAAYAS/wAQ6W1uczswAAABAAALAAIATAkFAOQSBQCjgISgsAAAE0KxcBQkAAEIQIIBAQA7MAAAAQAABgABACoUBgDzBgFl9N8TAAABAC4AAQATAQOwQBQHAAGgYAAAE4hhcnUBMxAAAFcBAACFsRATsgABsDASuQExEYEIOzAAAAEAAAYAAgALEwAAbhMFAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/2aaca7f8de58.js b/pr/395/docs/search.index/2aaca7f8de58.js new file mode 100644 index 000000000..70b81bbc7 --- /dev/null +++ b/pr/395/docs/search.index/2aaca7f8de58.js @@ -0,0 +1 @@ +rn_("AQIAOjAAAAEAAAAAAA4AEAAAABgNGQ3DEJ8RoBGhEUoSURJSElYSVxKmEqcS2hLbEgEJAPMoEQAAAQABAAEKAPNfEgAAAQABAAEGAPXtEAAAAQABAAEAAQDzgAJkbnMAhLEgE7IAAQARA/KuEgAAAQABDQD2HRMAAAEAnwBTABYAAQABgAKwoBM1AAGgEAAADFBjdQEBAPREEwAAAQBKAAEA8wABZfLFCQAAAQD3AACAxbAwEGoAAQACqAg=") \ No newline at end of file diff --git a/pr/395/docs/search.index/2b2167e7f8b2.js b/pr/395/docs/search.index/2b2167e7f8b2.js new file mode 100644 index 000000000..a4693a3aa --- /dev/null +++ b/pr/395/docs/search.index/2b2167e7f8b2.js @@ -0,0 +1 @@ +rn_("AQIA9Y4SAAABANwAdgABABOAArAwErkBMWlwAQYA8ysTAAABAAEA8wICYnMB+w8AAPMBAWby0AcAACIJAQIA9OASAAABAIsAAQAzgAOwQBKuAAFpdXk=") \ No newline at end of file diff --git a/pr/395/docs/search.index/2b7cfd46cb26.js b/pr/395/docs/search.index/2b7cfd46cb26.js new file mode 100644 index 000000000..634048365 --- /dev/null +++ b/pr/395/docs/search.index/2b7cfd46cb26.js @@ -0,0 +1 @@ +rn_("AYACoCAAAA+EoDAAABJ/ZXIBAAKgIAAAEw+wMBGSAAFpdPKFEgAAAQABAwA7MAAAAQAABgABABERBgABCAD0dBMAAAEAfgAFABMBA7CQE9MAAbBQEsYBS2dscDswAAABAAASAAkAIxACAJURAQC1EgEA1hIBAAYTAQBlEwEAmRMBAKgTAQDlEwEAAYACsDARjgABsDARkAABcnYBBAGwABJLAAFkOzAAAAEAAAUAAgCKEQMAdRIBALcFAAGHsFASTQD6sGASxAABlAkDOzAAAAEAAAoABQDqDAIAfQ4BALoQAAA8EQMAvBIAACOAAqAQAAAM6W5vAQMA8yAQAAABAAEAI4ACoAAAAAwwZHJjAYSgUAAAETmgMAAAEFgUARDyAwgAAAEAE4ADsGASagABsAAPbgABY2dyVwAAAIWgsAAAE5GgQAAADnygIAAAEVkQwAoBcA8AAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/2c24473cb3e9.js b/pr/395/docs/search.index/2c24473cb3e9.js new file mode 100644 index 000000000..333f02624 --- /dev/null +++ b/pr/395/docs/search.index/2c24473cb3e9.js @@ -0,0 +1 @@ +rn_("AQEA99AQAAABAAEAogIBAH4ABQABAgD0ZRAAAAEAAQABABOAAqDQAAAT42Nz84ACZW8jgQKwAAe8AAFsdAEAAPQrBgAAAQABAAEAU4ADoFAAABQxZG5y") \ No newline at end of file diff --git a/pr/395/docs/search.index/2c46939b6ca2.js b/pr/395/docs/search.index/2c46939b6ca2.js new file mode 100644 index 000000000..17d6bfd19 --- /dev/null +++ b/pr/395/docs/search.index/2c46939b6ca2.js @@ -0,0 +1 @@ +rn_("AQcA9h0TAAABAJ8AUwAWAAEA8wEBaQGxCQAAAQAA9bQJAAABAAEAAQABAEcBAIDGHYwB7L6OoKAAABQDsGATfwABoBAAAAmNAAogig==") \ No newline at end of file diff --git a/pr/395/docs/search.index/2dd8fd656c06.js b/pr/395/docs/search.index/2dd8fd656c06.js new file mode 100644 index 000000000..43cfd3b11 --- /dev/null +++ b/pr/395/docs/search.index/2dd8fd656c06.js @@ -0,0 +1 @@ +rn_("AQABoDAAABE2dPJ3EAAAAQABgAKgAAAAEEmgAAAAEEo0NvMDAXYBDQ0AABcBAACHsKASpgABsCAMQQABsGASogABsIASiQABoCAAABCuCURGOjAAAAEAAAAAAAkAEAAAAHIHcwd0B08MtA8MET4SPxJAEs4T") \ No newline at end of file diff --git a/pr/395/docs/search.index/2eb373ad3c24.js b/pr/395/docs/search.index/2eb373ad3c24.js new file mode 100644 index 000000000..b70afb7ea --- /dev/null +++ b/pr/395/docs/search.index/2eb373ad3c24.js @@ -0,0 +1 @@ +rn_("AYkCsCAT4QABoDAAABPqaXATAAOgIAAAErSggAAAEsdlaXX1MgwAADIAAQABAAEAAYYCsJAUBQABoAAAABJOYmQBgAKwMBKoAAGgMAAAEKZydtcAAACFoDAAABAmoDAAABL8EAETOjAAAAEAAAAAAAkAEAAAAFYJVwlYCYgJiQk9EEIQ9xD4EIsS") \ No newline at end of file diff --git a/pr/395/docs/search.index/2f71766d07cc.js b/pr/395/docs/search.index/2f71766d07cc.js new file mode 100644 index 000000000..0e8fb9b4f --- /dev/null +++ b/pr/395/docs/search.index/2f71766d07cc.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABNdcwGnEAAAAYACsDAQPgABsFAQQAABYWkBgAKgsAAAE+OgMAAAENNsbhcBAACFoCAAABI0oCAAAAlTsIATsAABEJEEOjAAAAEAAAAAABAAEAAAACoFXwVgBWEFYgVjBWQFvQl1D3YP0hKBE4gTlBOVE6wTrRMBAgA6MAAAAQAAAAAACgAQAAAApwmoCdwP9BL1Eh0THhO9ExAUJhQnFAEAADswAAABAAAGAAIACxMAAG4TBQABAgGwMBLiAAFjOjAAAAEAAAAAABMAEAAAAAYNjQ8gECEQIhAnECgQLBAtEL0QLxEzETQRNRGlEdgS2RKuE9MT1BMBggKwMBOmAAGwQBEbAAFhZQEBAbBAE9YAAWX1nhMAAAEAAQABAAEAE4ACoBAAAAwxYW/HBwAAx7BAE9wAAaCAAAASLwBMiAnyXggAANcDAYACoIAAABL7oJAAABLVYWsBAwDzUxIAAAEAAQAjgISxIBQkAAGgAAAACYegMAAAD5QEBAYBCAD1LBQAAAEAAQABAAEAE4oCsEAUBwABYXKTAISgwAAAEtGhAAAAE5EBQAby4QcAAAEAAQMBoEAAABGfZfMQDQAAtwMBAAEFAPQ5EwAAAQABAAEAU4ADsAAS9gABZXN0AQYA9BIUAAABAAEAAQDzCAFt9EESAAABAAEAAQABAgA7MAAAAQAABQABAP0TBQDzDQFtOzAAAAEAAAUAAQC+EwUAAQQA9xgNAAABADEFDAABAIMAAQABAwGAEv8AEOlzOzAAAAEAAAUAAQDkEgUAI4ICoJAAABPSaXMBgAKgsAAAE5uwMBL2AAFudAGAAqCAAAATELBAEZoAAWFlAYACoBAAABDJoAAAABENbnIBCQGgQAAAE9Vl8vQTAAABACOAAqCQAAATQ2lsxyMAAAqgMAAAEMqwUBKuAAGw8BQaAAGwcBMNAAGg0AAAE94wYmNlaGlrbm92OzAAAAEAABkACwAwDAAAoRADAKgRBAAKEwAAGBMDAEoTAQCREwAAlhMCALATAQC8EwAAzxMBAAECAqCwAAATyKCwAAATyWNz84AQAAABAFwAAQECsFARmgABoMAAABObaGnyShMAAAEA84ACZWkBAQGgkAAAEwpz9GUQAAABAAEAAQABAwGwABCbAAFzAQ8QAADzgANoaXQBAQGgUAAAEpljAd4MAAABBQGwgBPPAAF2OzAAAAEAAAcAAgB4EQQAPRMCAAEBAPQvEAAAAQCnAAEA84ADY2Rs92UPANGwkBItAAGgUAAAEMKhAAAAFAOgoAAAEyqgEAAAB7hAP78pOzAAAAEAADcAFgBSBQEAEgYCAH8HAgAeDAMA4QwBAIAPAgCPDwAAtQ8AAN0PAQDgEAgANhEAAGoSAQBzEgEAdxIBAM0SAQDqEgQANxMBAEATAQBDEwAAthMEAAMUAAAgFAAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/301f4a70d960.js b/pr/395/docs/search.index/301f4a70d960.js new file mode 100644 index 000000000..2a377da1c --- /dev/null +++ b/pr/395/docs/search.index/301f4a70d960.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAQD9EwUA8w0BbTswAAABAAAFAAEAvhMFAEcAAACFsAATzwABsEASVgABoQAAABQDoAAAABOIOAgCOzAAAAEAABgABgAeDAMA4RAHAHcSAQDqEgQAthMEACAUAAA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/3071a6cda431.js b/pr/395/docs/search.index/3071a6cda431.js new file mode 100644 index 000000000..4997a27ea --- /dev/null +++ b/pr/395/docs/search.index/3071a6cda431.js @@ -0,0 +1 @@ +rn_("AYECoCAAABCosFASTwABYW8BAwGgAAAAE11zAacQAAAjgAKgAAAACF5ydmMAA7BQERgAAWNlczswAAABAAAIAAQAXQkCAIsJAQApEAIANxIAAAcBAICFsFAQ2QABoKAAABMpsPATQAABsCAPbgABRCEE") \ No newline at end of file diff --git a/pr/395/docs/search.index/30f9df4e9837.js b/pr/395/docs/search.index/30f9df4e9837.js new file mode 100644 index 000000000..0c8e4181b --- /dev/null +++ b/pr/395/docs/search.index/30f9df4e9837.js @@ -0,0 +1 @@ +rn_("AQIA9JQMAAABAAEAAQABAgDzKBEAAAEAAQABBgD0EhQAAAEAAQABAPMGAW30QRIAAAEAAQABAPOAAm5284ACYWk=") \ No newline at end of file diff --git a/pr/395/docs/search.index/30ffbfdd11c6.js b/pr/395/docs/search.index/30ffbfdd11c6.js new file mode 100644 index 000000000..8a53f3c5d --- /dev/null +++ b/pr/395/docs/search.index/30ffbfdd11c6.js @@ -0,0 +1 @@ +rn_("AQIBsEATNQABZfIvBwAAAQABBADzKRAAAAEAAQBDAAOwQBKmAAGxABN5AAFlZnA7MAAAAQAAIwAGAPABAAA+BwwATAcMAGYPBQC+EAAA4hIBAGOAA7AwDDMAAWFlaQ==") \ No newline at end of file diff --git a/pr/395/docs/search.index/31340ca2241e.js b/pr/395/docs/search.index/31340ca2241e.js new file mode 100644 index 000000000..597c83962 --- /dev/null +++ b/pr/395/docs/search.index/31340ca2241e.js @@ -0,0 +1 @@ +rn_("AYICsEAT0wABsGASvgABZXIBBQD0UxAAAAEAAQABAPOAAmZtAQYAOjAAAAEAAAAAAAgAEAAAAJwPnQ9AEYESghJpE+gTGBQZFAEAAaFAAAAUEWf05AcAAAEAAQABAMMAhKCwAAATUrBAEGoAARQgBPVcBwAAgQUtA0MC+gBjgAOwMA/WAAFhZWk=") \ No newline at end of file diff --git a/pr/395/docs/search.index/3210ce3940fa.js b/pr/395/docs/search.index/3210ce3940fa.js new file mode 100644 index 000000000..3f9dbbcea --- /dev/null +++ b/pr/395/docs/search.index/3210ce3940fa.js @@ -0,0 +1 @@ +rn_("AYACoCAAAA+QoDAAABNSYXLzAAF0AegHAAABBAGgQAAAE9Vl8vQTAAABAAEFADswAAABAAALAAUAExAFAGISAQB5EwEAyBMAAOcTAADjAISgMAAAD46FAAI7MAAAAQAACwAFAB4GAgApBwQAcAwBADkRAAByEgAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/3240004448f4.js b/pr/395/docs/search.index/3240004448f4.js new file mode 100644 index 000000000..f1cfb1757 --- /dev/null +++ b/pr/395/docs/search.index/3240004448f4.js @@ -0,0 +1 @@ +rn_("AYACsHAUBQABoAAAAAUyZmcBAQA7MAAAAQAABwABAFwMBwABAQGgcAAAEjVhAXwHAAAjgAKhAAAAFA1kbnOAhKBgAAASNABBSA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/32b9b5daf9e8.js b/pr/395/docs/search.index/32b9b5daf9e8.js new file mode 100644 index 000000000..6cbe3a334 --- /dev/null +++ b/pr/395/docs/search.index/32b9b5daf9e8.js @@ -0,0 +1 @@ +rn_("AQkA8+0TAAABADUAAYACsCATkgABoCAAAA+UaXkBCQGggAAAFCBmARUTAAABAwGwABOlAEtzAVITAAAjgAKgIAAAD5BhcgECAPWLEAAAAQABADUCAQDzAANpdHU6MAAAAQAAAAAADQAQAAAA6AeHCfMPaRCAErASsRJWE1cTXROGE4cToxOkEwGAAqAwAAAT0bCwEy8AAWFsAQwBoKAAABQxd/IhFAAAAQABAQDztg8AAAEAAQDzgAJ0dQEDAPNTEgAAAQABAAEBAPXtEAAAAQABAAEAAQAjgAKwkBN/AAFhdAEEAaBAAAAT1WXy9BMAAAEAAQIAOzAAAAEAAAsABQATEAUAYhIBAHkTAQDIEwAA5xMAABOCArDwFCQAAWZnAYECsIAT+wABoGAAABQfZHMBCAD1thMAAAEAAQABAAEA8wIBZvVOEAAAAQABAAEAAQAjgAOgIAAAD46woBP5AAFkcnR37wEAjbAQE7AAAbDgE7IAAZfVDjswAAABAAAXARIAOgQBAEAFAgAeBgIAKQcEAFgIAwBFDAIAcAwBAO8MAQB/DuYArA8BAGoQAQB7EAIAphABADkRAACQEQEAchIAACETBwAJFAMA") \ No newline at end of file diff --git a/pr/395/docs/search.index/32ee32d485a7.js b/pr/395/docs/search.index/32ee32d485a7.js new file mode 100644 index 000000000..5c98cc0de --- /dev/null +++ b/pr/395/docs/search.index/32ee32d485a7.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAYAAQAREQYAAQAA93oJAAABALoGAQBpAAEADQEBggKgIAAAENWgMAAAETtpdPOAA2FncwEBAaBAAAATrmXy2BMAAAEAI4ACsFATZwABZnPzgAJpbw==") \ No newline at end of file diff --git a/pr/395/docs/search.index/3362c9bd9554.js b/pr/395/docs/search.index/3362c9bd9554.js new file mode 100644 index 000000000..6b4ce731f --- /dev/null +++ b/pr/395/docs/search.index/3362c9bd9554.js @@ -0,0 +1 @@ +rn_("AQABsKASZAABZTswAAABAAAJAAIATggHAKASAQABAAGgIAAADPZtAf8FAAABgAKwoBJiAAGwQA+XAAFsbwEEAbAAEpwAAWT0RhIAAAEAAQABACMChLCgE8YAAbBAEpoAAbBwE3cAARWAAAGcEwAAAQABsIATsAABcPKsEwAAAQBDAAOgIAAAEQ2gQAAAEkpkZXPztQ8AABgDAQDzgQJladcCAACGsCASKwABsNATtAABFwEC9C8FAAABAAEAtQA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/336f27a85aec.js b/pr/395/docs/search.index/336f27a85aec.js new file mode 100644 index 000000000..a4c9d06c6 --- /dev/null +++ b/pr/395/docs/search.index/336f27a85aec.js @@ -0,0 +1 @@ +rn_("AQUA9H0TAAABAEYAAQDzAwFj8pUSAAABACOAArBwFCQAAWhvAYICsAAScwABsAARkgABZXNDAAOgoAAAE8mgIAAAEw9laXTyhRIAAAEAAQIAOzAAAAEAAAYAAQAREQYAAQIBoEAAABOuZfLYEwAAAQDzgAJpbwEIAPR0EwAAAQB+AAUAEwEDsJAT0wABsFASxgFLZ2xwOzAAAAEAABIACQAjEAIAlREBALUSAQDWEgEABhMBAGUTAQCZEwEAqBMBAOUTAQABgAKwQBJNAPqgIAAAENZhaQGBArAwE4QAAaAwAAASaWNpAQMBsAASsAABczswAAABAAANAAMAkBEBACETBwAJFAMAAQMA9Y4RAAABAAsBAQBDAfOAAnJ2AQAA9EsSAAABAFAAAQDzAgFkOzAAAAEAAAkAAwCKEQMARhIDAHUSAQAjgAKgkAAAE8hpbxOAArDwFBYAAWxvAQUA9OQTAAALAB4ADwAjgAKgYAAAEwVncyOAArCQE8YAAWJs98cAAI2wYBP5AAGwcBN3AAGwYBLEAAGwUBKyAAG1yRc6MAAAAQAAAAAAHwAQAAAALwZcCYoJdwx4DOoM6wzsDHgOeQ56DnsOfQ5+DrkPug+7D7wPvQ+AEIEQuhDdEDwRPRE+ET8RiBG8EtMS1BKcEw==") \ No newline at end of file diff --git a/pr/395/docs/search.index/343b00f6b1cb.js b/pr/395/docs/search.index/343b00f6b1cb.js new file mode 100644 index 000000000..5a43c9c0c --- /dev/null +++ b/pr/395/docs/search.index/343b00f6b1cb.js @@ -0,0 +1 @@ +rn_("AQEBsFAPlwABY/QvBQAAAQABALUAAQABoIAAABQgZgEVEwAA8wYBc/IPDQAAOgQzAAOgYAAAFARjaXPygxEAAAEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/34a03cbb2682.js b/pr/395/docs/search.index/34a03cbb2682.js new file mode 100644 index 000000000..355c796a5 --- /dev/null +++ b/pr/395/docs/search.index/34a03cbb2682.js @@ -0,0 +1 @@ +rn_("AQwBoKAAABQxd/IhFAAAAQABAQDztg8AAAEAAQABiYSggAAAFB+gQAAAE9GgYAAAE/CgkAAAFCMCgAoTgYSgoAAAE2mxoBQvAAGw0BPKAAEEAQnzgANhdHU=") \ No newline at end of file diff --git a/pr/395/docs/search.index/34b69ebbda67.js b/pr/395/docs/search.index/34b69ebbda67.js new file mode 100644 index 000000000..1aed34b70 --- /dev/null +++ b/pr/395/docs/search.index/34b69ebbda67.js @@ -0,0 +1 @@ +rn_("ARMA9SwUAAABAAEAAQABABOAAqDAAAAS0WFvAQICoLAAABPIoLAAABPJY3PzgBAAAAEAXAABgQKwUBGaAAGgwAAAE5toafOAAmVpI4ACoLAAABMKaXQBBgGwgBPPAAF2OzAAAAEAAAcAAgB4EQQAPRMCAHcAAACGoKAAABMqoJAAABL7sFAQPgABBeAC9H8HAAABAAEAggw=") \ No newline at end of file diff --git a/pr/395/docs/search.index/34cae06d030c.js b/pr/395/docs/search.index/34cae06d030c.js new file mode 100644 index 000000000..ba1ea5c6b --- /dev/null +++ b/pr/395/docs/search.index/34cae06d030c.js @@ -0,0 +1 @@ +rn_("AQQA9GEQAAABAAEAAQABAAA6MAAAAQAAAAAACgAQAAAAow+kD4cQiBAQEZwRnRGeEp8SFhQXFPMBAXnyhBMAAAEAAQADoKAAABPJoCAAABMPsDARkgABZWl08oUSAAABAAEDADswAAABAAAGAAEAEREGAAEIAPR0EwAAAQB+AAUAEwEDsJAT0wABsFASxgFLZ2xwOzAAAAEAABIACQAjEAIAlREBALUSAQDWEgEABhMBAGUTAQCZEwEAqBMBAOUTAQABgAKwMBGOAAGwMBGQAAFydgECAbAAEksAAWQ7MAAAAQAABQACAIoRAwB1EgEAI4ECoJAAABPIaW8BBwD05BMAAAsAHgAPAHcWAAGJsFAThAABsFASTQD6sGASxAABtQkDOjAAAAEAAAAAAA0AEAAAAOoM6wzsDH0Ofg6AEIEQuhDdEDwRPRE+ET8RvBJDgAOgIAAADOCgEAAADOltbm8BAwD0IBAAAAEAAQANAQGAAqCwAAATm7AwEvYAAW50QwADoCAAABDJsFARmgABZWhpOzAAAAEAAAYAAwAwDAAAqREDAEoTAQDzgAJkcocLAAAKgAHOABABgAHRABABoFAAABE5oFAAABPOoDAAABBNoDAAABBYMDFjZWlvc3R1d/UDCAAAAQCDB64CQQE=") \ No newline at end of file diff --git a/pr/395/docs/search.index/352927a38cef.js b/pr/395/docs/search.index/352927a38cef.js new file mode 100644 index 000000000..7b2488fd8 --- /dev/null +++ b/pr/395/docs/search.index/352927a38cef.js @@ -0,0 +1 @@ +rn_("AQYA9J4SAAABAHcBAQATAgKwYBKgAAFmbfLtDAAAAQAjggKwQBLIAAFlbAEFAPR9EwAAAQBGAAEA8wMBY/KVEgAAAQABgQKgAAAACYegMAAAD5RzdBOAAqAQAAAHuG5404CEsHAUJAABkEAB") \ No newline at end of file diff --git a/pr/395/docs/search.index/36d2aad2bbc2.js b/pr/395/docs/search.index/36d2aad2bbc2.js new file mode 100644 index 000000000..ff27b1bdf --- /dev/null +++ b/pr/395/docs/search.index/36d2aad2bbc2.js @@ -0,0 +1 @@ +rn_("AQQAOjAAAAEAAAAAAAcAEAAAAJkPmg9aEFsQqhKrEjETMhMBAAD0WgkAAAEA+gncABcAAACHoEAAABDDsGAT2gABoBAAAAyYsFATewABsDARJAABsSAT+QABOMECOjAAAAEAAAAAAA4AEAAAAG8JcAmECYUJKAwpDCoMMww0DOgMNhIIEwkTihOLEzOAA6CAAAATgmVveQ==") \ No newline at end of file diff --git a/pr/395/docs/search.index/37448ddeb53e.js b/pr/395/docs/search.index/37448ddeb53e.js new file mode 100644 index 000000000..037f8fef0 --- /dev/null +++ b/pr/395/docs/search.index/37448ddeb53e.js @@ -0,0 +1 @@ +rn_("AQUA9F0SAAABAJoAAQABCAD0CBMAAAEAgQABADMAA7BAEt4AAWFiZTowAAABAAAAAAAJABAAAACaB5sH/wwADYgP9A8+ED8QOBL7Eg==") \ No newline at end of file diff --git a/pr/395/docs/search.index/37e6d63af90e.js b/pr/395/docs/search.index/37e6d63af90e.js new file mode 100644 index 000000000..5da17e7d4 --- /dev/null +++ b/pr/395/docs/search.index/37e6d63af90e.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABOuZfLYEwAAAQABBAGwABKcAAFk9EYSAAABAAEAAQCnAAAChbCgE8YAAbBAEpoAAbBwE3cAARWIAAGcEwAAAQABsIATsAABcPKsEwAAAQBDAAOgIAAAEQ2gQAAAEkpkZXPztQ8AABgDAQDzgAJlaQ==") \ No newline at end of file diff --git a/pr/395/docs/search.index/37f48bae88e4.js b/pr/395/docs/search.index/37f48bae88e4.js new file mode 100644 index 000000000..e2d2a92dd --- /dev/null +++ b/pr/395/docs/search.index/37f48bae88e4.js @@ -0,0 +1 @@ +rn_("AQIA9VoRAAABAAEAAQABAAGAArBgEqIAAaAgAAAQrm95AQABoMAAABOvafTzEgAABwAWABkAAQYA9BIUAAABAAEAAQABAgD0rBAAAAEAmgABAAECAaBAAAASy3I7MAAAAQAABwACAKoQAQBBEQUA84ACYW9nAwAAhrCAEsoAUrCgEzcAAQwwBjswAAABAAAIAAMAOQwAAG0RAwBBEgMA84ICZWkjAAKggAAAFCBmaPJVCQAAwAkBAAKwEAwiAAGwMBPGAAFlcPShDwAAAQDiAAEAAQADsEATlAABoUAAABP4oFAAABGUYmNmAXUHAADzgAJtbgEBA6CwAAATUrBAEGoAAaBgAAARQGNldPRcBwAArghDAvoAAQUAOzAAAAEAAAUAAQBsEgUAAQcA9eoSAAABAAEAAQABACOAAqBQAAAREGFv8wCFIEkCOzAAAAEAAAgABAC3BwAA6QcFAA8NAABJEQAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/380e3640e532.js b/pr/395/docs/search.index/380e3640e532.js new file mode 100644 index 000000000..940421621 --- /dev/null +++ b/pr/395/docs/search.index/380e3640e532.js @@ -0,0 +1 @@ +rn_("AQQBoEAAABEwZQH2DwAAAQgA9G0RAAABAAEAAQABAwGgQAAAES9lAYcPAAABAgDzSgwAAAEAAQAjAAKwIAw6AAFrcvZdBwAAAQBTCekAAQAAAvOAAnJ4AQMBsIATzwABdjswAAABAAALAAMAxBABAHgRBgA9EwIAI4ACsGAQPQC6YXIBAwGgAAAAD/Vz8oUPAAABACOAAqCwAAATYmVoAQMBoEAAABDDZfJvCQAAAQDXJQAAjLAgB9AJIrBwETcAAbAgB7IAAbCwEr4AAaCAAAARcaAAAAASy78MTDowAAABAAAAAAAJABAAAADiAS8EMAQxBCYMJwyFDIYMhwzMEg==") \ No newline at end of file diff --git a/pr/395/docs/search.index/3848f16ce3c2.js b/pr/395/docs/search.index/3848f16ce3c2.js new file mode 100644 index 000000000..1f381431b --- /dev/null +++ b/pr/395/docs/search.index/3848f16ce3c2.js @@ -0,0 +1 @@ +rn_("AQkA8+0TAAABADUAAQwBoKAAABQxd/IhFAAAAQABAQDztg8AAAEAAQDzgAJ0dQGBArCAE/sAAaBgAAAUH2RzNwIAg4agQAAAE9GwYBOlAEuwMBOSAAECkA4BEgD0CRQAAAEAAQABAAEAxqBQAAAScqAAAAAQTbAgEPsAAaBQAAARnqEAAAAUBKBAAAATrwADAgs7MAAAAQAAFQAHACcGAABNDAAA8wwAAMUPDQAdEQAAhxIBAKoTAQABAQGwsBP2AAFsOzAAAAEAAAwAAwCOBwgAEQ0BAAMQAQABAQGgMAAAENZo81wJAAAuAP4HAQIA9H8RAAABAAEAAQATgQKwIBGDAAFhc/OCAm5v9wEAgIegIAAABiagQAAADDYVAQc=") \ No newline at end of file diff --git a/pr/395/docs/search.index/386ab9ff3804.js b/pr/395/docs/search.index/386ab9ff3804.js new file mode 100644 index 000000000..13663b800 --- /dev/null +++ b/pr/395/docs/search.index/386ab9ff3804.js @@ -0,0 +1 @@ +rn_("AQIBsEAT6wABdDswAAABAAANAAYAjBIDAO8SAQBTEwEAahMBAOETAQAFFAEAE4ACsDASuQExaXABBQDzKxMAAAEAAQATgAKgMAAAE4FleVMBA7BwE48AAWJlcwH7DwAAE4ADsDAT+QABsCAHaAABZWxz") \ No newline at end of file diff --git a/pr/395/docs/search.index/3882b0299479.js b/pr/395/docs/search.index/3882b0299479.js new file mode 100644 index 000000000..fb9489ac4 --- /dev/null +++ b/pr/395/docs/search.index/3882b0299479.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgBlEQMAUxICAAEKAbBAE9YAAWX1nhMAAAEAAQABAAEA8wACaXP0QQwAAAEASwABACOBAqDgAAATvWV5") \ No newline at end of file diff --git a/pr/395/docs/search.index/38f2efb5b65e.js b/pr/395/docs/search.index/38f2efb5b65e.js new file mode 100644 index 000000000..581e85e55 --- /dev/null +++ b/pr/395/docs/search.index/38f2efb5b65e.js @@ -0,0 +1 @@ +rn_("AQcA9BETAAABAAEAAQDzBQF0OzAAAAEAAAgAAgAlDAAAUgwHAAEFAPMrEwAAAQABADcAAICFsDAQCAABoKAAABNGoDAAABOBEEFI") \ No newline at end of file diff --git a/pr/395/docs/search.index/39ac769ad27b.js b/pr/395/docs/search.index/39ac769ad27b.js new file mode 100644 index 000000000..115c653be --- /dev/null +++ b/pr/395/docs/search.index/39ac769ad27b.js @@ -0,0 +1 @@ +rn_("AQEBoGAAABQEc/KDEQAAAQABCAD1thMAAAEAAQABAAEAowCEsCAJxQABoIAAABKAMEAIOzAAAAEAAAUAAgDLAQAAThAEAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/3aa6d9572c12.js b/pr/395/docs/search.index/3aa6d9572c12.js new file mode 100644 index 000000000..4bbdbdda4 --- /dev/null +++ b/pr/395/docs/search.index/3aa6d9572c12.js @@ -0,0 +1 @@ +rn_("AQQA9C0SAAABAFEBAQDzAAFsOzAAAAEAAAgAAwC/CQEAkRABAO0QBAABAAGwYBOGAAFl9GoMAAABAAEATwcBBQD0vhIAAAEANwEBAAEHAPQREwAAAQABAAEA8wIBdDswAAABAAAIAAIAJQwAAFIMBwDzgAJhZQGAArEQFAcAAbAQDG0AAWVvAQUAOzAAAAEAAA0AAQBKEQ0AAQIA88IPAAABAAEAAQIA9L4PAAABAAEAAQABAwA7MAAAAQAACAABAGwQCACHBwAAh7CgE0AAAbDQE8wAAaBQAAATm4wUEfXABwAA1wgBAAEAAQDzgAJsdBOAA6AgAAAM3bCgE7QAAWFkbgGAArCAEpcAAbCgEwMAAWl0AYACsOAT8QABsEAQDQABbHIBBgD0lxAAAAEAAQABAEcFAACHoNAAABPOsFAQCwABoAAAABMFsPATrAABBZED8rEPAACnAAECAPTYEgAAAQD6AAEAI4ACoCAAAAwxaXIBCAD0QRIAAAEAAQABAAEEAbAAEJsAAXMBDxAAAAECAaBQAAASmWMB3gwAALcCAACHsEANGAABsBAS9gABoAAAABLSlRADOjAAAAEAAAAAAA0AEAAAAFIFUwUSBhMGFAbhDOIMgA+BD4IPjw9AE0ETQxMBgAKwIAmEAAGg4AAAE3ZibPcWAACJsBAGDAABsCAH4QABkQlHOzAAAAEAAIoACgBPBAAAfgkCAKUQAAD0EAAAMREAAK4RewArEgMAOxIAAPwSAAB/EwEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/3be462b0d192.js b/pr/395/docs/search.index/3be462b0d192.js new file mode 100644 index 000000000..b1479042e --- /dev/null +++ b/pr/395/docs/search.index/3be462b0d192.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAUAAQDkEgUAg4YEsFASrgABsHATDQABoNAAABPeYm5vdhOAArAQEocAAXByAQABoDAAABGXdAEqEwAA8wIBc/JcEAAA0gITgAKgYAAAEE1ldQECAPYVDQAAAQDvAgEAIAEBAAGAArAAEnMAAYAOfDFgAWVzAQEA88EHAABCCwEAY4ADsIATewABYm5084ADYW9yARQA9AkUAAABAAEAAQABAAA7MAAAAQAABgACAAsTAABuEwUAI4gCsEAT3AABZXMBBAGwMBLiAAFj8zMRAAABAAEAAQUAOzAAAAEAAAgAAwCiEQIAkBIEAJ0TAADzAgNjZXDz2gwAAAEAAQBjgAOgYAAAD7Blb3UBAAGwYBOGAAFlAbsTAADzAgFz9n4JAAABAAEAdAc9AAoBAQgA81MSAAABAAEAQ4ADoBAAABMPoOAAABORZG9zAQIBoFAAABEycgHUEAAAAYACoEAAABBZoHAAABPJYWUBAAA7MAAAAQAACwACAGEHBQBsEgUA84ACdHYBAQD0WAgAAAEAAQABAPMAAm5y9nQJAABQAHUGAQCvAAEA8wCHMJEM9YUSAAABAHkAAQDpAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/3c5511ca849c.js b/pr/395/docs/search.index/3c5511ca849c.js new file mode 100644 index 000000000..215510a8b --- /dev/null +++ b/pr/395/docs/search.index/3c5511ca849c.js @@ -0,0 +1 @@ +rn_("AQABoLAAABNGYvPdEgAAFAEBAAGAAqAgAAAPkKAwAAATUmFy8wABdAHoBwAAAQQBoEAAABPVZfL0EwAAAQABBQA7MAAAAQAACwAFABMQBQBiEgEAeRMBAMgTAADnEwAA4wGEoDAAAA+OhQACOzAAAAEAAAsABQAeBgIAKQcEAHAMAQA5EQAAchIAAGOAA7AAD7ADLGRucg==") \ No newline at end of file diff --git a/pr/395/docs/search.index/3e13ecd45e17.js b/pr/395/docs/search.index/3e13ecd45e17.js new file mode 100644 index 000000000..5ae007067 --- /dev/null +++ b/pr/395/docs/search.index/3e13ecd45e17.js @@ -0,0 +1 @@ +rn_("AQkA8+0TAAABADUAAQwBoKAAABQxd/IhFAAAAQABAQDztg8AAAEAAQDzgAJ0dQGBArCAE/sAAaBgAAAUH2RzNwIAgIagQAAAE9GwYBOlAEuwMBOSAAECkA4BAAGwUBC7AAFj9McHAAABAAEA8wHDgISgEAAADIygUAAAEjcMQAQ=") \ No newline at end of file diff --git a/pr/395/docs/search.index/3e1b601c727f.js b/pr/395/docs/search.index/3e1b601c727f.js new file mode 100644 index 000000000..b1f37ecdd --- /dev/null +++ b/pr/395/docs/search.index/3e1b601c727f.js @@ -0,0 +1 @@ +rn_("AQACoCAAABMPsDARkgABaXTyhRIAAAEAAQMAOzAAAAEAAAYAAQAREQYAAQgA9HQTAAABAH4ABQATAQOwkBPTAAGwUBLGAUtnbHA7MAAAAQAAEgAJACMQAgCVEQEAtRIBANYSAQAGEwEAZRMBAJkTAQCoEwEA5RMBAAGAArAwEY4AAbAwEZAAAXJ2AQQBsAASSwABZDswAAABAAAFAAIAihEDAHUSAQC3BQABh7BQEk0A+rBgEsQAAZQJAzswAAABAAAKAAUA6gwCAH0OAQC6EAAAPBEDALwSAAAjgAKgEAAADOlubwEDAPMgEAAAAQABACOAAqAAAAAMMGRyYwGEoFAAABE5oDAAABBYFAEQ8gMIAAABABOAArBgEmoAAWNnE4ADoLAAABORoEAAAA58ZW9z") \ No newline at end of file diff --git a/pr/395/docs/search.index/3ea2a9f02ef8.js b/pr/395/docs/search.index/3ea2a9f02ef8.js new file mode 100644 index 000000000..d5e166bbd --- /dev/null +++ b/pr/395/docs/search.index/3ea2a9f02ef8.js @@ -0,0 +1 @@ +rn_("AYACoAAAABBJoAAAABBKNDYBgAOwABNeAAGwABNgAAGwcBPPAAE0NmEBBAD0HRQAAAEACgABACOBAqBAAAARC21wAQEAOzAAAAEAAAgAAwBoEgAA0RIAACoUBgCHAwACxqBAAAAQEqBgAAATg6CAAAASRUAHgAg7MAAAAQAAFgAIALIPAQDEEAEABxEDAHgRBgA9EwIAiRMAAN8TAQAOFAEA8wACZXY6MAAAAQAAAAAAEQAQAAAA7AHtASkGDQ1zD3QPjg+nDzcQOBDeEN8QWRJaElsSXBKmE6cTAYECsHARBwABsHARCQABNDYjgAKwEAfLAAFucAEDArBAEqgAAaAwAAAS/GV39D0QAAAFALUAAQATgQKgcAAAEy5nbyOAArBQD7IAAWR0VwEAAIWgEAAAEBKxIBPfAAERIQH0fxEAAAEAAQABAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/3ef16a8006fc.js b/pr/395/docs/search.index/3ef16a8006fc.js new file mode 100644 index 000000000..5df36f2ee --- /dev/null +++ b/pr/395/docs/search.index/3ef16a8006fc.js @@ -0,0 +1 @@ +rn_("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") \ No newline at end of file diff --git a/pr/395/docs/search.index/3f496fd7d7dd.js b/pr/395/docs/search.index/3f496fd7d7dd.js new file mode 100644 index 000000000..6cebf01ef --- /dev/null +++ b/pr/395/docs/search.index/3f496fd7d7dd.js @@ -0,0 +1 @@ +rn_("AYkCsCAT4QABoDAAABPqaXATAAOgIAAAErSggAAAEsdlaXX1MgwAADIAAQABAAEAAYYCsJAUBQABoAAAABJOYmQBgAKwMBKoAAGgMAAAEKZydtcAAAKFoDAAABAmoDAAABL8EAETOjAAAAEAAAAAAAkAEAAAAFYJVwlYCYgJiQk9EEIQ9xD4EIsSAQMBsDASwgABc/N7EAAAAQABAAEAADswAAABAAAKAAMAHxECADkTAwASFAMA0wCEsCAIawABEBgB9Y8MAAABAAEAAQABAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/3f4f416f647f.js b/pr/395/docs/search.index/3f4f416f647f.js new file mode 100644 index 000000000..e3fbb0284 --- /dev/null +++ b/pr/395/docs/search.index/3f4f416f647f.js @@ -0,0 +1 @@ +rn_("AQABsIAS0wABdvTBCQAAAQC1CQEAE4ACsDAQuwABbG4BgAKgAAAAEEmgAAAAEEo0NvMDAXYBDQ0AABMAAqBAAAARNmFz908MAABlA1gBMgEBAAEAjgEBAgD0rBIAAAEAhgABAAEDAPTgEgAAAQCLAAEA8wACYnU6MAAAAQAAAAAAEwAQAAAAtAe1B7YHmQ+aDzsQPBBaEFsQ6xDsEJgRmRGqEqsStxK4EscSMRMyE/OAAmR0E4ACoCAAAA0XZXIBBwGgQAAAE65l8tgTAAABAAEHAPSyEAAAAQABAAEAAQAA9bQJAAABAAEAAQABACOAAqCgAAAUA2ZwAQMBoJAAABODZfJzDwAAAQDXAAAAhaBQAAAQMbFAFCoAAYEpAPLxBQAAAQDzgAJsdJcBAICFsTATsAABsRAT2gABEUUA") \ No newline at end of file diff --git a/pr/395/docs/search.index/3fa2f95c1020.js b/pr/395/docs/search.index/3fa2f95c1020.js new file mode 100644 index 000000000..46bcc2125 --- /dev/null +++ b/pr/395/docs/search.index/3fa2f95c1020.js @@ -0,0 +1 @@ +rn_("AQAA9JQJAAABAGAHAQABCQGggAAAFCBmARUTAAABAgD1ixAAAAEAAQA1AgEA8wACaXU6MAAAAQAAAAAABwAQAAAAaRCAElYTVxOGE4cToxOkEwEIAPW2EwAAAQABAAEAAQDzAgFm9U4QAAABAAEAAQABABOAArCgE/kAAXJ08wACYXPzexAAAAEAAQDzgAJ0dg==") \ No newline at end of file diff --git a/pr/395/docs/search.index/410cbfd39641.js b/pr/395/docs/search.index/410cbfd39641.js new file mode 100644 index 000000000..33e1c3ed7 --- /dev/null +++ b/pr/395/docs/search.index/410cbfd39641.js @@ -0,0 +1 @@ +rn_("AQQA9u0TAAABAA0AAQAjAAQAAQMA81MSAAABAAEAI4ECsSAUJAABY2sTgAKgMAAAE65uclMAA6BAAAAMPGVvcvTWEAAAZwGbAQEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/4248fe585b27.js b/pr/395/docs/search.index/4248fe585b27.js new file mode 100644 index 000000000..9ba95dbf4 --- /dev/null +++ b/pr/395/docs/search.index/4248fe585b27.js @@ -0,0 +1 @@ +rn_("AQUA9H0TAAABAEYAAQDzBAFj8pUSAAABACOCAqBQAAASaWZ0AQABsAATzwABZPQeDAAAAQABAAEA84ACZWkTgAKw4BPaAAF0dg==") \ No newline at end of file diff --git a/pr/395/docs/search.index/4375a073b2db.js b/pr/395/docs/search.index/4375a073b2db.js new file mode 100644 index 000000000..18e00f8db --- /dev/null +++ b/pr/395/docs/search.index/4375a073b2db.js @@ -0,0 +1 @@ +rn_("AQIA9VoRAAABAAEAAQABAAGAArBgEqIAAaAgAAAQrm95AQABoMAAABOvafTzEgAABwAWABkAAQMA9BETAAABAAEAAQABBgD0EhQAAAEAAQABAAECAPSsEAAAAQCaAAEAAQIBoEAAABLLcjswAAABAAAHAAIAqhABAEERBQDzgAJhb2cHAACHsIASygBSsKATNwABDDAHOzAAAAEAAA4ABABIBAUAOQwAAG0RAwBBEgMAo4CEoPAAABNHoFAAABAKlAEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/45f6ee715dad.js b/pr/395/docs/search.index/45f6ee715dad.js new file mode 100644 index 000000000..85385d7ac --- /dev/null +++ b/pr/395/docs/search.index/45f6ee715dad.js @@ -0,0 +1 @@ +rn_("AQkA8+0TAAABADUAAQ0BoKAAABQxd/IhFAAAAQABgQKwgBP7AAGgYAAAFB9kczcBAIWFoEAAABPRsGATpQBLApAK") \ No newline at end of file diff --git a/pr/395/docs/search.index/474d1a590443.js b/pr/395/docs/search.index/474d1a590443.js new file mode 100644 index 000000000..d40f37461 --- /dev/null +++ b/pr/395/docs/search.index/474d1a590443.js @@ -0,0 +1 @@ +rn_("AQEA9e0QAAABAAEAAQABACOBArCQE38AAWF0Q4ADsRAT9AABoIAAABNCY290EwACsAATzAABbnL25AEAAAEAVwIBAGEDcQs=") \ No newline at end of file diff --git a/pr/395/docs/search.index/483e10d0fb1a.js b/pr/395/docs/search.index/483e10d0fb1a.js new file mode 100644 index 000000000..c9de7961b --- /dev/null +++ b/pr/395/docs/search.index/483e10d0fb1a.js @@ -0,0 +1 @@ +rn_("AQYA9DwRAAABAAEAAQABAgA7MAAAAQAADAAFACsMAQBDEAUAZBIBALoSAQDJEwAA84ACZm4BAQA7MAAAAQAABwACAGYPBQDiEgEA84ACZG4BAQDzlwcAAAEAAQABhgKgAAAAEtygAAAAEt1kcvMBAXYBAhMAAEMAA7CAEncAAbCwE/YAAWtscjswAAABAAAMAAMAjgcIABENAQADEAEAAQIA9WAIAAABAAEAAQABAPMAhBRBAAFvAAAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/485aace276e9.js b/pr/395/docs/search.index/485aace276e9.js new file mode 100644 index 000000000..d5974f3f2 --- /dev/null +++ b/pr/395/docs/search.index/485aace276e9.js @@ -0,0 +1 @@ +rn_("AQYA9MQQAAABALgAAQATgQOgAAAAEQuxIBQOAAFhZG4BFAD1LBQAAAEAAQABAAEAAQYBsIATzwABdjswAAABAAAHAAIAeBEEAD0TAgBXAAABhqDAAAATCqCgAAATKqCQAAAS+7BQED4AAQXgAvR/BwAAAQABAIIMAQYA82YSAAABAJoAAQABoGAAAA+pcgFDBAAA1wEAgMWgMAAADC8AAIoK") \ No newline at end of file diff --git a/pr/395/docs/search.index/49793047756c.js b/pr/395/docs/search.index/49793047756c.js new file mode 100644 index 000000000..f0ebe2ebe --- /dev/null +++ b/pr/395/docs/search.index/49793047756c.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAABEABQAnBgAATQwAAPMMAADFDw0AHREAAAGEArAAESsAAbAAES0AATQ2FwgAAIigIAAAEYWgMAAAEPOg4AAAEymgMAAAEj2wcA+fAAGgIAAABjDLwAg7MAAAAQAAAQEVAJIBAAAcBgAAMAYAAFMJAABZCQAAdwkBAC0MAABoDAEAigwBAPYMAAAdDd0Aiw8AAJwPAQCGEAAAQBEAAIESAQBMEwUAaRMAAOgTAAD9EwUAGBQBAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/4ae488f26dde.js b/pr/395/docs/search.index/4ae488f26dde.js new file mode 100644 index 000000000..54a2d648d --- /dev/null +++ b/pr/395/docs/search.index/4ae488f26dde.js @@ -0,0 +1 @@ +rn_("AQAA9PMPAAC9AgEArADzAAFzOzAAAAEAABkACABABQIARQwCAKwPAQBqEAEAphABAJARAQAhEwcACRQDACOAArEQFA4AAWF0") \ No newline at end of file diff --git a/pr/395/docs/search.index/4b97e0c80ad3.js b/pr/395/docs/search.index/4b97e0c80ad3.js new file mode 100644 index 000000000..83d43da0f --- /dev/null +++ b/pr/395/docs/search.index/4b97e0c80ad3.js @@ -0,0 +1 @@ +rn_("AQAAOjAAAAEAAAAAAAoAEAAAAKMPpA+HEIgQEBGcEZ0RnhKfEhYUFxQBBADzng8AANcAAQDzAAFx9xUNAAABAO8CAQDAAGAAAQDzAAJleTowAAABAAAAAAAMABAAAABtDG4MQBBBENAQ0RDSEHQTdROEE4UT8xP4Ew==") \ No newline at end of file diff --git a/pr/395/docs/search.index/4c180bbff966.js b/pr/395/docs/search.index/4c180bbff966.js new file mode 100644 index 000000000..9c57067aa --- /dev/null +++ b/pr/395/docs/search.index/4c180bbff966.js @@ -0,0 +1 @@ +rn_("AQoBoIAAABQgZgEVEwAAAQgA9bYTAAABAAEAAQABAPMDAWb1ThAAAAEAAQABAAEA84MCYXMBAgKgIAAAEYWgMAAAEj1iaAGLDwAAI4ACoIAAABLRYXDzAAJobQEyBQAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/4cafff0b91f7.js b/pr/395/docs/search.index/4cafff0b91f7.js new file mode 100644 index 000000000..4ae312594 --- /dev/null +++ b/pr/395/docs/search.index/4cafff0b91f7.js @@ -0,0 +1 @@ +rn_("AYMCsCARMQEKoJAAABOdbXABggKwoBOSAAGwoBOUAAFjcgECAaBAAAARMGUB9g8AADOAhLAgEK8AAbAgD3gAAVAUAPOAAm9y") \ No newline at end of file diff --git a/pr/395/docs/search.index/4d6048f07ed5.js b/pr/395/docs/search.index/4d6048f07ed5.js new file mode 100644 index 000000000..806bff1be --- /dev/null +++ b/pr/395/docs/search.index/4d6048f07ed5.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAQD9EwUA8w0BbTswAAABAAAFAAEAvhMFACMDhLBAElYAAaEAAAAUA6AAAAATiDAIAjswAAABAAAUAAUA4RAHAHcSAQDqEgQAthMEACAUAAA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/4d8a1f849ace.js b/pr/395/docs/search.index/4d8a1f849ace.js new file mode 100644 index 000000000..104ef11f0 --- /dev/null +++ b/pr/395/docs/search.index/4d8a1f849ace.js @@ -0,0 +1 @@ +rn_("AYACsHASagABoLAAABORZW8jAQOwcBGmAAGAEv8AEOltbnM7MAAAAQAAGgAGANQHAQBMCQUAEw0BAP0QCQA6EQAA5BIFAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/4da0028e700f.js b/pr/395/docs/search.index/4da0028e700f.js new file mode 100644 index 000000000..e1bee9928 --- /dev/null +++ b/pr/395/docs/search.index/4da0028e700f.js @@ -0,0 +1 @@ +rn_("AQEA99AQAAABAAEAogIBAH4ABQABAgD0ZRAAAAEAAQABABOAAqDQAAAT42NzU4ADoHAAABPeZWlvQ4ADoCAAAA+nsAAHvAABZGx0") \ No newline at end of file diff --git a/pr/395/docs/search.index/4f541acf0097.js b/pr/395/docs/search.index/4f541acf0097.js new file mode 100644 index 000000000..8474898e4 --- /dev/null +++ b/pr/395/docs/search.index/4f541acf0097.js @@ -0,0 +1 @@ +rn_("AQkBoEAAABPVZfL0EwAAAQABgAKwAAz0AAGggAAAE0NkcAEDAaAAAAAP9XPyhQ8AAAEAAQIDsGASzQABsNAT2AABoIAAABNiaXJzAeMTAAD3AACAhaEwAAAT85BJAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/4fa3f1ec9b83.js b/pr/395/docs/search.index/4fa3f1ec9b83.js new file mode 100644 index 000000000..a205c32d7 --- /dev/null +++ b/pr/395/docs/search.index/4fa3f1ec9b83.js @@ -0,0 +1 @@ +rn_("AQgA9HQTAAABAH4ABQATAAOwkBPTAAGwUBLGAUtnbHA7MAAAAQAAEgAJACMQAgCVEQEAtRIBANYSAQAGEwEAZRMBAJkTAQCoEwEA5RMBAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/4fa8c75c3f45.js b/pr/395/docs/search.index/4fa8c75c3f45.js new file mode 100644 index 000000000..6a24afb7e --- /dev/null +++ b/pr/395/docs/search.index/4fa8c75c3f45.js @@ -0,0 +1 @@ +rn_("AQABoDAAAAzdaQH8EgAAAQABsDAQ+QABbvLCBwAAAQABAQDzPQgAADQHAQATAAKgUAAAEFxlc/UPBAAAAQABAAEAAQABhAKwoBOSAAGwoBOUAAFjcvMBAXTylBEAANUAAQMBsGAT2gABZjswAAABAAAGAAMAKAwCAAgTAQCKEwEA84ACYWXzgIQAKAU=") \ No newline at end of file diff --git a/pr/395/docs/search.index/503d89ddb639.js b/pr/395/docs/search.index/503d89ddb639.js new file mode 100644 index 000000000..fca12810f --- /dev/null +++ b/pr/395/docs/search.index/503d89ddb639.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwByEQIA/RIBAIwTAgABBADzdREAAAEAAQAjAAKgYAAAEothcvOdEAAAAQCwAQGAAqEQAAAUDbCAFAUAAWl1NwQAgYexUBQoAAGACXEAFw2wcBNWAAGhMAAAFBwKQBsBAAA7MAAAAQAAewABAPsNewABAgD2HRMAAAEAnwBTABYAAQAjgQKgIAAAEL1ydvOAAmVvI4ACoCAAABIvZHQBAAA7MAAAAQAA4AAEAEEGAABtCN0AmAwAAB4RAADzgANkbnY=") \ No newline at end of file diff --git a/pr/395/docs/search.index/50a025b610c7.js b/pr/395/docs/search.index/50a025b610c7.js new file mode 100644 index 000000000..d772eb91e --- /dev/null +++ b/pr/395/docs/search.index/50a025b610c7.js @@ -0,0 +1 @@ +rn_("AQABoJAAABL6aPLEEgAAAQDzAgFk8n0HAAABAAECADswAAABAAAFAAEA/RMFAPMLAW07MAAAAQAABQABAL4TBQAjgAKwIAz3AAFhbwECAPNZCQAADwMBAAGKArAAE14AAbAAE2AAATQ2I4ECoDAAAAyMZXIBgAKwQBGcAAEXW04Pg1xjdbcGAACHsBAQDQABsFAT2gABEVEJ9SkFAAAKALQAAQAvBw==") \ No newline at end of file diff --git a/pr/395/docs/search.index/510029c366bd.js b/pr/395/docs/search.index/510029c366bd.js new file mode 100644 index 000000000..c1f2270c9 --- /dev/null +++ b/pr/395/docs/search.index/510029c366bd.js @@ -0,0 +1 @@ +rn_("AQ8A9JkTAAABAEsAAQABBAD0ORAAAAEArwABAAECADowAAABAAAAAAAJABAAAACGEYcRiRKKEmMTZBO0E7UTJBQlFPMAAW/yxwkAAAEAU4ADsQAT9gABaG9w84ACc3Q=") \ No newline at end of file diff --git a/pr/395/docs/search.index/511f426cf452.js b/pr/395/docs/search.index/511f426cf452.js new file mode 100644 index 000000000..fa9e15677 --- /dev/null +++ b/pr/395/docs/search.index/511f426cf452.js @@ -0,0 +1 @@ +rn_("AQ0A9VgTAAABAAEAcAABAAGAAqEQAAATkbBwE6YAAW5yAQcA9GURAAABAAEAAQAzAASgIAAACYOw4BOGAAExZWly9BQEAACkA6wBIAMBAgD0uBAAAAEAeQEBAAEDAPQiEQAAAQBgAQEA8wACYnUB1Q8AAAEDAPS2EAAAAQB5AQEA84QCZHMBAgDzZgkAAAEAVADzgAJpdAEYAPMuFAAAAQABACOBArDAE1YAAWNtAQ0BsEAT1gABZfWeEwAAAQABAAEAAQATggKwABDOAAFrcwEEAPRhEAAAAQABAAEAAYECoCAAABDJsEAS9gABZWnzggJldAEEAaBgAAASWHL0PgwAAAEAlAMBAPOAA2FlbwEJAPaBEgAAAQDnAH8AMAABABOAArBgEHkAAWRuAQYA9aEQAAABAAEAAQBmAgEDAaBwAAAStGf0xQcAAAEA8wEBAAEJAPPtEwAAAQA1AAGBArCAE/sAAaBgAAAUH2RzFwEAiIWw0BQhAAGgQAAAE9GwYBOlAEsCkAoBBAD0RBMAAAEASgABAFOAA7CQFBoAAWVobwEAAaBAAAARL2UBhw8AACOBArBQENsAAWt0d/wBAI2hkAAAFBGwsBLCAAGgoAAAEoD9gQ87MAAAAQAAHQAIAHMAAAB5AAMA7QUBAJ8HBwCOCQMAXAwHAA0QAQAvEgAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/51be0345651c.js b/pr/395/docs/search.index/51be0345651c.js new file mode 100644 index 000000000..3b5d3328f --- /dev/null +++ b/pr/395/docs/search.index/51be0345651c.js @@ -0,0 +1 @@ +rn_("AYACsHASLQABsCAUGgABaW8BAAGw8BQaAAFr9ZETAAAFAAEAAQAkAPMFAWTyahIAAAEAAQIA84sQAAABAAEA8wABaTowAAABAAAAAAAHABAAAABpEIASVhNXE4YThxOjE6QTI4MCsLAT+QABYXMBgQKgoAAAEsygQAAAEC5vdvOAhAFQAQ==") \ No newline at end of file diff --git a/pr/395/docs/search.index/51fb0e67a5a5.js b/pr/395/docs/search.index/51fb0e67a5a5.js new file mode 100644 index 000000000..683188e64 --- /dev/null +++ b/pr/395/docs/search.index/51fb0e67a5a5.js @@ -0,0 +1 @@ +rn_("AQUA9MASAAABAIcAAQDzAAFj9RAQAAABALoAAQABAAEJAPPtEwAAAQA1AAENAaCgAAAUMXfyIRQAAAEAAYECsIAT+wABoGAAABQfZHM3AQCEhaBAAAAT0bBgE6UASwKQCiOAAqBwAAARDGRyAQsBoEAAABPVZfL0EwAAAQATgAKwMBAsAAFkcvMCAWX0RggAAAEAAQABACOAArAgDCYAAWV5AQoA81MSAAABAAEAAQsAOzAAAAEAAAcAAQAhEwcAAQIA9H0TAAABAEYAAQC3AACAhbBwEoUAAbDQFBYAAWAgBgEGAPNfEgAAAQABAAEDAbBAE+sAAXTyUxMAAAEAI4UCoEAAABOBYnMTAgKgUAAAER1iZvdiCQAAAQBDANMGAQBfAAEA84ACaXABBADzKRAAAAEAAQDzAAFwOzAAAAEAAAwAAQBMBwwAI4ADoDAAAAw0sHATNQABYWVp84ADZG1uAQAA9mwJAAASA80FAQBQAAEA8wIBZDswAAABAAAJAAMAihEDAEYSAwB1EgEAI4ACoJAAABPIaW8BgAOwMBLxAAGAEysAEAGwUBDOAAFjc3jzAAFlAVUFAADnFQAAiaAAAAAMjKBAAAAM4LBADOEAAZBIXQHmAQAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/534cb5ffe8b1.js b/pr/395/docs/search.index/534cb5ffe8b1.js new file mode 100644 index 000000000..63fc41fd7 --- /dev/null +++ b/pr/395/docs/search.index/534cb5ffe8b1.js @@ -0,0 +1 @@ +rn_("AYQCoCAAABDVoDAAABE7aXQBAQA7MAAAAQAAfAACANwHAADJCXsAYwADsAAB8QABZGVsOjAAAAEAAAAAAAcAEAAAAEMFAAYBBgIG4AfzEBYTFxM=") \ No newline at end of file diff --git a/pr/395/docs/search.index/5489a4fd5a52.js b/pr/395/docs/search.index/5489a4fd5a52.js new file mode 100644 index 000000000..7dce7a2f0 --- /dev/null +++ b/pr/395/docs/search.index/5489a4fd5a52.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwByEQIA/RIBAIwTAgABBwD2HRMAAAEAnwBTABYAAQABBADzdREAAAEAAQAjAAKgYAAAEothcvOdEAAAAQCwAQGAAqEQAAAUDbCAFAUAAWl1dwgAAIixUBQoAAGACXEAFw2wcBNWAAGhMAAAFBwKQRvysQkAAKgGAYECoFAAABDCoKAAABLVa3ABAwA7MAAAAQAA6AACAO8MAQB/DuYA84ACbHTzgAJhZQ==") \ No newline at end of file diff --git a/pr/395/docs/search.index/54a0f4bbac45.js b/pr/395/docs/search.index/54a0f4bbac45.js new file mode 100644 index 000000000..38a97950e --- /dev/null +++ b/pr/395/docs/search.index/54a0f4bbac45.js @@ -0,0 +1 @@ +rn_("AYECoBAAABLMoHAAABMMYWMBhwKwABNeAAGwABNgAAE0NlMAhKCQAAAS87CwE4QAAYEACfWNBwAANwCJCAsBGQA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/550f724dbd52.js b/pr/395/docs/search.index/550f724dbd52.js new file mode 100644 index 000000000..837386fb6 --- /dev/null +++ b/pr/395/docs/search.index/550f724dbd52.js @@ -0,0 +1 @@ +rn_("AYkCsCAT4QABoDAAABPqaXATAAOgIAAAErSggAAAEsdlaXX1MgwAADIAAQABAAEAAYYCsJAUBQABoAAAABJOYmQBAgD0NwwAAAEANAMBAAEAADswAAABAAAGAAIACxMAAG4TBQAjgQKwQBPcAAFlc1OAA7AACZAAAWV4eQGAArAgEqgAAbAACY4AAXJ4RwEAAMawwBM1AAGgEAAAER6wQAzxAAGgMAAAEKYARIAL9OsFAAABAAsHAQDXAAAAhaAwAAAQJqAwAAAS/BABEzowAAABAAAAAAAKABAAAABWCVcJWAmICYkJPAw9EEIQ9xD4EIsS") \ No newline at end of file diff --git a/pr/395/docs/search.index/551174c27cfd.js b/pr/395/docs/search.index/551174c27cfd.js new file mode 100644 index 000000000..6b51519c4 --- /dev/null +++ b/pr/395/docs/search.index/551174c27cfd.js @@ -0,0 +1 @@ +rn_("AQgBoIAAABQgZgEVEwAAE4ACoOAAABPpYnABAwGwABOlAEtz9aUPAAABAOMAAQDIAgGAArBAEk8AAaAQAAAQxnByAYEDoAAAABE2sAAPkAEYoBAAAA+IZXN184ADYW9yAQIA9YsQAAABAAEANQIBAAEEADswAAABAAAJAAEA/RAJAAGBAqCQAAATa7BQEjkAAWJ3o4CEsFAThgABsJASugABQCADVwMAAIagcAAAE2KgQAAAE84wQQw6MAAAAQAAAAAAOQAQAAAAUAVRBQMGBAYFBgYGBwYIBgkGCgYLBugHgQmCCYcJSAxJDGoMawxsDPEM8gx8DnoPew98D5APrg+vD/MPaRB3EHgQlRCWEKgQzhDPEDsRiRGSEZMRlxGAErASsRJWE1cTXROGE4cToxOkE7sTHRQeFCgUKRQ=") \ No newline at end of file diff --git a/pr/395/docs/search.index/55d74f9a517a.js b/pr/395/docs/search.index/55d74f9a517a.js new file mode 100644 index 000000000..54eac9b42 --- /dev/null +++ b/pr/395/docs/search.index/55d74f9a517a.js @@ -0,0 +1 @@ +rn_("AQIA9KwSAAABAIYAAQABAgD0rBAAAAEAmgABAAEDAPTgEgAAAQCLAAEAAQIBoEAAABLLcjswAAABAAAHAAIAqhABAEERBQDzAIQDQAg6MAAAAQAAAAAAFQAQAAAAtAe1B7YHmQ+aDzsQPBBaEFsQ6xDsEJgRmRGqEqsStxK4EscS8RLyEjETMhM=") \ No newline at end of file diff --git a/pr/395/docs/search.index/55dc7f8c9f93.js b/pr/395/docs/search.index/55dc7f8c9f93.js new file mode 100644 index 000000000..5a885466c --- /dev/null +++ b/pr/395/docs/search.index/55dc7f8c9f93.js @@ -0,0 +1 @@ +rn_("AQIA9KwSAAABAIYAAQABAwD04BIAAAEAiwABAPMFAmJ1OjAAAAEAAAAAAAcAEAAAADsQPBDrEOwQmBGZEbcSuBIBAgD0IhEAAAEAYAEBAAEFAPMoEQAAAQABAAEEAPRdEgAAAQCaAAEA84CEEQkA") \ No newline at end of file diff --git a/pr/395/docs/search.index/567c4dff1ab9.js b/pr/395/docs/search.index/567c4dff1ab9.js new file mode 100644 index 000000000..6d546baae --- /dev/null +++ b/pr/395/docs/search.index/567c4dff1ab9.js @@ -0,0 +1 @@ +rn_("AQIBoQAAABPvZPLKBwAA9AEBBgGgMAAAEZd0ASoTAAABAwGwUBKHAAFl9G0JAAASAwEAsgMBAQD0KwYAAAEAAQABAAEBAaEAAAAT5GTyKgYAADIClwcAAIewoBKFAAGg4AAAE7segArzFQQAAAEAAQA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/56fd0f0047d0.js b/pr/395/docs/search.index/56fd0f0047d0.js new file mode 100644 index 000000000..0f9aa8c86 --- /dev/null +++ b/pr/395/docs/search.index/56fd0f0047d0.js @@ -0,0 +1 @@ +rn_("AQIA9VoRAAABAAEAAQABAAGAArBgEqIAAaAgAAAQrm95AQABoMAAABOvafTzEgAABwAWABkAAQYA9BIUAAABAAEAAQABAgD0rBAAAAEAmgABAAECAaBAAAASy3I7MAAAAQAABwACAKoQAQBBEQUA84ACYW9nAwAAhrCAEsoAUrCgEzcAAQwwBjswAAABAAAIAAMAOQwAAG0RAwBBEgMA84ACZWk=") \ No newline at end of file diff --git a/pr/395/docs/search.index/573cd35ae6b3.js b/pr/395/docs/search.index/573cd35ae6b3.js new file mode 100644 index 000000000..ce372395f --- /dev/null +++ b/pr/395/docs/search.index/573cd35ae6b3.js @@ -0,0 +1 @@ +rn_("AQgAOjAAAAEAAAAAAAcAEAAAANYS1xJlE2YTmROaE+UT5hMjgAKwUBLKAFJmbgEAAbBAEqYAAWU7MAAAAQAADQACAD4HDAC+EAAAI4ACoDAAAAwzYWUzgISwUBMWAAGwEBOyAAEIYAE=") \ No newline at end of file diff --git a/pr/395/docs/search.index/57fb74ce5042.js b/pr/395/docs/search.index/57fb74ce5042.js new file mode 100644 index 000000000..da0ec6391 --- /dev/null +++ b/pr/395/docs/search.index/57fb74ce5042.js @@ -0,0 +1 @@ +rn_("AYQCoCAAABDVoDAAABE7aXQBAQA7MAAAAQAAfAACANwHAADJCXsA8wACZWz1QwUAAJ0CEwkjAgEAAQIAOzAAAAEAAN4AAgAtDAAAHQ3dAMcAAACFsLASyAABoBAAABNCsDANGwABFDACOzAAAAEAAAoBCwC5AQYAGwYAAEgG3QCqBwcA/g8CAEoRDQByEQIAORIBALISAQD9EgEAjBMCAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/583daef1b6ad.js b/pr/395/docs/search.index/583daef1b6ad.js new file mode 100644 index 000000000..31ee615d6 --- /dev/null +++ b/pr/395/docs/search.index/583daef1b6ad.js @@ -0,0 +1 @@ +rn_("AQIBsEAT6wABdDswAAABAAANAAYAjBIDAO8SAQBTEwEAahMBAOETAQAFFAEAE4ACsDASuQExaXABBQDzKxMAAAEAAQATgAKgMAAAE4FleVMCA7BwE48AAWJlcwH7DwAAEwACoCAAAAYcZm32dAAAAFsHAQDCAQEAXwc=") \ No newline at end of file diff --git a/pr/395/docs/search.index/592f5e893bf5.js b/pr/395/docs/search.index/592f5e893bf5.js new file mode 100644 index 000000000..82dbe2263 --- /dev/null +++ b/pr/395/docs/search.index/592f5e893bf5.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAN4AAgAtDAAAHQ3dAPMAAW07MAAAAQAA3gACABsGAABIBt0AAQMAOzAAAAEAAAcAAgBmDwUA4hIBAPMAAWM7MAAAAQAAVwAmADQFCwDpBQEAaAkBAEQMAABKDAIA+wwAAAYNAgAYDQEAjQ8AAJIPAQCVDwEAmw8AACAQAgAnEAEALBABAEsQAAC9EAAAwxAAAA4RAQAbEQEALxEBADMRAgCOEQEAnxECAKURAABKEgAAURIBAFYSAQCaEgEAphIDAMgSAQDYEgMA3hIBADUTAQBGEwAArhMAANMTBADcEwIA84ACcncBCQDz7RMAAAEANQABDAGgoAAAFDF38iEUAAABAAEBAPO2DwAAAQABAPOAAnR1AYECsIAT+wABoGAAABQfZHM3AgCBhqBAAAAT0bBgE6UAS7AwE5IAAQKQDgGIArAAE14AAbAAE2AAATQ2M4ADoDAAABEyZG5zYwADoBAAAAVUYWVvAXgAAAA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/597d40647479.js b/pr/395/docs/search.index/597d40647479.js new file mode 100644 index 000000000..52ec9c725 --- /dev/null +++ b/pr/395/docs/search.index/597d40647479.js @@ -0,0 +1 @@ +rn_("AQABsIATsAABcPKsEwAAAQBDBQOgIAAAEQ2gQAAAEkpkZXPztQ8AABgDAQABAgA7MAAAAQAAEwABAN8PEwATgYSgUAAAEL6gEAAADEOgIAAADPwcQAAzgAOwUBMDAAFhZm4=") \ No newline at end of file diff --git a/pr/395/docs/search.index/59b1b3c79cd8.js b/pr/395/docs/search.index/59b1b3c79cd8.js new file mode 100644 index 000000000..ec68c349d --- /dev/null +++ b/pr/395/docs/search.index/59b1b3c79cd8.js @@ -0,0 +1 @@ +rn_("AQcA9KkRAAABAAEAAQAjAQKgcAAAEahhcgHjBwAAAQMAOzAAAAEAAAcAAgBmDwUA4hIBAPMAAmN0OzAAAAEAAFcAJgA0BQsA6QUBAGgJAQBEDAAASgwCAPsMAAAGDQIAGA0BAI0PAACSDwEAlQ8BAJsPAAAgEAIAJxABACwQAQBLEAAAvRAAAMMQAAAOEQEAGxEBAC8RAQAzEQIAjhEBAJ8RAgClEQAAShIAAFESAQBWEgEAmhIBAKYSAwDIEgEA2BIDAN4SAQA1EwEARhMAAK4TAADTEwQA3BMCAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/5a4d4c312dc2.js b/pr/395/docs/search.index/5a4d4c312dc2.js new file mode 100644 index 000000000..7211a4054 --- /dev/null +++ b/pr/395/docs/search.index/5a4d4c312dc2.js @@ -0,0 +1 @@ +rn_("AQMBgBL/ABDpczswAAABAAAFAAEA5BIFACOCAqCQAAAT0mlzAQEA9KwSAAABAIYAAQAjgAKwoBOEAAFjbgGEArCgE5IAAbCgE5QAAWNyAQIAOzAAAAEAAAYAAQAZEAYAE4ICoCAAABN2ZXUBAgDzdREAAAEAAQABgAKwIBOyAAGgYAAAEwxvdmOBA6AQAAASzGFjcwEDAPTkDAAAAQABAAEAAQEA9KwQAAABAJoAAQATgAOgwAAAE96wEAl3AAFucHUBgAKwMA/WAAGhYAAAFBFhZQGBArBAElkAAbBAElsAATQ2AQUA9C8QAAABAKcAAQBTgISgsAAAE5uwMBL2AAEBoAQBCADz8xIAAAcAFgATgAKwQBGaAAFhZQEAAaAgAAAQS3IBDREAAAEAAPREEwAAAQBKAAEApwAAgIWgMAAAEKmgEAAAEMmwoBOmAAEJICEBCQGgQAAAE9Vl8vQTAAABAEOAhKAgAAAT46CQAAATQ6EwAAAT8xBJAAECAPTgEgAAAQCLAAEAE4ACsEASrgABaXkBAQKgMAAAEougQAAAEstncjswAAABAAAHAAIAqhABAEERBQABgAKwABNeAAGwABNgAAE0NgEGAPQdFAAAAQAKAAEA8wICcnYBiRMAAPMCAWX0NxAAAAEApgABAPOAAmR0560/ABKgMAAAEMqgQAAADPmw8BQaAAGwcBMNAAEwYWJjZWdoaWtsbm9wcnN0dXY7MAAAAQAA9gBEAE8FAAAhBgIAKwYDAC4HAACNBwAAtAcCAMQHAADWBwQA3AcAAD0IAABlCAEAWgkBAGAJAQBsCQAAxQkBAMkJewAiDAEAMAwAAD0MAAByDAAAfgwAAHEPAQCZDwEAsA8AABIQAAA7EAEATRAAAFoQAQCdEAEAoRADALIQAwDrEAEACxEAACgRAgBYEQAAcREAAJQRAACYEQEAqBEEADwSAABLEgEAThIAAFgSAABpEgAAnBIBAKoSAQC3EgEAvhIBAMQSAQDHEgAAyhIAANwSAADxEgEAChMAABgTBAAxEwEAQhMAAEQTAQBKEwEAVRMAAIMTAACPEwIAlhMCALATAQC8EwAAzxMBAPYTAQAxFAAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/5aa972e70a3a.js b/pr/395/docs/search.index/5aa972e70a3a.js new file mode 100644 index 000000000..d7d96b586 --- /dev/null +++ b/pr/395/docs/search.index/5aa972e70a3a.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAYAAgAzBgUAMhEAAAEEAbAAErAAAXM7MAAAAQAACwACACETBwAJFAMARwAAAYaggAAAEwWxABQWAAGwQBJzAAGwgBQkAAGwUBKyAAEVABb0eA4AAAEAAQABAAEFAPO7DwAAAQABAGOAA7BAEEAAAW1ubwGBArAADPEAAbAQED4AAXN18wACYWk6MAAAAQAAAAAABwAQAAAA0QeeD3UQdhARExITExMUEwGAA7AAE14AAbAAE2AAAbBwE88AATQ2YQEEAPQdFAAAAQAKAAEAI4ECoEAAABELbXABAgA7MAAAAQAADgAEANQHAQATDQEA/RAJADoRAAABAQA7MAAAAQAACAADAGgSAADREgAAKhQGAAEBADswAAABAAAKAAMAHxECADkTAwASFAMAI4ACoDAAABASZGeXBwAAx6BgAAATg6CAAAASRUBHgAg7MAAAAQAAGAAJALIPAQDEEAEA+RABAAcRAwB4EQYAPRMCAIkTAADfEwEADhQBAAEDAPZ0CQAAUAB1BgEArwABABOAArBwE7AAAW9yAQQBsLAT9gABbPQRDQAAAQDxAgEAAQIAOzAAAAEAAAwAAwBTCQAATBMFAP0TBQABAQDz+Q8AAAEAOgIjgAKgsAAAE69negECADowAAABAAAAAAAJABAAAACGEYcRiRKKEmMTZBO0E7UTJBQlFPMAAW/yxwkAAAEAAQUA9KUPAAABAOMAAQCDAISgQAAADQawcBL0AAGwIAyNAAEQBQI7MAAAAQAACQADAEcFAwD6BQIAdRECAAGAAqAgAAAQ07AgE7AAAWR0AYQCsLAT+QABsAATVgABYXPTgISgsAAAE+NAKAQBgAKgMAAAEZ6wYA+5AAFockf/AQDNoCAAAAYYoCAAAAYZsHATSgABA2c0BzswAAABAAA3ABgAKgUAAF8FBQC9CQAA3wwAAHUPAQBhEAMAGhEAAGgSAAB5EgUA0RIBAPMSAAD6EgAAEBMAAB8TAQApEwAAKxMCAC8TAQCBEwAAiBMAAJQTAQCsEwEAvhMFANITAAAqFAYA") \ No newline at end of file diff --git a/pr/395/docs/search.index/5ab07c163f18.js b/pr/395/docs/search.index/5ab07c163f18.js new file mode 100644 index 000000000..00e30aff4 --- /dev/null +++ b/pr/395/docs/search.index/5ab07c163f18.js @@ -0,0 +1 @@ +rn_("AYMCoJAAABOqoJAAABOrMjMBgAKgUAAAE9KggAAAEwJjZfMAAnN08qgHAAABAAEAAaCwAAATRmLz3RIAABQBAQAjgAKwAA+wAyxkcgEAAbCAE7AAAXDyrBMAAAEAQwADoCAAABENoEAAABJKZGVz87UPAAAYAwEAE4ICoCAAABOcZWnzgAJjZQEEADswAAABAAAJAAQAsgkBAFMQAwCPEAEAZxMBALOAhKCQAAAUAxABQg==") \ No newline at end of file diff --git a/pr/395/docs/search.index/5add61f19273.js b/pr/395/docs/search.index/5add61f19273.js new file mode 100644 index 000000000..060d3c328 --- /dev/null +++ b/pr/395/docs/search.index/5add61f19273.js @@ -0,0 +1 @@ +rn_("AQsA9B8TAAABAA8AAQABBAGwQBLeAAFlATgSAADzAAF0AdETAAABDQDz6BMAADAAAQABEADzEBQAABYAAQDzgQNrbXMBAAA7MAAAAQAABgACAAsTAABuEwUAI4YCsEAT3AABZXNjgAOwQBB3AAFjcnQzgISwYBCRAAGgEAAABjABCAk=") \ No newline at end of file diff --git a/pr/395/docs/search.index/5d2c023a10ee.js b/pr/395/docs/search.index/5d2c023a10ee.js new file mode 100644 index 000000000..c6a2d5745 --- /dev/null +++ b/pr/395/docs/search.index/5d2c023a10ee.js @@ -0,0 +1 @@ +rn_("AYEBcBfleFb1dAEEAbBAE9YAAWX1nhMAAAEAAQABAAEAAQIAOzAAAAEAAAsAAgBMEwUA/RMFAPMDAW07MAAAAQAADQAEAN8MAAAaEQAAeRIFAL4TBQDzgQJjZfOAAmFl") \ No newline at end of file diff --git a/pr/395/docs/search.index/5d68fc368cd0.js b/pr/395/docs/search.index/5d68fc368cd0.js new file mode 100644 index 000000000..e4fb62666 --- /dev/null +++ b/pr/395/docs/search.index/5d68fc368cd0.js @@ -0,0 +1 @@ +rn_("AQEA9KMHAAABAAEAAQABAQGgQAAAETZz9U8MAAC9BDIBAQABABOCAqAgAAANF2VyI4ADoEAAAAxOsDAJgQABZW90AQAA9J8HAAABAAEAAQABAgD0HxMAAAEADwABABOAArBAE8wAAWJ284ACb3gBCgD1WBMAAAEAAQBwAAEAAYECoCAAABCosFASTwABYW8BBAGgAAAAE11zAacQAABjAwOwUBEYAAFjZXP2XQkAAAEAAQAsAAEAqwgBAQD05AwAAAEAAQABACOAArAwEsYBS3J08wCGAcAH8soBAACPDw==") \ No newline at end of file diff --git a/pr/395/docs/search.index/5d85d427a4e9.js b/pr/395/docs/search.index/5d85d427a4e9.js new file mode 100644 index 000000000..c2fd602d6 --- /dev/null +++ b/pr/395/docs/search.index/5d85d427a4e9.js @@ -0,0 +1 @@ +rn_("AQIA9LgQAAABAHkBAQABAAD0rg8AAAEA5gACASOCArBQEtoAAWVzAQIA9KwSAAABAIYAAQABAwD04BIAAAEAiwABAPOFAmJ1AQIA9CIRAAABAGABAQDzgAJpbPMAA2J0dTowAAABAAAAAAAPABAAAADbB2cIaAhpCGoI1Q+OECoTpRPtE+4T8BP7E/wTHxQjFAEMAPOMEwAAAQABAAGAArAACXUAAaAgAAANDmRuAQYA9KoSAAABAIYAAQDzAwFi9LYQAAABAHkBAQCXBgAAh7BAD6wAAaBwAAASPLCQEtgAATgJAzswAAABAAAiABEAPAYDAJwHAQADDQIACQ0CAKUPAQD3DwEA/A8BABkQBgBcEAAAiRABAAwTAAAuEwAAUhMAAOQTAADvEwAADRQAABwUAAA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/5db8f7a37811.js b/pr/395/docs/search.index/5db8f7a37811.js new file mode 100644 index 000000000..efed36e0e --- /dev/null +++ b/pr/395/docs/search.index/5db8f7a37811.js @@ -0,0 +1 @@ +rn_("AQQBoFAAABPOcwE1EgAAAYICsEASWQABsEASWwABNDYBBgD0HRQAAAEACgABAPMCAXIBiRMAAPMDAWX0NxAAAAEApgABAFOEA7CwE6YAAWFlaWOAA6BQAAAUBGNldA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/5e234a0755e9.js b/pr/395/docs/search.index/5e234a0755e9.js new file mode 100644 index 000000000..118684835 --- /dev/null +++ b/pr/395/docs/search.index/5e234a0755e9.js @@ -0,0 +1 @@ +rn_("AQAA9LgQAAABAHkBAQAjgQKgYAAAEsdiaQEAAPSuDwAAAQDmAAIBI4ICsFAS2gABZXMBiQKwIBPhAAGgMAAAE+ppcAECAPSsEgAAAQCGAAEAAQMA9OASAAABAIsAAQDzhQJidQECAPQiEQAAAQBgAQEA84ACaWy3AQAAhaAgAAAStBIBDDowAAABAAAAAAAUABAAAADbB2cIaAhpCGoIMgxkDGUMZgxnDNUPjhAqE6UT7RPuE/AT+xP8Ex8UIxQBBQDzjBMAAAEAAQBDhgOwkBQFAAGgAAAAEk5iZHcBgAKwAAl1AAGwIA0OAxhkbgEDAPOsDwAAAQD5AAECAPY3DAAAAQA0AwEAoAMBAAEAADswAAABAAAGAAIACxMAAG4TBQAjgQKwQBPcAAFlc1OAA7AACZAAAWV4eQGAA6BAAAAUA7AgEqgAAbAACY4AAWVyeAGAAqBAAAAM+bBAD9YAAWdsFw0AAMiwwBM1AAGgEAAAER6gMAAAE4KwQAzxAAEARqALOjAAAAEAAAAAAAkAEAAAAOsF7AX3DPgMDA2oDwEQAhCkEqUSAQYA9KoSAAABAIYAAQDzAwFi9LYQAAABAHkBAQC3DAAAiaBwAAASPLCQEtgAAbBgD/kAAaAwAAAS/DiJEzowAAABAAAAAAAtABAAAAA8Bj0GPgY/BpwHnQdWCVcJWAmICYkJPAwDDQQNBQ0JDQoNCw2lD6YP9w/4D/wP/Q8ZEBoQGxAcEB0QHhAfED0QQhBcEIkQihD3EPgQixIMEy4TUhPkE+8TDRQcFA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/5fc89dcacf77.js b/pr/395/docs/search.index/5fc89dcacf77.js new file mode 100644 index 000000000..38843f321 --- /dev/null +++ b/pr/395/docs/search.index/5fc89dcacf77.js @@ -0,0 +1 @@ +rn_("AQQBsAASsAABczswAAABAAALAAIAIRMHAAkUAwBHAAAFhqCAAAATBbEAFBYAAbBAEnMAAbCAFCQAAbBQErIAARUAFvR4DgAAAQABAAEAQwCEoCAAAAmGoNAAABMPoDAAAAllFQAE89kBAAABAAMG") \ No newline at end of file diff --git a/pr/395/docs/search.index/6037a95cf9ab.js b/pr/395/docs/search.index/6037a95cf9ab.js new file mode 100644 index 000000000..e05a86c54 --- /dev/null +++ b/pr/395/docs/search.index/6037a95cf9ab.js @@ -0,0 +1 @@ +rn_("AYICsEASWQABsEASWwABNDYBBgD0HRQAAAEACgABAPMCAXIBiRMAAPMDAWX0NxAAAAEApgABAFOAA7CwE6YAAWFlaQ==") \ No newline at end of file diff --git a/pr/395/docs/search.index/610707ac8244.js b/pr/395/docs/search.index/610707ac8244.js new file mode 100644 index 000000000..7257991e9 --- /dev/null +++ b/pr/395/docs/search.index/610707ac8244.js @@ -0,0 +1 @@ +rn_("AQsBoEAAABOuZfLYEwAAAQABBQD0qREAAAEAAQABAAGDArAAEJ0AAbBQEv0AAWR3E4ACoKAAABNqZXYBAgD0uBAAAAEAeQEBAAEAAPSuDwAAAQDmAAIBI4ICsFAS2gABZXMBAgD0rBIAAAEAhgABAAEDAPTgEgAAAQCLAAEA84UCYnUBAgD0IhEAAAEAYAEBAPOAAmls8wADYnR1OjAAAAEAAAAAAA8AEAAAANsHZwhoCGkIagjVD44QKhOlE+0T7hPwE/sT/BMfFCMUAQwA84wTAAABAAEAAYACsAAJdQABoCAAAA0OZG4BBgD0qhIAAAEAhgABAPMDAWL0thAAAAEAeQEBAJcGAACHsEAPrAABoHAAABI8sJAS2AABOAkDOzAAAAEAACIAEQA8BgMAnAcBAAMNAgAJDQIApQ8BAPcPAQD8DwEAGRAGAFwQAACJEAEADBMAAC4TAABSEwAA5BMAAO8TAAANFAAAHBQAAAEDAPS+EgAAAQA3AQEA84ACY213AQCAxbGAFCwAAUCBBAg=") \ No newline at end of file diff --git a/pr/395/docs/search.index/6207a70bbfba.js b/pr/395/docs/search.index/6207a70bbfba.js new file mode 100644 index 000000000..f26de172c --- /dev/null +++ b/pr/395/docs/search.index/6207a70bbfba.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAkABACyCQEAUxADAI8QAQBnEwEAAQIBsEAT6wABdDswAAABAAANAAYAjBIDAO8SAQBTEwEAahMBAOETAQAFFAEAE4ACsDASuQExaXABiAKwABNeAAGwABNgAAE0NhOBAqAwAAARMmRzAYACoCAAAAyMsGATjwABbngBBQDzKxMAAAEAAQATgAKgMAAAE4FlefMAhBIAA/L7DwAA2QDzgAJydA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/62bb924ae01b.js b/pr/395/docs/search.index/62bb924ae01b.js new file mode 100644 index 000000000..7054f2ee9 --- /dev/null +++ b/pr/395/docs/search.index/62bb924ae01b.js @@ -0,0 +1 @@ +rn_("AQAA9KMHAAABAAEAAQABhQKwcBOPAAGwMBLvAAFlcxMAA7BAD5UAAaBgAAAROmJlczswAAABAAASAAQAJAYBADMHCgALEAEAXRADAAECAPMvBgAAigkBAAEDAPWNDwAAmgABAAQAAQABCgGgQAAAE9Vl8vQTAAABACMAA6AwAAAQyqCQAAATEDBjaPWoEQAAcAEBAAEAAQDzgAJkcucEAACHsBAJUgL/sCAMJAD2oDAAAA+xEkE4OzAAAAEAABAABgB+AAIAFQYAAEQGAwAFCAMARggDAC8MAAA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/639a2b456721.js b/pr/395/docs/search.index/639a2b456721.js new file mode 100644 index 000000000..7c525e2b8 --- /dev/null +++ b/pr/395/docs/search.index/639a2b456721.js @@ -0,0 +1 @@ +rn_("AQcAOzAAAAEAAHwAAgClEAAArhF7AAEDADswAAABAAB7AAEA+w17AAECADswAAABAAAHAAEAXAwHAPMAA2JpdDswAAABAAB8AAIALQUAAGYFewA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/64d55e5f1bdd.js b/pr/395/docs/search.index/64d55e5f1bdd.js new file mode 100644 index 000000000..0defff5de --- /dev/null +++ b/pr/395/docs/search.index/64d55e5f1bdd.js @@ -0,0 +1 @@ +rn_("AYACsRAUBwABsBAMbQABZW8BBQA7MAAAAQAADQABAEoRDQABAgDzwg8AAAEAAQABAgD0vg8AAAEAAQABAAEDADswAAABAAAIAAEAbBAIAIcHAACHsKATQAABsNATzAABoFAAABObjBQR9cAHAADXCAEAAQABAPOBAmx08wABaTswAAABAAB9AAMATwQAAKUQAACuEXsAAQUA8+0TAAABADUAAQABoEAAABCpZfNgCQAAAQDbCBOAAqAwAAAPm2Ry8wABZfNZBwAAAQABAPOAAmh0AQEAOzAAAAEAAHwAAgClEAAArhF7ABOAAqBQAAAP3Gl0AQgA9HQTAAABAH4ABQATAISw8BPvAC2wkBPTAAGwUBLGAUtAiQA7MAAAAQAAEgAJACMQAgCVEQEAtRIBANYSAQAGEwEAZRMBAJkTAQCoEwEA5RMBANcBAACGsIARYwABoEAAAAmDCDAmOjAAAAEAAAAAAA0AEAAAAIMAhADKB74JJAwaDbgQuRAyEjMSrBKtEjMTNBM=") \ No newline at end of file diff --git a/pr/395/docs/search.index/64e46650dfe4.js b/pr/395/docs/search.index/64e46650dfe4.js new file mode 100644 index 000000000..b4a737edc --- /dev/null +++ b/pr/395/docs/search.index/64e46650dfe4.js @@ -0,0 +1 @@ +rn_("AQMBgBL/ABDpczswAAABAAAFAAEA5BIFACOCAqCQAAAT0mlzBwEAhMWwUBKuAAGgoAAAE0OwcBMNAAGg0AAAE96AARgI") \ No newline at end of file diff --git a/pr/395/docs/search.index/657c7a54fc8d.js b/pr/395/docs/search.index/657c7a54fc8d.js new file mode 100644 index 000000000..96caf6cb0 --- /dev/null +++ b/pr/395/docs/search.index/657c7a54fc8d.js @@ -0,0 +1 @@ +rn_("AQIBsIAS0wABdvTBCQAAAQC1CQEA8wABYwF/EgAAAQICoLAAABPIoLAAABPJY3PzgBAAAAEAXAABgQKwUBGaAAGgwAAAE5toafOCAmVp84ACZW8=") \ No newline at end of file diff --git a/pr/395/docs/search.index/665d99930d33.js b/pr/395/docs/search.index/665d99930d33.js new file mode 100644 index 000000000..16654abbf --- /dev/null +++ b/pr/395/docs/search.index/665d99930d33.js @@ -0,0 +1 @@ +rn_("AQABsPAUGgABa/WREwAABQABAAEAJAATAAKxABQHAAFkbvJqEgAAAQATgAKwEAxtAAFlbwEFADswAAABAAANAAEAShENAAECAPPCDwAAAQABAAECAPS+DwAAAQABAAEAAQMAOzAAAAEAAAgAAQBsEAgAAYACoEAAABC+sMATzAABZXMBAQA7MAAAAQAAEwABAN8PEwAjgAKwkBNAAAFpbTc2AACKoBAAAAxDoFAAABObsHATfwABoCAAAAz8nFUR9cAHAADXCAEAAQABAPOAAmx0") \ No newline at end of file diff --git a/pr/395/docs/search.index/66af3e6bd759.js b/pr/395/docs/search.index/66af3e6bd759.js new file mode 100644 index 000000000..1ad307c9a --- /dev/null +++ b/pr/395/docs/search.index/66af3e6bd759.js @@ -0,0 +1 @@ +rn_("AQgA9C8QAAABAKcAAQABAAGggAAAFCBm8lUJAADACQEHAPXqEgAAAQABAAEAAQAjgAKgUAAAERBhb/MCAmZzOzAAAAEAAAgABAC3BwAA6QcFAA8NAABJEQAAM4ADoEAAAAwvYWVv") \ No newline at end of file diff --git a/pr/395/docs/search.index/677118915139.js b/pr/395/docs/search.index/677118915139.js new file mode 100644 index 000000000..ea66dd95e --- /dev/null +++ b/pr/395/docs/search.index/677118915139.js @@ -0,0 +1 @@ +rn_("AQQBsAASsAABczswAAABAAALAAIAIRMHAAkUAwBHAAABhqCAAAATBbEAFBYAAbBAEnMAAbCAFCQAAbBQErIAARUAFvR4DgAAAQABAAEAAQUA87sPAAABAAEAY4CEsEAQQAABoAAAAAdLAPAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/693c05a1eff1.js b/pr/395/docs/search.index/693c05a1eff1.js new file mode 100644 index 000000000..e54d1f0f0 --- /dev/null +++ b/pr/395/docs/search.index/693c05a1eff1.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAUAAQBsEgUAAQACsEATNQABoKAAABO7ZXI6MAAAAQAAAAAABwAQAAAALwcwB8sHzAeqD6sPkxCUEFOAA6CgAAATm2dpcw==") \ No newline at end of file diff --git a/pr/395/docs/search.index/6aea33209657.js b/pr/395/docs/search.index/6aea33209657.js new file mode 100644 index 000000000..2d74f3c65 --- /dev/null +++ b/pr/395/docs/search.index/6aea33209657.js @@ -0,0 +1 @@ +rn_("AYACsEAS9AABoMAAABOjZW8BAgD0fxEAAAEAAQABABMAA7AgEYMAAbCAE7QAAWFzdAHpDAAAAQQA87sPAAABAAEAs4CEsEATAwABQQAG") \ No newline at end of file diff --git a/pr/395/docs/search.index/6af0c970065d.js b/pr/395/docs/search.index/6af0c970065d.js new file mode 100644 index 000000000..ee5e69d68 --- /dev/null +++ b/pr/395/docs/search.index/6af0c970065d.js @@ -0,0 +1 @@ +rn_("AYACsAAP3QABsCAMdwABZWkBiQKwIBPhAAGgMAAAE+ppcBMAA6AgAAAStKCAAAASx2VpdfUyDAAAMgABAAEAAQABhgKwkBQFAAGgAAAAEk5iZAGAArAwEqgAAaAwAAAQpnJ21wAAAIWgMAAAECagMAAAEvwQARM6MAAAAQAAAAAACgAQAAAAVglXCVgJiAmJCTwMPRBCEPcQ+BCLEmOAA6AQAAAJS2RtdA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/6b88e2cdd5d5.js b/pr/395/docs/search.index/6b88e2cdd5d5.js new file mode 100644 index 000000000..0594f9e49 --- /dev/null +++ b/pr/395/docs/search.index/6b88e2cdd5d5.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAABYACQATEAUAGBEBAKIRAgBiEgEAkBIEAHkTAQCdEwAAyBMAAOcTAAAjgAOh8AAAFDGw8BQkAAFjZmcjgAKwUBM3AAFtbgEFAPN5EwAAAQBtAPMEAWMBOREAAAEBAbBwE8wAAXA7MAAAAQAACQACAD4IBwCCEAEAAQIAOzAAAAEAAAsAAgBMEwUA/RMFAPMBAW07MAAAAQAADQAEAN8MAAAaEQAAeRIFAL4TBQATAAKgMAAADONlc/KmEQAAAQABggKwcBOsAAGgQAAAEMZpc/cBAICGsJATkgABkEkI") \ No newline at end of file diff --git a/pr/395/docs/search.index/6bcf60824292.js b/pr/395/docs/search.index/6bcf60824292.js new file mode 100644 index 000000000..cb39761f0 --- /dev/null +++ b/pr/395/docs/search.index/6bcf60824292.js @@ -0,0 +1 @@ +rn_("AQcA9KkRAAABAAEAAQAjAAKgcAAAEahhcgHjBwAAAQAA9s4QAAABAE4DAQAKAAEAAQAAOzAAAAEAAAYAAgALEwAAbhMFACOIArBAE9wAAWVzAQ0BsEAT1gABZfWeEwAAAQABAAEAAQABBQA7MAAAAQAACAADAKIRAgCQEgQAnRMAANcDAACGHr2hQAX1FIQG9U0FAAABAIwHAQABAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/6bd9cf2870c6.js b/pr/395/docs/search.index/6bd9cf2870c6.js new file mode 100644 index 000000000..9c3ba2db4 --- /dev/null +++ b/pr/395/docs/search.index/6bd9cf2870c6.js @@ -0,0 +1 @@ +rn_("AQMA9GoJAAABABEDAQABAwDzeQwAAAEAAQDzgAJpbwEBAbBQELsAAWP0xwcAAAEAAQDzASOBAqBQAAAQf2NuAQQAOzAAAAEAAAcAAwByEQIA/RIBAIwTAgABAQA6MAAAAQAAAAAABwAQAAAAlRGWEbUSthIGEwcTqBOpE/MJAWkBuhAAANcCAACHsMAS9gABoFAAAA93oRAAABPOFAEXOzAAAAEAACMAAQCTASMA") \ No newline at end of file diff --git a/pr/395/docs/search.index/6ccbc319ac73.js b/pr/395/docs/search.index/6ccbc319ac73.js new file mode 100644 index 000000000..82c6b7600 --- /dev/null +++ b/pr/395/docs/search.index/6ccbc319ac73.js @@ -0,0 +1 @@ +rn_("AQEA9NEHAADNB9cAAQABBQD0wBIAAAEAhwABAPMCAWP1EBAAAAEAugABAAEAYwAEoDAAAAxQoHAAABGJZW5zdvMuEAAAkwD8AQ==") \ No newline at end of file diff --git a/pr/395/docs/search.index/6d3546009992.js b/pr/395/docs/search.index/6d3546009992.js new file mode 100644 index 000000000..dd19b951a --- /dev/null +++ b/pr/395/docs/search.index/6d3546009992.js @@ -0,0 +1 @@ +rn_("AQUBsEASVgABZfJ3EgAAAQABAAA7MAAAAQAAEQAEANcBAQBsEAgAZREDAFMSAgABCgGwQBPWAAFl9Z4TAAABAAEAAQABAEcGAACHsCAQwgITsMAUGgABoCAAAAdnsDAHwgABGIkKOjAAAAEAAAAAAA4AEAAAALIHswe+B78Hlgk6DDsMQQxCDI0MjgzXDNgMohKjEg==") \ No newline at end of file diff --git a/pr/395/docs/search.index/6ef23363b488.js b/pr/395/docs/search.index/6ef23363b488.js new file mode 100644 index 000000000..8ca55af21 --- /dev/null +++ b/pr/395/docs/search.index/6ef23363b488.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAcAAQCpCQcAAQIA9wcQAADOAIMCAQABAHAAAQATgAKwMBCWAKVpdPMAAXPy+RMAAAEAM4ADsRATdwABZG5w") \ No newline at end of file diff --git a/pr/395/docs/search.index/6fd721d22688.js b/pr/395/docs/search.index/6fd721d22688.js new file mode 100644 index 000000000..5c39425a9 --- /dev/null +++ b/pr/395/docs/search.index/6fd721d22688.js @@ -0,0 +1 @@ +rn_("AQADoKAAABPJoCAAABMPsDARkgABZWl08oUSAAABAAEDADswAAABAAAGAAEAEREGAAEIAPR0EwAAAQB+AAUAEwEDsJAT0wABsFASxgFLZ2xwOzAAAAEAABIACQAjEAIAlREBALUSAQDWEgEABhMBAGUTAQCZEwEAqBMBAOUTAQABgAKwMBGOAAGwMBGQAAFydgECAbAAEksAAWQ7MAAAAQAABQACAIoRAwB1EgEAI4ECoJAAABPIaW8BBwD05BMAAAsAHgAPAHcLAAGIsFASTQD6sGASxAABlQkDOjAAAAEAAAAAAA0AEAAAAOoM6wzsDH0Ofg6AEIEQuhDdEDwRPRE+ET8RvBIjgAKgEAAADOlubw==") \ No newline at end of file diff --git a/pr/395/docs/search.index/7017e57856f5.js b/pr/395/docs/search.index/7017e57856f5.js new file mode 100644 index 000000000..c16223955 --- /dev/null +++ b/pr/395/docs/search.index/7017e57856f5.js @@ -0,0 +1 @@ +rn_("AQkBoIAAABQgZgEVEwAAAQIA9YsQAAABAAEANQIBAPMAAml1OjAAAAEAAAAAAAcAEAAAAGkQgBJWE1cThhOHE6MTpBMBCAD1thMAAAEAAQABAAEA8wIBZvVOEAAAAQABAAEAAQATgAKwoBP5AAFydPMAAmFz83sQAAABAAEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/71b46e229639.js b/pr/395/docs/search.index/71b46e229639.js new file mode 100644 index 000000000..dc776609c --- /dev/null +++ b/pr/395/docs/search.index/71b46e229639.js @@ -0,0 +1 @@ +rn_("AQYA9DkTAAABAAEAAQABDwD0EhQAAAEAAQABAAEAAbDwFBoAAWv1kRMAAAUAAQABACQAAQIA9C8QAAABAKcAAQD3AAAAhbEAFAcAAYkwAPJqEgAAAQATgAKwEAxtAAFlbwEFADswAAABAAANAAEAShENAAECAPPCDwAAAQABAAECAPS+DwAAAQABAAEAAQMAOzAAAAEAAAgAAQBsEAgAAYACoAAAAAxDsDAT1gABcXIBgAKgQAAAEL6wwBPMAAFlcwEBADswAAABAAATAAEA3w8TACOAArCQE0AAAWltdzYAAIqgUAAAE5uwcBN/AAGgIAAADPycVRE7MAAAAQAACQADAMAHAACXEAMAnhMEAAECAbAwESQAAXABNhIAALOAhKAgAAARiQIoBA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/71d860ce371b.js b/pr/395/docs/search.index/71d860ce371b.js new file mode 100644 index 000000000..a0ed8f427 --- /dev/null +++ b/pr/395/docs/search.index/71d860ce371b.js @@ -0,0 +1 @@ +rn_("AQABsGAThgABZQG7EwAAAQYA9L4SAAABADcBAQABBAD02BIAAAEA+gABAPMAA2VyczowAAABAAAAAAAJABAAAAB+CX8JgAn0EDERLRIuEjsSfxOAEw==") \ No newline at end of file diff --git a/pr/395/docs/search.index/724367660563.js b/pr/395/docs/search.index/724367660563.js new file mode 100644 index 000000000..43e898811 --- /dev/null +++ b/pr/395/docs/search.index/724367660563.js @@ -0,0 +1 @@ +rn_("AQwA8z0TAAABAAEAAQYA8x8RAAABAAEAAYICsGASzQABsNAT2AABaXITgAKwEAz0AAFlaRcDAIOIsTAT9AABoGAAABEeoFAAABCxsJASpAABsEAQrwABtBAH8wABYfSUCQAAAQBgBwEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/73a80984aee8.js b/pr/395/docs/search.index/73a80984aee8.js new file mode 100644 index 000000000..e98f8bf3d --- /dev/null +++ b/pr/395/docs/search.index/73a80984aee8.js @@ -0,0 +1 @@ +rn_("AQUA9F0SAAABAJoAAQABCAD0CBMAAAEAgQABADMBA7BAEt4AAWFiZfX/DAAAAQD0AkQCwwABAAD1SAwAAAEAMQMBAAEA84ACc3UjgQKwgBMDAAFydAEGAPQ7EAAAAQCvAAEAUwCEsIASlwABoKAAABQxAgEU9fUFAAABANsNUAABAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/73e02919d8d7.js b/pr/395/docs/search.index/73e02919d8d7.js new file mode 100644 index 000000000..6a66ab41f --- /dev/null +++ b/pr/395/docs/search.index/73e02919d8d7.js @@ -0,0 +1 @@ +rn_("AYACsHARJAABsCATzAABYWQBBQD0zxIAAAEAiwABAPMAAWMBRRIAACOAArCgE7IAAXN5AQYA9MQQAAABALgAAQCTgISgAAAAEQuxIBQOAAEJIAHzgAJlaQ==") \ No newline at end of file diff --git a/pr/395/docs/search.index/7403472d979d.js b/pr/395/docs/search.index/7403472d979d.js new file mode 100644 index 000000000..c2df7e80d --- /dev/null +++ b/pr/395/docs/search.index/7403472d979d.js @@ -0,0 +1 @@ +rn_("AQUA9f4PAAABAAEAOQIBAAEEAaAAAAAQjnP1CQ0AAAEAAQDxAgEAAQIA9VoRAAABAAEAAQABAAGAArBgEqIAAaAgAAAQrm95AQABoMAAABOvafTzEgAABwAWABkAAQYA9BIUAAABAAEAAQABAgD0rBAAAAEAmgABAAECAaBAAAASy3I7MAAAAQAABwACAKoQAQBBEQUA84ACYW9nAwAAhrCAEsoAUrCgEzcAAQwwBjswAAABAAAIAAMAOQwAAG0RAwBBEgMA84ICZWlHAQAAhbBwE0QAAaCAAAAUIKBAAAAPtLBABPRVCQAASgYBAHUDAYECoCAAABCosFASTwABYW8BBAGgAAAAE11zAacQAABjBAOwUBEYAAFjZXP2XQkAAAEAAQAsAAEAqwgBAAA7MAAAAQAAJAAIAKoHBwD+DwIAShENAHIRAgA5EgEAshIBAP0SAQCMEwIAAQACsBAMIgABsDATxgABZXD0oQ8AAAEA4gABAAEAA7BAE5QAAaFAAAAT+KBQAAARlGJjZgF1BwAA44CEsUAUHQABAjAQAQEDoLAAABNSsEAQagABoGAAABFAY2V09FwHAACuCEMC+gABAwA7MAAAAQAABQABAGwSBQABAAGgoAAAE7ty9KoPAAABAOgAAQDzgQJncwEHAPXqEgAAAQABAAEAAQABgAKgQAAAERCwUBD5AAFjafOAAmFvAQABoCAAABEwcvNODAAAKQNcAROAAoAF7QAcQXJ4AQMCsEASqAABoDAAABL8ZXfyQhAAALYAE4MCoHAAABMuZ28BAgD1jhIAAAEA3AB2AAEAE4ACsDASuQExaXABBgDzKxMAAAEAAQDzBQJicwH7DwAAAQIA9NgPAAABAAEAAQAjgAKgYAAAEjViY/e/AACMoJAAABI2O8kaOjAAAAEAAAAAABcAEAAAAOMB8QHyATcEOAQ5BHwHtwfpB+oH6wfsB+0H7gcdDIkMDw32DwgQCRDJEEkRBxQIFA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/745e0125b99d.js b/pr/395/docs/search.index/745e0125b99d.js new file mode 100644 index 000000000..e62363e03 --- /dev/null +++ b/pr/395/docs/search.index/745e0125b99d.js @@ -0,0 +1 @@ +rn_("AQACoDAAABDKoJAAABMQMGj1qBEAAHABAQABAAEAE4ACoDAAAA+NZHIjAYSwEAlSAv+wIAwkAPagMAAAD7ESABj1FQYAAC8AAQABAAEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/751ec5fe38a5.js b/pr/395/docs/search.index/751ec5fe38a5.js new file mode 100644 index 000000000..6abf2bebc --- /dev/null +++ b/pr/395/docs/search.index/751ec5fe38a5.js @@ -0,0 +1 @@ +rn_("AQQBsAASsAABczswAAABAAALAAIAIRMHAAkUAwBHAAAEhqCAAAATBbEAFBYAAbBAEnMAAbCAFCQAAbBQErIAARUAFvR4DgAAAQABAAEA") \ No newline at end of file diff --git a/pr/395/docs/search.index/75634231c1f5.js b/pr/395/docs/search.index/75634231c1f5.js new file mode 100644 index 000000000..384e5e4ef --- /dev/null +++ b/pr/395/docs/search.index/75634231c1f5.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABE2c/VPDAAAvQQyAQEAAQATggKgIAAADRdlcgECAPSqDwAAAQDoAAEAI4ECoBAAAAxOZWkTgAKg8AAAE5xucjMAA7AwCYEAAWVvdAGFEQAA") \ No newline at end of file diff --git a/pr/395/docs/search.index/7743460538bb.js b/pr/395/docs/search.index/7743460538bb.js new file mode 100644 index 000000000..2763bbb45 --- /dev/null +++ b/pr/395/docs/search.index/7743460538bb.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAD4AAgB5CQAAmQw9AAGAArBQEHcAAbCwEx8AAWFsYwAEsEAPdQABoBAAABAxYnBydjswAAABAAAGAAEAEREGAAGAArCAEMEB/LDgE4QAAWlyAQMBsEARDgABZfIuDAAASQIBBQA7MAAAAQAABgABABkQBgABAwD05AwAAAEAAQABAPMAAnBzOjAAAAEAAAAAAA4AEAAAACEGIgYjBi4HbAlyDH4MshCzELQQtRBLEkwSnBKdElcAAACFsFAQaQIXsFAPkQDGoEAAABE6SIADOzAAAAEAAA8ABgAuBQAA4gUDAH0PAACKEQMARhIDAHUSAQDTAISgYAAADPYQCAbziQEAAAEATws=") \ No newline at end of file diff --git a/pr/395/docs/search.index/77cd02480e18.js b/pr/395/docs/search.index/77cd02480e18.js new file mode 100644 index 000000000..2f80d7956 --- /dev/null +++ b/pr/395/docs/search.index/77cd02480e18.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABPVZfL0EwAAAQABgAOgMAAAEjaw4BO0AAGwkBP2AAFhY22HAQCAhrAQENsAAbAQEPsAAaCgAAAT46DwAAAUAzFJAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/782860c5c65d.js b/pr/395/docs/search.index/782860c5c65d.js new file mode 100644 index 000000000..cbe56fb45 --- /dev/null +++ b/pr/395/docs/search.index/782860c5c65d.js @@ -0,0 +1 @@ +rn_("AYECoCAAABQEsEAPuQABYnABBQD0zxIAAAEAiwABAPMDAWMBRRIAAAEMAPQJFAAAAQABAAEA84ACbnYBgAKwMAxwAAGgMAAADHJpbwECAaAAAAAT0nMBnhEAACOAAqBAAAATg2Fl5wEAgIWwAAXvAAGQSAE=") \ No newline at end of file diff --git a/pr/395/docs/search.index/792cc1abc097.js b/pr/395/docs/search.index/792cc1abc097.js new file mode 100644 index 000000000..18321c92b --- /dev/null +++ b/pr/395/docs/search.index/792cc1abc097.js @@ -0,0 +1 @@ +rn_("AQMA81MSAAABAAEAI4ECsSAUJAABY2sBBAGgQAAAEZ9l8xANAAC3AwEAAQAA9aEQAAABAAEAAQBmAnOAhKAwAAATrggwAQ==") \ No newline at end of file diff --git a/pr/395/docs/search.index/7a1f32acb3d1.js b/pr/395/docs/search.index/7a1f32acb3d1.js new file mode 100644 index 000000000..5baef4fe1 --- /dev/null +++ b/pr/395/docs/search.index/7a1f32acb3d1.js @@ -0,0 +1 @@ +rn_("AQIA9Y4RAAABAAsBAQBDAQEBAPfQEAAAAQABAKICAQB+AAUAAQIA9GUQAAABAAEAAQATgAKg0AAAE+Njc/OAAmVvI4ACsAAHvAABbHTzAAJhcvJuDwAAAQA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/7b8a6a4a9fb4.js b/pr/395/docs/search.index/7b8a6a4a9fb4.js new file mode 100644 index 000000000..5f72f7c24 --- /dev/null +++ b/pr/395/docs/search.index/7b8a6a4a9fb4.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAkAAQD9EAkAAQIAOzAAAAEAABMAAQDfDxMAE4GEoFAAABC+oBAAAAxDoCAAAAz8HEAAE4ACsFATAwABYW4zgAOwMA+JAAFpc3Q=") \ No newline at end of file diff --git a/pr/395/docs/search.index/7c16cb6d06bb.js b/pr/395/docs/search.index/7c16cb6d06bb.js new file mode 100644 index 000000000..123fd360d --- /dev/null +++ b/pr/395/docs/search.index/7c16cb6d06bb.js @@ -0,0 +1 @@ +rn_("AQUA9F0SAAABAJoAAQABCAD0CBMAAAEAgQABADMBA7BAEt4AAWFiZTowAAABAAAAAAAJABAAAACaB5sH/wwADYgP9A8+ED8QOBL7EgEAADswAAABAAAeAA4AUAUBAAMGCACBCQEASAwBAPEMAQB8DgAAeg8CAJAPAACuDwEAlRABAKgQAAA7EQAAkhEBAJcRAAABgQKwABErAAGwABEtAAE0NiOAArCAE/kAAW9wAQABsEAS2gABZfM2EQAAPQEBALcBAICFoGAAABDUEAEL") \ No newline at end of file diff --git a/pr/395/docs/search.index/7c840b92ee85.js b/pr/395/docs/search.index/7c840b92ee85.js new file mode 100644 index 000000000..cae40e371 --- /dev/null +++ b/pr/395/docs/search.index/7c840b92ee85.js @@ -0,0 +1 @@ +rn_("AQAA9GEQAAABAAEAAQDzAgFz9ZwJAAABAAEAAQD1BQEGAPSpEQAAAQABAAEAAQEAOzAAAAEAAAYAAQAqFAYA8wYBZfTfEwAAAQAuAAEAEwYDsEAUBwABoGAAABOIYXJ1ATMQAADzgAJhZQEDAPRlEQAAAQABAAEAAQIA9IkPAAABAAgEAQABgAOwAAfhAAGgAAAADDWwcBBAAAFucnOnBwAAh6BwAAARqLBwEokAAZEFQTowAAABAAAAAAAMABAAAADAAcEBwgHDAeMHTAhNCKUJbwzCD8MPxA/KEA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/7d4f6308935a.js b/pr/395/docs/search.index/7d4f6308935a.js new file mode 100644 index 000000000..2157367e8 --- /dev/null +++ b/pr/395/docs/search.index/7d4f6308935a.js @@ -0,0 +1 @@ +rn_("AQEBsIAS8QABdfKaCQAAAQAjgAKwEAmYAAEzNgECAPSUCQAAAQBgBwEAAQIA82oMAAABAAEAE4ACsBAJkgABaXUBAgA7MAAAAQAACwACAEwTBQD9EwUA8wkBbTswAAABAAAMAAMAGhEAAHkSBQC+EwUAdyAAAIqgMAAAE2KwMBE3AAGgcAAAEaWgIAAACZawMBFrAAGwIAe7AdyzhQo7MAAAAQAAFAIEANQBAAD5ASIB3gcAAEUK7wA=") \ No newline at end of file diff --git a/pr/395/docs/search.index/7f07551082cf.js b/pr/395/docs/search.index/7f07551082cf.js new file mode 100644 index 000000000..e5d8c56e9 --- /dev/null +++ b/pr/395/docs/search.index/7f07551082cf.js @@ -0,0 +1 @@ +rn_("AQUBsEASVgABZfJ3EgAAAQABggKgkAAAE6qgkAAAE6syMwEAAqAgAAASNKAgAAAJU2ltOzAAAAEAAAwABgAqBQAAXwUFAL0JAAB1DwEAgRMAAJQTAQDzAAFzAeAQAAABBQD0ERMAAAEAAQABAAEAAPWPDAAAAQABAAEAAQDzgAJna3OAhLCwE2cAARFIAAEGAaBAAAARn2XzEA0AALcDAQABgAOwUBOjAAGgEAAADB2gcAAAEwJnbnJHAQAAhaBgAAATgqBQAAAT0qCgAAATdhUBBDswAAABAAARAAQA1wEBAGwQCABlEQMAUxICAAGFArBwE48AAbAwEu8AAWVzEwADsEAPlQABoGAAABE6YmVzOzAAAAEAABIABAAkBgEAMwcKAAsQAQBdEAMAAQMA9CcQAAABAAQAAQABCwGgQAAAE9Vl8vQTAAABAPOAAmRy8wACZW87MAAAAQAABwACAAUIAwBGCAMAAQIAOzAAAAEAAAkAAQATDAkAAQAA9GEQAAABAAEAAQABAQA7MAAAAQAA3gACAEEGAABtCN0AYwADsBASrgABZWlzOjAAAAEAAAAAAAwAEAAAAGUJnAmdCZ4Jnwk3DDgMbA9tD5QPrhANEw4T44CEsCAHwgABEUAEAQGEsDAMJgABoEAAAAzgoIAAABEMsEAM4QABAAAdAeYBAAABBQD1WBMAAAEAAQBwAAEAE4ACoAAAAAdnYWcBAwD0LRIAAAEAUQEBAPOAAmlvAQEAOzAAAAEAAN4AAgDfBwAANQvdAAEIAbBAE9YAAWX1nhMAAAEAAQABAAEA84ECZ3QBAQGgcAAAEaVpOzAAAAEAAPAAAgDeBwAARQrvAAEFAPN7EAAAAQABAPOAAmFvE4ACoEAAAAxNcnUBgAKgAAAAB8SgEAAACWRkeBMAArAgDQcAAWVy8tYPAAABABOAArCwFBoAAXJ4AQoA9BgTAAABAAEAAQATgAKwEBDCAhNhZQEEAPRdEAAAAQABAAEAAQQAOjAAAAEAAAAAAAcAEAAAAMASwRLPEtASSBNJE1sTXBMBBAD0shAAAAEAAQABAAEDAPNmEgAAAQCaAPOAhAFIAce/AwCPoIAAABJosGAM9wABsEAMdQABsTAT8QABP6kfOzAAAAEAAE0AIwBqAAAAbgAAAEsFAQBUBQAA/QUBAKgHAQCyBwEAugcAAL4HAQCWCQAAqQkHAL8JAQAlDAAAOgwBAEEMAQBSDAcAdQwBAI0MAQDXDAEAow8BABAQAQCHEAEAkRABAKkQAADLEAIA2xABAO0QBAAQEQAAXxEDAJwRAQBFEgAAnhIBAKISAQDLEgAAFhQBAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/7f8c3fb9fdb6.js b/pr/395/docs/search.index/7f8c3fb9fdb6.js new file mode 100644 index 000000000..8b33a9bf3 --- /dev/null +++ b/pr/395/docs/search.index/7f8c3fb9fdb6.js @@ -0,0 +1 @@ +rn_("AQAA9FoJAAABAPoJ3AAXAAAAyaBAAAAQw7BgE9oAAaAQAAAMmLBQE3sAAbAwESQAAaAAAAAMW7EgE/kAAbBAEMEB/ABOsAk6MAAAAQAAAAAAEQAQAAAAbwlwCYQJhQkoDCkMKgwzDDQM6Aw2El8SYBJhEggTCROKE4sT") \ No newline at end of file diff --git a/pr/395/docs/search.index/7f983daa5a41.js b/pr/395/docs/search.index/7f983daa5a41.js new file mode 100644 index 000000000..aaad2fb91 --- /dev/null +++ b/pr/395/docs/search.index/7f983daa5a41.js @@ -0,0 +1 @@ +rn_("AYACoFAAABD0oFAAABIqYXUBgwKxABQmAAGw0BQYAAFrbQEEAaCgAAAUMXfyIRQAAAEAAQsA85YTAAABAAEAI4ACoIAAABO8ZW9jAwOgYAAAE1VlbnABQAwAACOAAqBAAAAQhm1wAQoA83kTAAABAG0AE4ACohAAABQxbG/3AAAAhaEAAAAT6QSBAzswAAABAAA7AAQAMwYFALkHAAAJCDMAMhEAAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/7fee0594063e.js b/pr/395/docs/search.index/7fee0594063e.js new file mode 100644 index 000000000..0343aa077 --- /dev/null +++ b/pr/395/docs/search.index/7fee0594063e.js @@ -0,0 +1 @@ +rn_("AQEA99AQAAABAAEAogIBAH4ABQABAQKgQAAAE96gMAAAENZlaPVcCQAALgDtAgEAEAUBAgD0fxEAAAEAAQABABOBArAgEYMAAWFz84ACbm8BAgD0ZRAAAAEAAQABABMAAqDQAAAT42Nz8t0PAAABAPOAA2Vpbw==") \ No newline at end of file diff --git a/pr/395/docs/search.index/alias/a762150c532c.js b/pr/395/docs/search.index/alias/a762150c532c.js new file mode 100644 index 000000000..f829e4636 --- /dev/null +++ b/pr/395/docs/search.index/alias/a762150c532c.js @@ -0,0 +1 @@ +rd_("") \ No newline at end of file diff --git a/pr/395/docs/search.index/crateNames/8a381fcf2bd4.js b/pr/395/docs/search.index/crateNames/8a381fcf2bd4.js new file mode 100644 index 000000000..59242fbfb --- /dev/null +++ b/pr/395/docs/search.index/crateNames/8a381fcf2bd4.js @@ -0,0 +1 @@ +rd_("ebenchdbulkkcertificatemdata_transferjiroh_quinnA`iroh_quinn_protoniroh_quinn_udpdperfjquinn_perfAaset_up_connection") \ No newline at end of file diff --git a/pr/395/docs/search.index/desc/377b54f4891b.js b/pr/395/docs/search.index/desc/377b54f4891b.js new file mode 100644 index 000000000..65a616da4 --- /dev/null +++ b/pr/395/docs/search.index/desc/377b54f4891b.js @@ -0,0 +1 @@ +rd_("AmData flows in both directions0CdThe CE codepoint, signalling that congestion was \xe2\x80\xa600AjContains the success valueBeThe PathId of this path.AoGet the identity of this stream00AfWhich path is now openAjWhich path has been closedB`Which path had its state droppedBaPath for which the error occurredAmPath which has changed statusCfPath over which the observed address was reported, \xe2\x80\xa6AlWhich stream is now readableAlWhich stream is now writableAnWhich stream has been finishedAmWhich stream has been stoppedAnType of the Long header packetBcExperimental! Use at your own risk.0ClWhether a stream communicates data in both directions or \xe2\x80\xa60AhContains the error valueAoThe largest representable valueAlThe maximum path ID allowed.10ClConnects to a QUIC perf server and maintains a specified \xe2\x80\xa6BmData flows only from the stream\xe2\x80\x99s initiator0AnWhich directions data flows in0CaDirectionality for which streams have been openedCdDirectionality for which streams are newly availableCjExplicit congestion notification bits to set on the packet0CeThe Explicit Congestion Notification bits for the \xe2\x80\xa61B`The current best RTT estimation.BeThe amount of I/O operations executed0BcLength of QUIC packet being decodedAlLength of the packet payload0BmThe number of bytes the associated buffer hasBbSet a custom TokenLog0CaMinimum RTT registered so far for this estimator.ClConstruct with an approximate maximum memory usage and a \xe2\x80\xa6CjConstruct an endpoint with arbitrary configuration and \xe2\x80\xa6DiCreates a new QlogConfig that writes a qlog trace to the \xe2\x80\xa6CiCreates a new qlog factory that writes files into the \xe2\x80\xa6D`Create a default config with a particular reset_keyCmCreate a default config with a particular handshake token \xe2\x80\xa6CkCreate a default config with a particular cryptographic \xe2\x80\xa6AmConstruct cid from byte arrayoConstruct emptyAeCreate a new StreamIdDjConstruct a state using the given config and current time \xe2\x80\xa600:8AeCreate a new endpointDcCreate a new instance of FixedLengthConnectionIdParserBdCreate a generator with a random key:987DgBegin decoding a QUIC packet from bytes, returning any \xe2\x80\xa67BkConstruct an error with a code and a reasonCgInitialize Random CID generator with a fixed CID length87666CkGet SystemTime::now() or the mocked equivalentAhLook up the current time01ChCurrent best estimate of this connection\xe2\x80\x99s latency \xe2\x80\xa6000ChThe ECT(0) codepoint, indicating that an endpoint is \xe2\x80\xa600ChThe ECT(1) codepoint, indicating that an endpoint is \xe2\x80\xa600CaA complete set of keys for a certain packet space0CbA Long packet header, as used during the handshakeChNot attributed to any particular FrameType.AcAn open (Multi)PathAb(Multi)Path eventsB`An error occurred during readingCeWhether an endpoint was the initiator of a connection0nThe 0 path id.0CaThe address observed by the remote over this pathCjThe source address of the datagram(s) contained in the \xe2\x80\xa6mType of errorBkCurrent congestion window of the connection0gPayloadAoReturns the argument unchanged.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000BaCalls U::from(self).00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dNextCeMethod for opening a sealed message dataB`Open a single stream if possible1CgReturns the Path structure of an open pathBhPing the remote endpoint over this path.AhPing the remote endpointCkCheck whether the timer has expired, and register to be \xe2\x80\xa6BaReturns application-facing eventsBgRead data contiguously from the stream.AoRead from the given recv streamCcDecode TransportParameters from bufferBiReceive an unreliable, unordered datagramBlMethod for sealing message data0CiQueue an unreliable, unordered datagram for immediate \xe2\x80\xa6CbSends a Transmit on the given socket.CmNumber of address validation tokens sent to a client when \xe2\x80\xa60BmThe side of the connection (client or server)ChLook up whether we\xe2\x80\x99re the client or server of this \xe2\x80\xa6AlMethod for signing a message0CdAmount of data written to the caller-supplied buffer0hSpin bitAcStop accepting data0BoStop accepting data on the given receive streamCiTry to find and take a token that was stored with the \xe2\x80\xa60BgA chunk of data from the receive stream0CiThe RFC8312 congestion controller, as widely used for TCP0Borustls reports protocol errors using this type.0DbErrors encountered while decoding TransportParametersCdErrors that the nat traversal state might encounter.BeEvents of interest to the applicationCiEvent emitted when the client receives ADD_ADDRESS or \xe2\x80\xa6CjAttributed to a specific FrameType, never \xe2\x80\xa6A`Any other error.0CcThe peer abandoned transmitting data on this streamCnThe peer is unable to continue processing this connection, \xe2\x80\xa6CdThe peer abandoned transmitting data on this stream.1AeA Retry packet headerCdA short packet header, as used during the data phaseBiConstruct a fresh Controller0AiThe contents of the chunkCiThe total amount of bytes which have been transferred \xe2\x80\xa6BjThe amount of bytes which had been written210BnUnderlying error describing reason for failureBaClose the connection immediately.CnClose all of this endpoint\xe2\x80\x99s connections immediately and \xe2\x80\xa6A`Closes this pathAnClose a connection immediatelyAgThe error that occurredBcFrame type that triggered the errorCcDistinguishes streams of the same initiator and \xe2\x80\xa60CjReturns whether the codepoint is a CE, signalling that \xe2\x80\xa60AgKey for encrypting data0BgParse a connection id from given bufferAhReturns all known paths.BlUpdate the timer to expire at iBbClose the send stream immediately.BeAbandon transmitting data on a streamCnRespond with a retry packet, requiring the client to retry \xe2\x80\xa60CiDrive future to completion in the backgroundCeShow connection stats the at the end of the benchmarkAmReturns connection statisticsBiReturns relevant stats from this Endpoint1BcTitle to record in the qlog capture0CmWrite a buffer into this stream, returning how many bytes \xe2\x80\xa6AmSend data on the given streamCcEncode TransportParameters into bufferC`Future produced by Endpoint::acceptClPaths marked as backup will only be used if there are no \xe2\x80\xa6CeChunks returned from RecvStream::read().AmThe initiator of a connection0AdRun as a perf clientAfA path has been closedCgAttempted to initiate NAT traversal on a closed, or \xe2\x80\xa6CcFuture produced by Connection::open_biAjA new path has been openedChOne or more new streams has been opened and might be \xe2\x80\xa6ChId representing different paths when using multipath \xe2\x80\xa60AbCoding result typeAlThe acceptor of a connection0AdRun as a perf servermStream eventsAiAn integer less than 2^620CfGet the next incoming connection attempt from a clientClAttempt to accept this incoming connection (an error may \xe2\x80\xa6CcAccept a remotely initiated stream of a certain \xe2\x80\xa61C`The amount of full chunks which had been written0BlAllows to configure the desired cipher suiteCiHelper to construct an endpoint for use with outgoing \xe2\x80\xa6CcWait for the connection to be closed for any reasonAfCoding related traits.BnAccess the configuration used by this endpointChTraits and implementations for the QUIC cryptography \xe2\x80\xa6BjCreate QUIC error code from TLS alert codeBoTLS configuration used for incoming connections210BkAn underlying crypto (e.g. TLS) layer errorCgDecode a plain header from given buffer, with given \xe2\x80\xa6DiDecode a Self from the provided buffer, if the buffer is \xe2\x80\xa6ChThe destination IP address which was encoded in this \xe2\x80\xa6D`Append the encoding of self to the provided buffer.CmNotify the peer that no more data will ever be written to \xe2\x80\xa6CnFinish a send stream, signalling that no more data will be \xe2\x80\xa6B`Process an incoming UDP datagramAfHeader protection keys0ClIgnore this incoming connection attempt, not sending any \xe2\x80\xa60C`Potentially store a token for later one-time use0CfPerform NSS-compatible TLS key logging to the file \xe2\x80\xa6AhThe offset in the stream0B`Packet deliveries were confirmed0AfPacket protection keys0lRandom valueBcHuman-readable reason for the close000BhHuman-readable explanation of the reasonBeReason that the connection was closedAjSwitch to a new UDP socketBgReject this incoming connection attempt0AgKey for decrypting dataAmThe remote side of this tuple1AmTLS interface based on rustls0CnHelper to construct an endpoint for use with both incoming \xe2\x80\xa6BkOptional source IP address for the datagram00CgThe current local PathStatus of this path.B`The new status set by the remoteCfThe size of a single datagram in the associated bufferCjStatistics about datagrams and bytes received on this pathCgStatistics about UDP datagrams received on a connection10CfStatistics about datagrams and bytes sent on this pathCjStatistics about UDP datagrams transmitted on a connection10AnMethod for verifying a message0CcNumber of ack-eliciting bytes that may be in flight0CaA key for sealing data with AEAD-based algorithms0A`Send would blockCcNo more data is currently available on this stream.CjThe peer is not able to accept additional data, or the \xe2\x80\xa6BfA catch-all error for unlikely errors.0BlA key for signing with HMAC-based algorithms0AhAn Initial packet headerBnA pair of keys for bidirectional communication0BhA simple, standard congestion controller0CdFuture produced by Connection::open_uniCkAbstracts I/O and timer operations for runtime independenceBfA cryptographic session (commonly TLS)0CcThe peer is no longer accepting data on this streamClThe peer is no longer accepting data on this stream, and \xe2\x80\xa6CkThe peer is no longer accepting data on this stream. No \xe2\x80\xa6CgThe peer asked us to stop sending on an outgoing streamAaAccess to streamsCaThe stream is larger than the user-supplied limitCnAttributed to some frame type this implementation does not \xe2\x80\xa6ClIndicates how many bytes and chunks had been transferred \xe2\x80\xa60l0-RTT packetCjReturns the length of a CID for connections created by \xe2\x80\xa60CdProvide the length of dst_cid in short header packetCcThe total number of clients which should be createdAlConnect to a remote endpointAeInitiate a connectionCgDecrypt the packet payload with the given packet numberBcDecrypt the given packet\xe2\x80\x99s header10ClConstructs a value with arbitrary fields, intended to be \xe2\x80\xa6DjConstruct an event that indicating that a Connection will \xe2\x80\xa6BkThe destination connection ID of the packetBkThe destination Connection ID of the packetAiDestination Connection ID00000CgEncrypt the packet payload with the given packet numberBcEncrypt the given packet\xe2\x80\x99s header10Abiroh NAT TraversalCbCheck if this stream has been opened during 0-RTT.0CmRetrieve implementation-specific metrics used to populate \xe2\x80\xa60CnThe maximum UDP payload size guaranteed to be supported by \xe2\x80\xa60BbOne or more packets were just sent0BmInitiate a new outgoing bidirectional stream.CaReturns the path ID of the new path being opened.AdSource Connection ID0000CiCompletes when the peer stops the stream or reads the \xe2\x80\xa6CjCheck if this stream was stopped, get the reason if it wasCcThe total number of streams which should be createdAlProvide control over streamsCeThe length of the AEAD tag appended to packets on \xe2\x80\xa60CdUpgrade the handle to a full ConnectionAkSet the QUIC version to use0lQUIC versionB`The version that was unsupported11CeFuture produced by Connection::accept_biAfAn unreliable datagramBdDatagram support is disabled locally0A`A QUIC endpoint.BcThe main entry point to the libraryChA finished stream has been fully acknowledged or stoppedCkAn incoming connection for which the server has not yet \xe2\x80\xa60BoLong packet types with uniform header structureB`The largest encoded value length0Cmthe connection is being closed abruptly in the absence of \xe2\x80\xa60CeFuture returned by Connection::on_closedClFuture produced by crate::Connection::open_pathCmA currently open stream likely has data or errors waiting \xe2\x80\xa6CkMetadata for a single buffer filled with bytes received \xe2\x80\xa6BkResponse generated directly by the endpointCfIdentifier for a stream within a particular connection0CnCommunication with the peer has lapsed for longer than the \xe2\x80\xa60ChResponsible for limiting clients\xe2\x80\x99 ability to reuse \xe2\x80\xa60ClThe datagram is larger than the connection can currently \xe2\x80\xa60AbAn outgoing packet00CmStatistics about UDP datagrams transmitted or received on \xe2\x80\xa60CnA formerly write-blocked stream might be ready for a write \xe2\x80\xa6AkCongestion algorithm to useAhContents of the datagramBoMark the read data as consumed from the stream.C`Statistics about frames received on a connection0CcStatistics about frames transmitted on a connection0DhConstructs cid by reading len bytes from a Buf0DkCreates a new qlog factory that writes files into QLOGDIR, \xe2\x80\xa60BfCreate a generator with a specific keyBjConstruct a VarInt infallibly0BeSucceeds iff x < 2^620AeWhat type we receivedAoWhat handshake type we received10CbWhether 0-RTT is/was possible during the handshakeCmSpecifies the time to wait after completing MTU discovery \xe2\x80\xa60CcReturns Self for use in down-casting to extract \xe2\x80\xa60EhReturns true if the Connection associated with this handle \xe2\x80\xa6CmDuration after an address validation token was issued for \xe2\x80\xa60CeThe local IP address which was used when the peer \xe2\x80\xa6000AmThe local side of this tuple.ClCompute the maximum size of datagrams that may be passed \xe2\x80\xa6BnInitiate a new outgoing unidirectional stream.BcGet the priority of the send streamAlGet the priority of a streamCfThe negotiated application protocol, if ALPN is in use0Aeqlog output directoryBbOptional response to transmit backCiOptional seed to be used internally for random number \xe2\x80\xa60AlSlow start threshold (bytes)0BeSucceeds iff x < 2^6200000DaSends a Transmit on the given socket without any \xe2\x80\xa6DkQuickly determine whether cid could have been generated by \xe2\x80\xa600BoAll remaining state for a path has been removedCfFuture produced by Connection::accept_uniCdPaths marked with as available will be used when \xe2\x80\xa6CnAt least one new stream of a certain directionality may be \xe2\x80\xa6ClConfiguration for the Bbr congestion controller0BkFuture produced by Path::closeBkThe connection was successfully establishedAoAPI to control datagram trafficAlDecoding of QUIC primitives.BgInfallible encoding of QUIC primitives.CnIdentifies a network path by the combination of remote and \xe2\x80\xa6A`Handshake packetBcKeys used to protect packet headers0CiCatch-all error for problems while decoding transport \xe2\x80\xa6CeNat traversal attempt failed due to a multipath errorBdKeys used to protect packet payloads0BmErrors that might trigger a path being closedAnApplication events about pathsBiStatistics related to a transmission path0AeA read error occurredBmErrors that arise from reading from a stream.C`Errors triggered when reading from a recv streamCiAn object for asynchronously writing to an associated \xe2\x80\xa6BmAccept the next incoming bidirectional streamBbDuplicate the controller\xe2\x80\x99s state0BdThe amount of UDP datagrams observedAaControl datagrams1BeCreate new object from the given bits00BoWhich side of a connection initiated the stream0CmConvert into a 0-RTT or 0.5-RTT connection at the cost of \xe2\x80\xa6BoShorthand for self == Side::Client0B`Whether the connection is closedBoShorthand for self == Side::Server0CbWhether it is legal to respond with a retry packet0CdWhether to allow clients to migrate to new addresses0CdConstruct a timer that will expire at iCjWait for the connection to be closed without keeping a \xe2\x80\xa6CjOpens an additional path if the multipath extension is \xe2\x80\xa6A`Opens a new pathBmPing the remote endpoint over a specific pathCiAttempts to read from the stream into the provided bufferCeReceive UDP datagrams, or register to be woken if \xe2\x80\xa6CkSend a UDP datagram, or register to be woken if sending \xe2\x80\xa6ClPrivate key used to send authenticated connection resets \xe2\x80\xa60BmGenerate the integrity tag for a retry packet0BgA stable identifier for this connectionCePrivate key used to authenticate data included in \xe2\x80\xa60BlThe position of a token in the packet bufferCgTransport configuration to use for incoming connections0CjWait for all connections on the endpoint to be cleanly \xe2\x80\xa6BoWrite a buffer into this stream in its entiretyCiAbstract implementation of an async timer for runtime \xe2\x80\xa6BoNumber of UDP packets to send/receive at a timeCkError indicating that a path has not been opened or has \xe2\x80\xa6BnThe path is already closed or was never opened1BeIn-progress connection attempt futureAbA QUIC connection.CeProtocol state and logic for a single QUIC connectionCeCommon interface for different congestion controllers0CkNumber of frames transmitted or received of each frame type0BoThe connection ID was not recognized by the \xe2\x80\xa60DdThe type used to refer to FrameTypes in closing and \xe2\x80\xa6AnThe QUIC-MULTIPATH path statusBeConfiguration for qlog trace logging.0BnA stream that can only be used to receive dataAaAccess to streamsChErrors that arise while waiting for a stream to be resetDhError for attempting to retry an Incoming which already \xe2\x80\xa60BkA stream that can only be used to send data3BmObject to get current SystemTime0AdA rustls TLS session0CkResponsible for storing validation tokens received from \xe2\x80\xa60AeA borrowed UDP socketBjErrors that arise from writing to a streamBoErrors triggered while writing to a send streamBoAccept the next incoming uni-directional streamCkWhether the implementation is permitted to set the spin \xe2\x80\xa60BmCloses a path by sending a PATH_ABANDON frameCdLogic for controlling the rate at which data is sent0BfWhether to print connection statisticsBnClass of error as encoded in the specificationB`Application-specific reason code10BgError code supplied by the peer See \xe2\x80\xa6AoError code supplied by the peerBcType of frame that caused the close0AiExtract the integer value0BfQUIC transport protocol implementationCnWhether there is no longer any need to keep the connection \xe2\x80\xa6DkDetermine whether this is the last event a Connection will \xe2\x80\xa6CiLook up the local IP address and port used by this socketDgGet the local SocketAddr the underlying socket is bound toBeThe amount of bytes lost on this path0CjPad UDP datagrams carrying application data to current \xe2\x80\xa60AgReturns path statistics0CjThe final path stats, they are no longer available via \xe2\x80\xa6C`Attempt to write bytes from buf into the stream.AmRead the next segment of dataAoReads the next segment of data.CkRead an exact number of bytes contiguously from the stream.BnSets the PathStatus of this path.BoEpoch qlog event times are recorded relative to0ClError type for attempting to accept an IncomingBjErrors in the configuration of an endpoint0AeGeneric crypto errors0CnConfiguration for the Cubic congestion controller0CdReasons why attempting to finish a stream might failCnMaximum duration of inactivity to accept before timing out \xe2\x80\xa60AnValue exceeds supported bounds0D`Constructs a QlogConfig for individual connections.0AhA Quinn runtime for smolB`Application events about streamsCbAccept this incoming connection using a custom \xe2\x80\xa6CdLargest UDP payload size the path currently supportsClStorage size required for the largest packet that can be \xe2\x80\xa61BiDescription to record in the qlog capture0BjThe socket this datagram should be sent to00BkStarting guess for maximum UDP payload sizeCkThe initial value to be used as the maximum UDP payload \xe2\x80\xa60ChUDP payload size that the network must be capable of \xe2\x80\xa6BjThe RTT used before an RTT sample is taken0BfThe initial round-trip-time (in msecs)CeThe amount of concurrent streams which should be usedDdPackets are acked in batches, all with the same now \xe2\x80\xa60AdPacing rate (bits/s)0DiA broadcast receiver of PathEvents for all paths in this \xe2\x80\xa6DfGets the local PathStatus for a known PathIdAnRead the next segments of dataCkConvenience method to read all remaining data into a bufferAlProvide control over streamsBoThe sample size used for this key\xe2\x80\x99s algorithm01CiMaximum number of bytes to transmit to a peer without \xe2\x80\xa600BoThe server name specified by the client, if any0BmObject to get current SystemTime0BdSet a custom TokenStore0CaNumber of bytes to transmit from client to serverCnSpecifies the upper bound to the max UDP payload size that \xe2\x80\xa60ChReturns a weak reference to the inner connection struct.DgCreate a server config with the given crypto::ServerConfig0CeSets a prefix to the filename of the generated files.0DbWrite a single Bytes into this stream in its entiretyBbAn ADD_ADDRESS frame was received.BfConfiguration for outgoing connectionsCaClient-side configuration for the crypto protocol10CgThe stream has already been stopped, finished, or resetBmThe stream has already been finished or resetCmError indicating that a stream has not been opened or has \xe2\x80\xa6CjThe stream has not been opened or was already stopped, \xe2\x80\xa6CfThe stream has not been opened or has already been \xe2\x80\xa6CmThe stream has not been opened or was already finished or \xe2\x80\xa63CgErrors in the parameters being used to create a new \xe2\x80\xa60BkProtocol-level identifier for a connection.0CkWe couldn\xe2\x80\x99t decrypt a message. This is invariably fatal.0BjExplicit congestion notification codepoint00CnWe couldn\xe2\x80\x99t encrypt a message because it was larger than \xe2\x80\xa60BhParameters that are semantically invalidCaThis is the last path, which can not be abandonedAmIroh\xe2\x80\x99s nat traversal eventsDhNull implementation of TokenLog, which never accepts tokens0ClReceived an observation of our external address from the \xe2\x80\xa6CiFuture produced by Connection::read_datagramBiThe remote changed the status of the pathBlRTT estimation for a particular network pathCnFuture produced by Connection::send_datagram_waitBiParameters governing incoming connectionsCaServer-side configuration for the crypto protocol10CmErrors that arise while monitoring for a send stream stop \xe2\x80\xa6AiA Quinn runtime for TokioC`Returns the lifetime of generated Connection IDs0BlIf the connection is closed, the reason why.CjConnect to a remote endpoint using a custom configuration.AlConservative estimate of RTTBnGet the 0-RTT keys if available (clients only)0AgWhich types we expectedB`Which handshake type we expected10AcGenerates a new CID0ClThe number of segments to read when GRO is enabled. Used \xe2\x80\xa6DeProcess ConnectionEvents generated by the associated \xe2\x80\xa6EaProcess EndpointEvents emitted from related ConnectionsCgCreate the initial set of keys given the client\xe2\x80\x99s \xe2\x80\xa6000BgThe amount of packets lost on this path0DcMaximum number of Incoming to allow to exist at a time0CjWhether datagrams might get fragmented into multiple parts0CmWhether transmitted datagrams might get fragmented by the \xe2\x80\xa6CkReturns the path\xe2\x80\x99s network path represented as a 4-tuple.CkThe original destination CID when initiating the connectionCiThe original destination connection ID sent by the clientDhReturns the next time at which handle_timeout should be \xe2\x80\xa6D`Configures qlog capturing by setting a QlogFactory.0CkThe segment size if this transmission contains multiple \xe2\x80\xa600ChThe number of streams that may have unacknowledged data.CbSet the lifetime of CIDs created by this generator0BcSet the priority of the send streamAlSet the priority of a streamCnInitialize a QUIC-compatible TLS client configuration with \xe2\x80\xa6000DgWrite a slice of Bytes into this stream, returning how \xe2\x80\xa6AmSend data on the given streamCkWe received a fatal alert. This means the peer is unhappy.0BhBloom filter-based TokenLog0CmThe connection could not be created because not enough of \xe2\x80\xa6000CaEvent resulting from processing a single datagramBlEvents sent from a Connection to an EndpointAoStatistics on Endpoint activityBnThe stream finished before all bytes were readBlAuthentication data for (rustls) TLS session0Ccreceived an invalid Retry Token in a client Initial0AlThe packet header is invalidBkThe local application closed the connectionAgPath was closed locally1DaThe datagram may result in starting a new ConnectionD`Configuration for the NewReno congestion controller0BjDecodes a QUIC packet\xe2\x80\x99s invariant headerCgErrors triggered when opening a recv stream for readingCaDefault implementation of TimeSource0CgError indicating that the provided buffer was too smallChFor clients, if the peer accepted the 0-RTT data packetsAbAck Frequency modeAfExports for benchmarksBoSupply a custom connection ID generator factory0DgCreate a UdpSender that can register a single task for \xe2\x80\xa6CaNumber of bytes to transmit from server to clientAmGet the Incoming0CnThe max_ack_delay we will request the peer to use0CgDisable packet encryption/decryption (for debugging \xe2\x80\xa6CkThe known MTU for the current network path has been updated0BbCryptographic identity of the peerBgGet the peer\xe2\x80\x99s identity, if availableDhFor the rustls TlsSession, the Any type is \xe2\x80\xa610CnAttempts to read from the stream into the provided buffer, \xe2\x80\xa6AkReturns packets to transmitDjConfigures qlog capturing through the QLOGDIR environment \xe2\x80\xa60AoReceive an application datagramDfTransmit data as an unreliable, unordered application \xe2\x80\xa6CmWhether to implement fair queuing for send streams having \xe2\x80\xa60BfLength of sign\xe2\x80\x99s output0BnStart a client session with this configurationBnStart a server session with this configuration10BdA REMOVE_ADDRESS frame was received.CgAbstract implementation of a UDP socket for runtime \xe2\x80\xa6BgErrors triggered when abandoning a pathAgThe connection was lost00000ChGlobal configuration for the endpoint, affecting all \xe2\x80\xa60Cithe endpoint encountered an internal error and cannot \xe2\x80\xa60FbBasic adapter to let Incoming be await-ed like a ConnectingBhEncounter a frame ID that was not valid.CeThe peer sent us a TLS message with invalid contents.0Amno viable network path exists0DhNull implementation of TokenStore, which does not store \xe2\x80\xa60CiThe peer deviated from the standard TLS protocol. The \xe2\x80\xa60BmErrors that arise from reading from a stream.C`Errors from RecvStream::read_to_endCaIndicates whether a frame needs to be transmittedCmThe peer violated the QUIC specification as understood by \xe2\x80\xa60CiTransport-level errors occur when a peer violates the \xe2\x80\xa6C`Tokio-compatible UDP socket with some useful \xe2\x80\xa6AfDerive AEAD using hkdf0BnCreate a client endpoint and client connectionAgGet a session referenceDiReturns a QlogConfig for a connection, if logging should \xe2\x80\xa60AiProcess timer expirationsBjParameters negotiated during the handshake0CfGet data negotiated during the handshake, if available0AiInitial congestion windowCiDefault limit on the amount of outstanding data in bytes.001000BnConverts this stream into an unordered stream.CjUniform interface to send and receive UDP packets with \xe2\x80\xa6DdReturns true until the connection is fully established.CeWhether the connection is in the process of being \xe2\x80\xa61BfVerify the integrity of a retry packet0CnSpecifies the minimum MTU change to stop the MTU discovery \xe2\x80\xa60BdCompute keys for the next key update0BkConfigures qlog capturing into a directory.0AlRead bytes of handshake data0BeWhether to use the unordered read APIClMaximum number of bytes the peer may transmit across all \xe2\x80\xa600CkCompletes when the stream has been reset by the peer or \xe2\x80\xa60CiCheck whether this stream has been reset by the peer, \xe2\x80\xa6AjThe peer\xe2\x80\x99s UDP addressesAhThe peer\xe2\x80\x99s UDP address0BgThe peer\xe2\x80\x99s UDP address for this path.1CeSaturating integer addition. Computes self + rhs, \xe2\x80\xa6000ChSaturating integer subtraction. Computes self - rhs, \xe2\x80\xa60CkMaximum reordering in time space before time based loss \xe2\x80\xa60ChReason given by the transport for closing the connection0BfReasons why a connection might be lost0CiThe datagram is redirected to its ConnectionBlEvents sent from an Endpoint to a ConnectionAeConnection statistics0BiStatistics for the currently open streamsAcPlain packet headerBkEnables writing qlog traces to a directory.0CfError for when a validation token may have been reused0C`Parameters governing the core QUIC state machine0CbThe peer doesn\xe2\x80\x99t implement any supported version0CdFuture that completes when a connection is fully \xe2\x80\xa6CbThis was a 0-RTT stream and the server rejected it000CjAutomatically select an appropriate runtime from those \xe2\x80\xa6CkWhether to accept QUIC packets containing any value for \xe2\x80\xa60CdMaximum number of incoming packets that may fail \xe2\x80\xa60CnThe interface index of the interface on which the datagram \xe2\x80\xa6AjSwitch to a new UDP socketCiCreates a server endpoint which runs on the given runtimeD`Sets the PathStatus for a known PathIdCfSee proto::TransportConfig::send_window()BoSee TransportConfig::send_window()BmReturns whether a frame should be transmittedD`Convert t into the socket type used by this runtimeCdWrites handshake bytes into the given buffer and \xe2\x80\xa60ClStream produced by Path::observed_external_addrCiReason given by an application for closing the connection0CbThe peer\xe2\x80\x99s QUIC stack aborted the connection \xe2\x80\xa60DjInternal identifier for a Connection currently associated \xe2\x80\xa6CdOne or more application datagrams have been receivedCaThe endpoint can no longer create new connections0Cnreceived a STREAM frame or a RESET_STREAM frame containing \xe2\x80\xa60ClSpecific failure cases from keys_match or a \xe2\x80\xa60A`key update error0CaCurrent limits do not allow us to open more pathsClThe peer doesn\xe2\x80\x99t support a protocol version/feature we \xe2\x80\xa60BjA QUIC-compatible TLS client configuration0BjA QUIC-compatible TLS server configuration0EfTokenStore implementation that stores up to N tokens per \xe2\x80\xa60CgAn endpoint (local or remote) tried to add too many \xe2\x80\xa6CaPath could not be validated and will be abandonedBcA Version Negotiation packet headerCiRecord that the token was used and, ideally, return a \xe2\x80\xa60CjCurrent state of the congestion control algorithm, for \xe2\x80\xa6CcCurrent state of this connection\xe2\x80\x99s congestion \xe2\x80\xa6BaUpdate traffic keys spontaneously0BoLow-level protocol logic for the QUIC protoocolCnThe maximum amount of segments which can be transmitted if \xe2\x80\xa6CnMaximum duration of inactivity to accept before timing out \xe2\x80\xa60CeGet the number of connections that are currently openBmNumber of connections that are currently openCiOpens a new path if no path exists yet for the remote \xe2\x80\xa6CmOpens a new path only if no path on the same network path \xe2\x80\xa6CiMaximum reordering in packet number space before FACK \xe2\x80\xa60CfGet the size of the socket receive bufferAlReceive buffer size in bytesCcGet the size of the socket send bufferAiSend buffer size in bytesBiSet a custom TransportConfig000ChConfiguration for sending and handling validation tokens0CnCreate a server config with the given certificate chain to \xe2\x80\xa60DdWrite a slice of Bytes into this stream in its entiretyCfthe application or application protocol caused the \xe2\x80\xa60AnThe peer closed the connection0B`Constructs controllers on demand0BdCommon congestion controller metrics0AlA pseudo random key for HKDF0BcThe given server name was malformed0AcPacket decode errorBmErrors that can arise when sending a datagram0CcThe peer does not support receiving datagram frames0BcCongestion events on the connection0AiCongestion window (bytes)0ClCompute the maximum size of datagrams that may be passed \xe2\x80\xa6BoBytes available in the outgoing datagram bufferClReplace the server configuration, affecting new incoming \xe2\x80\xa60Cmthe endpoint has reached the confidentiality or integrity \xe2\x80\xa60CkParameters for controlling the peer\xe2\x80\x99s acknowledgement \xe2\x80\xa60DjThe max_fragment_size value supplied in configuration was \xe2\x80\xa60Bmthe server refused to accept a new connection0BjParse connection id in short header packetCjOne or more application datagrams have been sent after \xe2\x80\xa6Ckreceived more data than permitted in advertised data limits0AoThe TLS handshake was confirmedBjThe connection\xe2\x80\x99s handshake data is readyCeAttempted an ordered read following an unordered readAnWe saw an invalid certificate.0BcParameters governing MTU discovery.0BnNot enough addresses to complete the operationCkdetected an error with protocol compliance that was not \xe2\x80\xa60Cjreceived a frame for a stream identifier that exceeded \xe2\x80\xa60Cnreceived a frame for a stream that was not in a state that \xe2\x80\xa60AnError from setting path statusAjTransport-level error code0ChThe local endpoint does not support the QUIC version \xe2\x80\xa6CkError indicating that the specified QUIC version is not \xe2\x80\xa61C`Packet uses a QUIC version that is not supported1CiMaximum quantity of out-of-order crypto layer data to \xe2\x80\xa60C`Create a VarInt without ensuring it\xe2\x80\x99s in range0ChCumulative number of Quic handshakes ignored on this \xe2\x80\xa6CiMinimum interval between outgoing stateless reset packets0CjConstruct with an approximate maximum memory usage and \xe2\x80\xa60ChCumulative number of Quic handshakes refused on this \xe2\x80\xa6AnReturns the remote path statusDfTransmit data as an unreliable, unordered application \xe2\x80\xa6CiSee proto::TransportConfig::receive_window()CbSee TransportConfig::receive_window()B`Override supported QUIC versions0CiOverride the instant relative to which all events are \xe2\x80\xa60BkNo remote CIDs available to open a new pathCkTransport parameters used to negotiate connection-level \xe2\x80\xa6CgA stream that can be used to receive data out-of-order.CmThe certificate verifier doesn\xe2\x80\x99t support the given type \xe2\x80\xa60CfThe operation is not allowed for this endpoint\xe2\x80\x99s \xe2\x80\xa6CiCumulative number of Quic handshakes accepted by this \xe2\x80\xa6CjSpecifies the amount of time that MTU discovery should \xe2\x80\xa60CiIf the 0-RTT-encrypted data has been accepted by the peer0BgWait for the handshake to be confirmed.CgPeriod of inactivity before sending a keep-alive packet0CiThe amount of PLPMTUD probe packets lost on this path \xe2\x80\xa60BlPackets were deemed lost or marked congested0CgCumulative number of Quic handshakes sent from this \xe2\x80\xa6CnThe number of remotely initiated open streams of a certain \xe2\x80\xa6AhBench fns for SendBufferCiThe amount of PLPMTUD probe packets sent on this path \xe2\x80\xa60Bireceived a frame that was badly formatted0CkThis function doesn\xe2\x80\x99t work until the TLS handshake is \xe2\x80\xa60CkWe received a TLS message that isn\xe2\x80\x99t valid right now. \xe2\x80\xa60CiThe remote SocketAddr supplied was malformedCkThe remote address for the path is not supported by the \xe2\x80\xa61ChThe initial cipher suite (AES-128-GCM-SHA256) is not \xe2\x80\xa60BdPaths can only be opened client-sideDmError returned when constructing a VarInt from a value >= \xe2\x80\xa60CeA handle to some connection internals, use with care.DkSpecifies the ACK frequency config (see AckFrequencyConfig \xe2\x80\xa60CiThe number of times a black hole was detected in the path0CgMaximum number of received bytes to buffer for each \xe2\x80\xa60AeIs multipath enabled?ClMaximum number of datagrams that might be described by a \xe2\x80\xa60CkMaximum UDP payload size accepted from peers (excluding \xe2\x80\xa60AmMax UDP payload size in bytesDkSpecifies the MTU discovery config (see MtuDiscoveryConfig \xe2\x80\xa60AmReturn endpoint-facing eventsCkThe preferred IPv4 address that will be communicated to \xe2\x80\xa60CkThe preferred IPv6 address that will be communicated to \xe2\x80\xa60ChThe reordering threshold we will request the peer to use0ClDuration after a retry token was issued for which it\xe2\x80\x99s \xe2\x80\xa60C`Sets the keep_alive_interval for a specific pathDfResize the receive buffer of socket to bytesDcResize the send buffer of socket to bytesBfThe peer\xe2\x80\x99s QUIC transport parametersBdQUIC connection transport parameters1CaGenerates connection IDs for incoming connections0CdAn incoming connection did not support any known \xe2\x80\xa60BjNo default client configuration was set up0BiPath abandoned due to unstable interfaces0ClConfiguration for sending and handling validation tokens \xe2\x80\xa60CmMaximum number of packets that may be sent using a single \xe2\x80\xa60CiCounter for the number of bytes currently used in the \xe2\x80\xa6CgReturns whether we think the other address probably \xe2\x80\xa6ChHandle a change in the local address, i.e. an active \xe2\x80\xa6CkReduction in congestion window when a new loss event is \xe2\x80\xa60DdMaximum number of datagrams that a Transmit may encode.0DjA broadcast receiver of iroh_hp::Events for updates about \xe2\x80\xa6CjGet the address observed by the remote over the given pathCiMaximum number of bytes the peer may transmit without \xe2\x80\xa600Chreceived more data in CRYPTO frames than can be buffered0B`The extension was not negotiatedCbWe failed to figure out what time it currently is.0CbWe failed to acquire random bytes from the system.0CnError indicating that this operation requires multipath to \xe2\x80\xa6BnThe extension was not negotiated with the peerCnError when the multipath extension was not negotiated, but \xe2\x80\xa6BnHeader of an Initial packet, before decryptionCgDerive keying material from this connection\xe2\x80\x99s TLS \xe2\x80\xa6EdFill output with output.len() bytes of keying material \xe2\x80\xa60BnPathological case: many segments, get from endCfGet segments in the old way, using a loop of get callsClCurrent number of remotely initiated streams that may be \xe2\x80\xa6ClTrack changed on our external address as reported by the \xe2\x80\xa6ClTrack changes on our external address as reported by the \xe2\x80\xa6CmCreate a client configuration that trusts specified trust \xe2\x80\xa60BkThe peer didn\xe2\x80\x99t give us any certificates.0BkThe peer sent an oversized record/fragment.0CkThe ack-eliciting threshold we will request the peer to use0BiWhether there are any pending retransmitsCdWhether the Multipath for QUIC extension is enabled.C`Sets the keep_alive_interval for a specific pathBoSet a custom ValidationTokenConfig0BmPath abandoned at the application\xe2\x80\x99s request0CjGet the current value of max_udp_payload_size0ChConfigure how to populate the destination CID of the \xe2\x80\xa60CjConstruct an endpoint with arbitrary configuration and \xe2\x80\xa6CfWhether the socket address that is initiating this \xe2\x80\xa60CmModify the number of open paths allowed when multipath is \xe2\x80\xa6Cmthe number of connection IDs provided by the peer exceeds \xe2\x80\xa60CbError returned by Session::export_keying_material.0CiA provided certificate revocation list (CRL) was invalid.0CmPath abandoned due to no available connection IDs for the \xe2\x80\xa60Clreceived transport parameters that were badly formatted, \xe2\x80\xa60CiRegisters one address at which this endpoint might be \xe2\x80\xa6ClAdd addresses the local endpoint considers are reachable \xe2\x80\xa6ClMaximum number of outgoing application datagram bytes to \xe2\x80\xa60CiSet the client configuration used by connectBmSets the max_idle_timeout for a specific pathBfThe QUIC protocol version implemented.BoBytes available in the outgoing datagram bufferDhMaximum number of received bytes to buffer for all Incoming0DaVariant of max_concurrent_bidi_streams affecting \xe2\x80\xa60ClModify the number of remotely initiated streams that may \xe2\x80\xa6CnCreate a client configuration that trusts the platform\xe2\x80\x99s \xe2\x80\xa60CkGenerates 8-byte connection IDs that can be efficiently \xe2\x80\xa6CkAn error occurred while handling Encrypted Client Hello \xe2\x80\xa60CkPath abandoned due to resource limitations in the transport0CiGenerates purely random connection IDs of a specified \xe2\x80\xa6ChWhether to use \xe2\x80\x9cGeneric Segmentation Offload\xe2\x80\x9d to \xe2\x80\xa60CmMaximum number of incoming bidirectional streams that may \xe2\x80\xa60BhUpdates this tuple\xe2\x80\x99s local address iffClMaximum number of incoming application datagram bytes to \xe2\x80\xa60BcInitiates a new nat traversal round0BdPackets were incorrectly deemed lost0CnRemoves one or more addresses from the set of addresses at \xe2\x80\xa6CgRemoves an address the endpoing no longer considers \xe2\x80\xa6C`Sets the keep_alive_interval for a specific pathDeA ConnectionIdParser implementation that assumes the \xe2\x80\xa6CjWe received a TLS handshake message that isn\xe2\x80\x99t valid \xe2\x80\xa60CiHow to construct new congestion::Controllers0BlSets a default per-path maximum idle timeout0BoThe key exchange group negotiated with the peer0CbWhether to send observed address reports to peers.0CiModify the number of remotely initiated bidirectional \xe2\x80\xa6BiEnables the Multipath Extension for QUIC.0CjModify the number of remotely initiated unidirectional \xe2\x80\xa6CeNumber of consecutive PTOs after which network is \xe2\x80\xa60BkSets a default per-path keep alive interval0CjWhether to receive observed address reports from other \xe2\x80\xa60BmGet the current local nat traversal addresses0CkGet the currently advertised nat traversal addresses by \xe2\x80\xa60AcNat traversal draftCkSets the maximum number of nat traversal addresses this \xe2\x80\xa60CjWhen multipath is required and has not been explicitly \xe2\x80\xa6") \ No newline at end of file diff --git a/pr/395/docs/search.index/entry/1282991302e8.js b/pr/395/docs/search.index/entry/1282991302e8.js new file mode 100644 index 000000000..19df9d414 --- /dev/null +++ b/pr/395/docs/search.index/entry/1282991302e8.js @@ -0,0 +1 @@ +rd_("Ah[3952,15,3953,0,119,0,0]Ak[4735,15,4736,4736,120,0,0]Ai[3952,15,3953,0,4138,0,0]Al[4735,15,4736,1934,4139,0,0]Al[4441,15,4442,4442,4140,0,0]Al[4735,15,1840,1840,1593,0,0]Ao[1365,13,1366,1366,3984,3140,0]Ao[3952,13,3953,3710,4777,3140,0]Ao[3952,13,3953,4088,4401,3140,0]Ao[3952,13,3953,4088,3148,3140,0]Ao[3952,13,3953,4088,3335,3140,0]Ao[3952,13,3953,4088,4400,3140,0]Ao[3952,13,3953,4093,3353,3140,0]Ao[3952,13,3953,4093,4172,3140,0]Al[3952,13,3953,0,1603,3140,0]Al[3952,13,3953,0,4933,3140,0]Al[3952,13,3953,0,1324,3140,0]Al[3952,13,3953,0,1586,3140,0]Al[3952,13,3953,0,2434,3140,0]Al[3952,13,3953,0,3145,3140,0]Al[3952,13,3953,0,2018,3140,0]Al[3952,13,3953,0,4124,3140,0]Al[3952,13,3953,0,4495,3140,0]Al[3952,13,3953,0,3997,3140,0]Al[3952,13,3953,0,3987,3140,0]Al[3952,13,3953,0,3128,3140,0]Al[3952,13,3953,0,4492,3140,0]Al[3952,13,3953,0,4680,3140,0]Al[3952,13,3953,0,4130,3140,0]Al[3952,13,3953,0,4133,3140,0]Al[3952,13,3953,0,4138,3140,0]Al[3952,13,3953,0,4835,3140,0]Ak[3952,13,3953,0,485,3140,0]Ak[3952,13,3953,0,119,3140,0]Al[3952,13,3953,0,2422,3140,0]Al[3952,13,1844,0,5079,3140,0]Al[3952,13,1923,0,1341,3140,0]Ao[4735,13,4736,1960,1604,3140,0]Ao[4735,13,4736,1960,4934,3140,0]Ao[4735,13,4736,3164,1325,3140,0]Ao[4735,13,4736,3173,4778,3140,0]Ao[4735,13,4736,1526,1587,3140,0]Ao[4735,13,4736,1526,3329,3140,0]Ao[4735,13,4736,1526,3144,3140,0]Ao[4735,13,4736,1526,4832,3140,0]Ao[4735,13,4736,1526,3304,3140,0]Ao[4735,13,4736,1542,2435,3140,0]Ao[4735,13,4736,1542,3146,3140,0]Ao[4735,13,4736,1075,3149,3140,0]Ao[4735,13,4736,1075,4292,3140,0]Ao[4735,13,4736,1080,2019,3140,0]Ao[4735,13,4736,1080,3354,3140,0]Ao[4735,13,4736,1080,3996,3140,0]Ao[4735,13,4736,2152,4014,3140,0]Ao[4735,13,4736,2152,4402,3140,0]Ao[4735,13,4736,2152,4128,3140,0]Ao[4735,13,4736,3711,4496,3140,0]Ao[4735,13,4736,3711,3141,3140,0]Ao[4735,13,4736,3711,4368,3140,0]Ao[4735,13,4736,3291,3998,3140,0]Ao[4735,13,4736,1842,3988,3140,0]Ao[4735,13,4736,1516,3129,3140,0]Ao[4735,13,4736,1516,4383,3140,0]Ao[4735,13,4736,1516,3321,3140,0]Ao[4735,13,4736,1516,3343,3140,0]Ao[4735,13,4736,1516,4135,3140,0]Ao[4735,13,4736,1516,4494,3140,0]Ao[4735,13,4736,1516,4682,3140,0]Ao[4735,13,4736,2445,4686,3140,0]Ao[4735,13,4736,2445,4131,3140,0]Ao[4735,13,4736,1907,2406,3140,0]Ao[4735,13,4736,1907,4776,3140,0]Ao[4735,13,4736,1934,4134,3140,0]Ao[4735,13,4736,1934,4139,3140,0]Al[4735,13,4736,0,4406,3140,0]Al[4735,13,4736,0,4836,3140,0]An[4735,13,4736,4736,486,3140,0]An[4735,13,4736,4736,120,3140,0]Ao[4735,13,4736,4736,2423,3140,0]Ao[4735,13,4736,4736,3127,3140,0]Ao[4735,13,1840,1840,4298,3140,0]Ao[4735,13,4975,4975,4876,3140,0]Ao[4735,13,4975,4975,1343,3140,0]Ao[4735,13,1845,1845,5080,3140,0]Al[4735,13,1924,0,1342,3140,0]Ao[4441,13,4442,4442,4140,3140,0]Ak[3952,13,3953,1049,478,0,0]Al[3952,13,3953,4088,3333,0,0]Al[3952,13,3953,4088,4877,0,0]Al[3952,13,3953,4093,3339,0,0]Ai[4735,14,3142,0,1580,0,0]Ai[4735,14,3142,0,1570,0,0]Ai[4735,14,3142,0,3107,0,0]Ai[4735,14,3142,0,4275,0,0]Ai[4735,14,3142,0,4159,0,0]Ai[4735,14,3142,0,4152,0,0]Ai[4735,14,4013,0,2416,0,0]Ai[4735,14,4013,0,2437,0,0]Ai[4735,14,4013,0,2401,0,0]Ai[4735,14,4013,0,2007,0,0]Ae[1365,7,1366,0,0,0,0]Ah[4735,14,4507,0,474,0,0]Ai[3952,15,3953,0,3128,0,0]Al[4735,15,4736,1516,3129,0,0]Ag[3952,5,3705,135,0,0,0]Ag[4735,5,3706,135,0,0,0]Al[1050,15,1051,1051,4870,0,0]Ae[3972,5,3973,0,0,0,0]Ah[3952,6,3953,4736,0,0,0]Ae[4735,6,4736,0,0,0,0]Al[4735,15,1840,1840,1593,0,0]Ai[3952,19,3953,0,1603,0,0]Ai[3952,19,3953,0,1586,0,0]Al[4735,19,4736,1960,1604,0,0]Al[4735,19,4736,1526,1587,0,0]Ae[1365,5,1366,0,0,0,0]Ae[1050,5,1834,0,0,0,0]Ae[1050,5,1929,0,0,0,0]Ah[3952,15,3953,0,119,0,0]Ak[4735,15,4736,4736,120,0,0]Ak[3952,13,3953,0,1603,130,0]Ak[3952,13,3953,0,1586,130,0]Ak[3952,13,3953,0,3997,130,0]Ak[3952,13,3953,0,4133,130,0]Aj[3952,13,3953,0,485,130,0]Aj[3952,13,3953,0,119,130,0]Ak[3952,13,3953,0,2422,130,0]An[4735,13,4736,1960,1604,130,0]An[4735,13,4736,3173,4778,130,0]An[4735,13,4736,1526,1587,130,0]An[4735,13,4736,1075,3149,130,0]An[4735,13,4736,1075,4292,130,0]An[4735,13,4736,1080,3354,130,0]An[4735,13,4736,3291,3998,130,0]An[4735,13,4736,2445,4686,130,0]An[4735,13,4736,1907,4776,130,0]An[4735,13,4736,1934,4134,130,0]Am[4735,13,4736,4736,486,130,0]Am[4735,13,4736,4736,120,130,0]An[4735,13,4736,4736,2423,130,0]Ai[3952,13,3953,0,2422,0,0]Al[4735,13,4736,4736,2423,0,0]Ai[4735,14,4013,0,1581,0,0]Ai[4735,14,4013,0,3113,0,0]Ai[3952,14,3953,0,2431,0,0]Al[4735,14,4736,4736,2432,0,0]Al[4441,14,4442,4442,2420,0,0]Al[4441,14,4442,4442,2433,0,0]An[1365,13,1366,1366,127,1331,0]Ao[1365,13,1366,1366,3984,1331,0]Ao[1365,13,1540,1540,4403,1331,0]Ao[4021,13,4022,4022,5021,1331,0]Ao[3952,13,3953,3710,3306,1331,0]Ao[3952,13,3953,3710,3308,1331,0]Ao[3952,13,3953,3710,4936,1331,0]Dd[3952,13,3953,3710,4777,1979,0,\"impl-Display-for-SendDatagramError\"]Db[3952,13,3953,3710,4777,1331,0,\"impl-Debug-for-SendDatagramError\"]Ao[3952,13,3953,2444,2399,1331,0]Ao[3952,13,3953,2444,4265,1331,0]Ao[3952,13,3953,2471,2403,1331,0]Cm[3952,13,3953,2471,3336,1979,0,\"impl-Display-for-RetryError\"]Ck[3952,13,3953,2471,3336,1331,0,\"impl-Debug-for-RetryError\"]Ao[3952,13,3953,2471,4382,1331,0]An[3952,13,3953,1049,478,1331,0]Ao[3952,13,3953,4088,3333,1331,0]Ao[3952,13,3953,4088,4877,1331,0]Co[3952,13,3953,4088,4401,1331,0,\"impl-Debug-for-ReadToEndError\"]Da[3952,13,3953,4088,4401,1979,0,\"impl-Display-for-ReadToEndError\"]Cj[3952,13,3953,4088,3148,1331,0,\"impl-Debug-for-ReadError\"]Cl[3952,13,3953,4088,3148,1979,0,\"impl-Display-for-ReadError\"]Ck[3952,13,3953,4088,3335,1331,0,\"impl-Debug-for-ResetError\"]Cm[3952,13,3953,4088,3335,1979,0,\"impl-Display-for-ResetError\"]Co[3952,13,3953,4088,4400,1331,0,\"impl-Debug-for-ReadExactError\"]Da[3952,13,3953,4088,4400,1979,0,\"impl-Display-for-ReadExactError\"]Ao[3952,13,3953,1552,4173,1331,0]Ao[3952,13,3953,1092,4010,1331,0]Ao[3952,13,3953,4093,3339,1331,0]Ck[3952,13,3953,4093,3353,1331,0,\"impl-Debug-for-WriteError\"]Cm[3952,13,3953,4093,3353,1979,0,\"impl-Display-for-WriteError\"]Co[3952,13,3953,4093,4172,1979,0,\"impl-Display-for-StoppedError\"]Cm[3952,13,3953,4093,4172,1331,0,\"impl-Debug-for-StoppedError\"]Cf[3952,13,3953,0,1603,1979,0,\"impl-Display-for-VarInt\"]Cd[3952,13,3953,0,1603,1331,0,\"impl-Debug-for-VarInt\"]Dd[3952,13,3953,0,4933,1979,0,\"impl-Display-for-VarIntBoundsExceeded\"]Db[3952,13,3953,0,4933,1331,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Al[3952,13,3953,0,1324,1331,0]Cd[3952,13,3953,0,1586,1331,0,\"impl-Debug-for-PathId\"]Cf[3952,13,3953,0,1586,1979,0,\"impl-Display-for-PathId\"]Al[3952,13,3953,0,2434,1331,0]Al[3952,13,3953,0,3314,1331,0]Al[3952,13,3953,0,3145,1331,0]Al[3952,13,3953,0,4499,1331,0]Al[3952,13,3953,0,2018,1331,0]Cj[3952,13,3953,0,4124,1331,0,\"impl-Debug-for-ClosedStream\"]Cl[3952,13,3953,0,4124,1979,0,\"impl-Display-for-ClosedStream\"]Cm[3952,13,3953,0,4495,1331,0,\"impl-Debug-for-ConnectionError\"]Co[3952,13,3953,0,4495,1979,0,\"impl-Display-for-ConnectionError\"]Al[3952,13,3953,0,4509,1331,0]Al[3952,13,3953,0,4516,1331,0]Al[3952,13,3953,0,4801,1331,0]Al[3952,13,3953,0,4816,1331,0]Al[3952,13,3953,0,3997,1331,0]Al[3952,13,3953,0,4377,1331,0]Al[3952,13,3953,0,4166,1331,0]Al[3952,13,3953,0,4990,1331,0]Al[3952,13,3953,0,4118,1331,0]Ci[3952,13,3953,0,3987,1331,0,\"impl-Debug-for-ConfigError\"]Ck[3952,13,3953,0,3987,1979,0,\"impl-Display-for-ConfigError\"]Cg[3952,13,3953,0,3128,1331,0,\"impl-Debug-for-FrameType\"]Ci[3952,13,3953,0,3128,1979,0,\"impl-Display-for-FrameType\"]Co[3952,13,3953,0,4492,1979,0,\"impl-Display-for-ConnectionClose\"]Cm[3952,13,3953,0,4492,1331,0,\"impl-Debug-for-ConnectionClose\"]D`[3952,13,3953,0,4680,1979,0,\"impl-Display-for-ApplicationClose\"]Cn[3952,13,3953,0,4680,1331,0,\"impl-Debug-for-ApplicationClose\"]Cj[3952,13,3953,0,4130,1331,0,\"impl-Debug-for-ConnectError\"]Cl[3952,13,3953,0,4130,1979,0,\"impl-Display-for-ConnectError\"]Cj[3952,13,3953,0,4133,1331,0,\"impl-Debug-for-ConnectionId\"]Cl[3952,13,3953,0,4133,1979,0,\"impl-Display-for-ConnectionId\"]Al[3952,13,3953,0,4138,1331,0]D`[3952,13,3953,0,4835,1331,0,\"impl-Debug-for-TransportErrorCode\"]Db[3952,13,3953,0,4835,1979,0,\"impl-Display-for-TransportErrorCode\"]Al[3952,13,3953,0,3317,1331,0]Al[3952,13,3953,0,4711,1331,0]Ak[3952,13,3953,0,485,1331,0]Cb[3952,13,3953,0,119,1979,0,\"impl-Display-for-Dir\"]C`[3952,13,3953,0,119,1331,0,\"impl-Debug-for-Dir\"]Cf[3952,13,3953,0,2422,1331,0,\"impl-Debug-for-StreamId\"]Ch[3952,13,3953,0,2422,1979,0,\"impl-Display-for-StreamId\"]Al[3952,13,3953,0,2431,1331,0]Ak[3952,13,3705,0,113,1331,0]Al[3952,13,3705,0,3116,1331,0]Al[3952,13,3705,0,1328,1331,0]Al[3952,13,3705,0,3992,1331,0]Al[3952,13,3705,0,1992,1331,0]Al[3952,13,3705,0,4284,1331,0]Al[3952,13,3705,0,4769,1331,0]Al[3952,13,1844,0,5079,1331,0]Al[3952,13,1844,0,3990,1331,0]Al[3952,13,1844,0,4839,1331,0]Db[3952,13,1923,0,4929,1331,0,\"impl-Debug-for-NoInitialCipherSuite\"]Dd[3952,13,1923,0,4929,1979,0,\"impl-Display-for-NoInitialCipherSuite\"]Cc[3952,13,1923,0,1341,1331,0,\"impl-Debug-for-Error\"]Ce[3952,13,1923,0,1341,1979,0,\"impl-Display-for-Error\"]Ao[4735,13,4736,3711,3309,1331,0]Ao[4735,13,4736,2445,2400,1331,0]Cg[4735,13,4736,1960,1604,1331,0,\"impl-Debug-for-VarInt\"]Ci[4735,13,4736,1960,1604,1979,0,\"impl-Display-for-VarInt\"]De[4735,13,4736,1960,4934,1331,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Dg[4735,13,4736,1960,4934,1979,0,\"impl-Display-for-VarIntBoundsExceeded\"]Ao[4735,13,4736,3164,1325,1331,0]Dd[4735,13,4736,3173,4778,1979,0,\"impl-Display-for-SendDatagramError\"]Db[4735,13,4736,3173,4778,1331,0,\"impl-Debug-for-SendDatagramError\"]Ci[4735,13,4736,1526,1587,1979,0,\"impl-Display-for-PathId\"]Cg[4735,13,4736,1526,1587,1331,0,\"impl-Debug-for-PathId\"]Ao[4735,13,4736,1526,4162,1331,0]Ao[4735,13,4736,1526,3329,1331,0]Ao[4735,13,4736,1526,3144,1331,0]Dc[4735,13,4736,1526,4832,1331,0,\"impl-Debug-for-SetPathStatusError\"]De[4735,13,4736,1526,4832,1979,0,\"impl-Display-for-SetPathStatusError\"]Cm[4735,13,4736,1526,3304,1979,0,\"impl-Display-for-ClosedPath\"]Ck[4735,13,4736,1526,3304,1331,0,\"impl-Debug-for-ClosedPath\"]Ao[4735,13,4736,1542,2435,1331,0]Ao[4735,13,4736,1542,3315,1331,0]Ao[4735,13,4736,1542,3146,1331,0]Ao[4735,13,4736,1542,4500,1331,0]Cl[4735,13,4736,1075,3149,1979,0,\"impl-Display-for-ReadError\"]Cj[4735,13,4736,1075,3149,1331,0,\"impl-Debug-for-ReadError\"]D`[4735,13,4736,1075,4292,1979,0,\"impl-Display-for-ReadableError\"]Cn[4735,13,4736,1075,4292,1331,0,\"impl-Debug-for-ReadableError\"]Ao[4735,13,4736,1080,2019,1331,0]Cm[4735,13,4736,1080,3354,1979,0,\"impl-Display-for-WriteError\"]Ck[4735,13,4736,1080,3354,1331,0,\"impl-Debug-for-WriteError\"]Cn[4735,13,4736,1080,3996,1979,0,\"impl-Display-for-FinishError\"]Cl[4735,13,4736,1080,3996,1331,0,\"impl-Debug-for-FinishError\"]Ao[4735,13,4736,2152,4014,1331,0]Ao[4735,13,4736,2152,4402,1331,0]Cm[4735,13,4736,2152,4128,1331,0,\"impl-Debug-for-ClosedStream\"]Co[4735,13,4736,2152,4128,1979,0,\"impl-Display-for-ClosedStream\"]D`[4735,13,4736,3711,4496,1331,0,\"impl-Debug-for-ConnectionError\"]Db[4735,13,4736,3711,4496,1979,0,\"impl-Display-for-ConnectionError\"]Cj[4735,13,4736,3711,3141,1331,0,\"impl-Debug-for-PathError\"]Cl[4735,13,4736,3711,3141,1979,0,\"impl-Display-for-PathError\"]Co[4735,13,4736,3711,4368,1331,0,\"impl-Debug-for-ClosePathError\"]Da[4735,13,4736,3711,4368,1979,0,\"impl-Display-for-ClosePathError\"]Dg[4735,13,4736,3711,5017,1331,0,\"impl-Debug-for-MultipathNotNegotiated\"]Di[4735,13,4736,3711,5017,1979,0,\"impl-Display-for-MultipathNotNegotiated\"]Ao[4735,13,4736,3711,1346,1331,0]Ao[4735,13,4736,1071,4510,1331,0]Ao[4735,13,4736,3291,4517,1331,0]Ao[4735,13,4736,3291,4802,1331,0]Ao[4735,13,4736,3291,4817,1331,0]Ao[4735,13,4736,3291,3998,1331,0]Ao[4735,13,4736,1842,4378,1331,0]Ao[4735,13,4736,1842,4168,1331,0]Ao[4735,13,4736,1842,4991,1331,0]Ao[4735,13,4736,1842,4120,1331,0]Cn[4735,13,4736,1842,3988,1979,0,\"impl-Display-for-ConfigError\"]Cl[4735,13,4736,1842,3988,1331,0,\"impl-Debug-for-ConfigError\"]Cl[4735,13,4736,1516,3129,1979,0,\"impl-Display-for-FrameType\"]Cj[4735,13,4736,1516,3129,1331,0,\"impl-Debug-for-FrameType\"]Co[4735,13,4736,1516,4383,1331,0,\"impl-Debug-for-InvalidFrameId\"]Da[4735,13,4736,1516,4383,1979,0,\"impl-Display-for-InvalidFrameId\"]Ck[4735,13,4736,1516,3321,1331,0,\"impl-Debug-for-MaybeFrame\"]Cm[4735,13,4736,1516,3321,1979,0,\"impl-Display-for-MaybeFrame\"]Ck[4735,13,4736,1516,3343,1331,0,\"impl-Debug-for-StreamInfo\"]Cm[4735,13,4736,1516,3343,1979,0,\"impl-Display-for-StreamInfo\"]Co[4735,13,4736,1516,4135,1979,0,\"impl-Display-for-DatagramInfo\"]Cm[4735,13,4736,1516,4135,1331,0,\"impl-Debug-for-DatagramInfo\"]D`[4735,13,4736,1516,4494,1331,0,\"impl-Debug-for-ConnectionClose\"]Db[4735,13,4736,1516,4494,1979,0,\"impl-Display-for-ConnectionClose\"]Dc[4735,13,4736,1516,4682,1979,0,\"impl-Display-for-ApplicationClose\"]Da[4735,13,4736,1516,4682,1331,0,\"impl-Debug-for-ApplicationClose\"]Ci[4735,13,4736,1516,2393,1331,0,\"impl-Debug-for-Datagram\"]Ck[4735,13,4736,1516,2393,1979,0,\"impl-Display-for-Datagram\"]Ao[4735,13,4736,2445,4686,1331,0]Ao[4735,13,4736,2445,2404,1331,0]Co[4735,13,4736,2445,4131,1979,0,\"impl-Display-for-ConnectError\"]Cm[4735,13,4736,2445,4131,1331,0,\"impl-Debug-for-ConnectError\"]Ao[4735,13,4736,2445,3982,1331,0]Ck[4735,13,4736,2445,3337,1331,0,\"impl-Debug-for-RetryError\"]Cm[4735,13,4736,2445,3337,1979,0,\"impl-Display-for-RetryError\"]Ao[4735,13,4736,1907,4287,1331,0]Ao[4735,13,4736,1907,4508,1331,0]Ao[4735,13,4736,1907,5020,1331,0]Ao[4735,13,4736,1907,2406,1331,0]Dd[4735,13,4736,1907,4776,1979,0,\"impl-Display-for-PacketDecodeError\"]Db[4735,13,4736,1907,4776,1331,0,\"impl-Debug-for-PacketDecodeError\"]Ao[4735,13,4736,1934,4498,1331,0]Ao[4735,13,4736,1934,4264,1331,0]Cm[4735,13,4736,1934,4134,1331,0,\"impl-Debug-for-ConnectionId\"]Co[4735,13,4736,1934,4134,1979,0,\"impl-Display-for-ConnectionId\"]Ao[4735,13,4736,1934,4139,1331,0]Ce[4735,13,4736,0,4406,1979,0,\"impl-Display-for-Error\"]Cc[4735,13,4736,0,4406,1331,0,\"impl-Debug-for-Error\"]Cd[4735,13,4736,0,4836,1979,0,\"impl-Display-for-Code\"]Cb[4735,13,4736,0,4836,1331,0,\"impl-Debug-for-Code\"]Ao[4735,13,4736,4305,3318,1331,0]Ao[4735,13,4736,4305,5113,1331,0]Ao[4735,13,4736,4866,4712,1331,0]An[4735,13,4736,4736,486,1331,0]Cc[4735,13,4736,4736,120,1331,0,\"impl-Debug-for-Dir\"]Ce[4735,13,4736,4736,120,1979,0,\"impl-Display-for-Dir\"]Ci[4735,13,4736,4736,2423,1331,0,\"impl-Debug-for-StreamId\"]Ck[4735,13,4736,4736,2423,1979,0,\"impl-Display-for-StreamId\"]Ao[4735,13,4736,4736,2432,1331,0]Cj[4735,13,4736,4736,3127,1331,0,\"impl-Debug-for-FourTuple\"]Cl[4735,13,4736,4736,3127,1979,0,\"impl-Display-for-FourTuple\"]Cn[4735,13,1840,1840,4298,1331,0,\"impl-Debug-for-UnexpectedEnd\"]D`[4735,13,1840,1840,4298,1979,0,\"impl-Display-for-UnexpectedEnd\"]Ao[4735,13,4975,4975,4876,1331,0]Ch[4735,13,4975,4975,1343,1979,0,\"impl-Display-for-Error\"]Cf[4735,13,4975,4975,1343,1331,0,\"impl-Debug-for-Error\"]Ao[4735,13,1845,1845,5080,1331,0]Ao[4735,13,1845,1845,3991,1331,0]Ao[4735,13,1845,1845,4842,1331,0]De[4735,13,1924,1924,4930,1331,0,\"impl-Debug-for-NoInitialCipherSuite\"]Dg[4735,13,1924,1924,4930,1979,0,\"impl-Display-for-NoInitialCipherSuite\"]Ce[4735,13,1924,0,1342,1979,0,\"impl-Display-for-Error\"]Cc[4735,13,1924,0,1342,1331,0,\"impl-Debug-for-Error\"]Am[4735,13,3706,135,114,1331,0]An[4735,13,3706,135,3117,1331,0]Ao[4735,13,3706,1511,1329,1331,0]Ao[4735,13,3706,1511,3993,1331,0]Ao[4735,13,3706,2493,1993,1331,0]Ao[4735,13,3706,2493,4285,1331,0]Ao[4735,13,3706,3706,4770,1331,0]Ch[4735,13,2123,2123,1344,1979,0,\"impl-Display-for-Error\"]Cf[4735,13,2123,2123,1344,1331,0,\"impl-Debug-for-Error\"]Ao[4735,13,2123,2123,1347,1331,0]An[4441,13,4442,393,4407,1331,0]Ao[4441,13,4442,4442,2420,1331,0]Ao[4441,13,4442,4442,2433,1331,0]Ao[4441,13,4442,4442,4140,1331,0]Al[4735,13,4736,1526,4162,0,0]Ai[3952,14,3953,0,2434,0,0]Al[4735,14,4736,1542,2435,0,0]Al[4735,13,4736,1907,4287,0,0]Al[4735,14,4736,1907,5020,0,0]Ah[4735,14,4507,0,474,0,0]Al[4441,14,4442,4442,2420,0,0]Ai[3952,13,3953,0,4990,0,0]Al[4735,13,4736,1842,4991,0,0]8Al[1365,13,1540,1540,4403,0,0]Al[4021,13,4022,4022,5021,0,0]Ai[3952,13,3953,0,4256,0,0]Al[3952,13,3953,2444,2399,0,0]Ai[3952,13,3953,0,3330,0,0]Ai[3952,13,3953,0,4509,0,0]Ai[3952,13,3953,0,4377,0,0]Ai[3952,13,3953,0,4166,0,0]Ai[3952,13,3953,0,4118,0,0]Ai[3952,13,3953,0,4133,0,0]Ai[3952,13,3953,0,4711,0,0]Ai[3952,13,3953,0,2422,0,0]Ah[3952,13,3705,0,113,0,0]Ai[3952,13,3705,0,1328,0,0]Ai[3952,13,3705,0,1992,0,0]Al[4735,13,4736,4526,4257,0,0]Al[4735,13,4736,1071,3331,0,0]Al[4735,13,4736,2445,2400,0,0]Al[4735,13,4736,1907,5138,0,0]Al[4735,13,4736,4305,5108,0,0]Al[4735,13,4736,1071,4510,0,0]Al[4735,13,4736,1842,4378,0,0]Al[4735,13,4736,1842,4168,0,0]Al[4735,13,4736,1842,4120,0,0]Al[4735,13,4736,1907,4287,0,0]Al[4735,13,4736,1934,4134,0,0]Ai[4735,13,4736,0,4406,0,0]Al[4735,13,4736,4305,5113,0,0]Al[4735,13,4736,4866,4712,0,0]Al[4735,13,4736,4736,2423,0,0]Aj[4735,13,3706,135,114,0,0]Al[4735,13,3706,1511,1329,0,0]Al[4735,13,3706,2493,1993,0,0]Ak[4441,13,4442,393,4407,0,0]Al[1050,13,4225,4225,5065,0,0]Al[1050,13,4225,4225,5066,0,0]Aj[3952,13,3953,0,485,126,0]Am[4735,13,4736,4736,486,126,0]Ai[3952,12,3953,0,3346,0,0]Al[3952,13,3953,2144,2004,0,0]0Ao[3952,13,3953,1552,4173,2004,0]Al[3952,13,3953,0,4296,3346,0]Al[4735,12,4736,1842,3347,0,0]Ao[4735,13,4736,1842,4297,3347,0]Al[3952,13,3953,3710,3308,0,0]Ai[3952,14,3953,0,3145,0,0]Al[4735,13,4736,3711,3309,0,0]Al[4735,14,4736,1542,3146,0,0]Ae[1050,7,1834,0,0,0,0]Ae[1050,7,1929,0,0,0,0]Ae[3952,4,3953,0,0,0,0]Ai[3952,15,3953,0,4138,0,0]Al[4735,15,4736,1934,4139,0,0]Al[4441,15,4442,4442,4140,0,0]210Ah[3952,5,1844,1845,0,0,0]Ae[4735,5,1845,0,0,0,0]Al[4735,15,4736,1907,4508,0,0]Al[4735,15,4736,1516,3321,0,0]Ah[3952,5,3953,1049,0,0,0]Al[4735,15,4736,3711,1346,0,0]Ai[3952,15,3953,0,3128,0,0]Al[4735,15,4736,1516,3129,0,0]Al[3952,15,3953,4088,4401,0,0]Ah[3952,6,3953,4736,0,0,0]Ae[4735,6,4736,0,0,0,0]Ai[3952,19,3953,0,1586,0,0]Al[4735,19,4736,1526,1587,0,0]Ai[3952,14,3953,0,3314,0,0]Al[4735,14,4736,1542,3315,0,0]Ai[4735,14,3142,0,4152,0,0]Al[4441,14,4442,4442,2420,0,0]Aa[495,3,0,0,0,0,0]Ai[4735,14,4736,0,4406,0,0]Ai[3952,14,3953,0,3145,0,0]Al[4735,14,4736,1542,3146,0,0]Al[4735,14,4736,1516,2393,0,0]An[3952,13,3953,3710,2413,462,0]An[3952,13,3953,2471,2403,462,0]An[3952,13,3953,4088,3333,462,0]An[3952,13,3953,4093,3339,462,0]An[4735,13,4736,1075,1566,462,0]Am[1365,13,1366,1366,127,469,0]An[1365,13,1366,1366,3984,469,0]An[1365,13,1540,1540,4015,469,0]An[1365,13,1540,1540,1361,469,0]An[1365,13,1540,1540,4403,469,0]Ak[495,13,496,496,3985,469,0]An[4021,13,4022,4022,5021,469,0]An[3952,13,3953,3710,4521,469,0]An[3952,13,3953,3710,2413,469,0]Ak[3952,13,3953,0,4256,469,0]An[3952,13,3953,1049,2414,469,0]An[3952,13,3953,1049,3119,469,0]An[3952,13,3953,1049,4678,469,0]An[3952,13,3953,3710,3306,469,0]An[3952,13,3953,3710,3308,469,0]An[3952,13,3953,3710,1995,469,0]An[3952,13,3953,3710,1579,469,0]An[3952,13,3953,3710,3109,469,0]An[3952,13,3953,3710,2387,469,0]An[3952,13,3953,3710,4158,469,0]An[3952,13,3953,3710,4163,469,0]An[3952,13,3953,3710,4936,469,0]An[3952,13,3953,3710,4777,469,0]0An[3952,13,3953,2444,2399,469,0]An[3952,13,3953,2444,4265,469,0]An[3952,13,3953,2444,1558,469,0]An[3952,13,3953,2471,2403,469,0]An[3952,13,3953,2471,3336,469,0]An[3952,13,3953,2471,4382,469,0]Am[3952,13,3953,1049,478,469,0]An[3952,13,3953,4088,3333,469,0]An[3952,13,3953,4088,4877,469,0]An[3952,13,3953,4088,4401,469,0]0Dm[3952,13,3953,4088,3148,469,0,\"impl-From%3CConnectionError%3E-for-ReadError\"]Dh[3952,13,3953,4088,3148,469,0,\"impl-From%3CResetError%3E-for-ReadError\"]An[3952,13,3953,4088,3148,469,0]An[3952,13,3953,4088,3335,469,0]0An[3952,13,3953,4088,4400,469,0]0An[3952,13,3953,1552,4173,469,0]An[3952,13,3953,1092,4010,469,0]An[3952,13,3953,4093,3339,469,0]An[3952,13,3953,4093,3353,469,0]Dk[3952,13,3953,4093,3353,469,0,\"impl-From%3CStoppedError%3E-for-WriteError\"]Dk[3952,13,3953,4093,3353,469,0,\"impl-From%3CClosedStream%3E-for-WriteError\"]Dn[3952,13,3953,4093,3353,469,0,\"impl-From%3CConnectionError%3E-for-WriteError\"]An[3952,13,3953,4093,4172,469,0]0Ak[3952,13,3953,0,3330,469,0]Ak[3952,13,3953,0,4296,469,0]Ak[3952,13,3953,0,4513,469,0]Ak[3952,13,3953,0,4150,469,0]Ak[3952,13,3953,0,4391,469,0]Dj[3952,13,3953,0,1603,469,0,\"impl-From%3CTransportErrorCode%3E-for-VarInt\"]D`[3952,13,3953,0,1603,469,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Ck[3952,13,3953,0,1603,469,0,\"impl-From%3Cu32%3E-for-VarInt\"]Ak[3952,13,3953,0,1603,469,0]Ck[3952,13,3953,0,1603,469,0,\"impl-From%3Cu16%3E-for-VarInt\"]Cj[3952,13,3953,0,1603,469,0,\"impl-From%3Cu8%3E-for-VarInt\"]Ak[3952,13,3953,0,4933,469,0]Ak[3952,13,3953,0,1324,469,0]Ak[3952,13,3953,0,1586,469,0]0Ak[3952,13,3953,0,2434,469,0]Ak[3952,13,3953,0,3314,469,0]Ak[3952,13,3953,0,3145,469,0]Ak[3952,13,3953,0,4499,469,0]Ak[3952,13,3953,0,2018,469,0]Ak[3952,13,3953,0,4124,469,0]Dl[3952,13,3953,0,4495,469,0,\"impl-From%3CCloseReason%3E-for-ConnectionError\"]Df[3952,13,3953,0,4495,469,0,\"impl-From%3CClose%3E-for-ConnectionError\"]Do[3952,13,3953,0,4495,469,0,\"impl-From%3CTransportError%3E-for-ConnectionError\"]Ak[3952,13,3953,0,4495,469,0]Ak[3952,13,3953,0,4509,469,0]Ak[3952,13,3953,0,4516,469,0]Ak[3952,13,3953,0,4801,469,0]Ak[3952,13,3953,0,4816,469,0]Ak[3952,13,3953,0,3997,469,0]0Ak[3952,13,3953,0,4377,469,0]Ak[3952,13,3953,0,4166,469,0]Ak[3952,13,3953,0,4990,469,0]Ak[3952,13,3953,0,4118,469,0]Dl[3952,13,3953,0,3987,469,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]Ea[3952,13,3953,0,3987,469,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]Ak[3952,13,3953,0,3987,469,0]Ak[3952,13,3953,0,3128,469,0]Ak[3952,13,3953,0,4492,469,0]0Ak[3952,13,3953,0,4680,469,0]Ak[3952,13,3953,0,4130,469,0]0Ak[3952,13,3953,0,4133,469,0]Ak[3952,13,3953,0,4138,469,0]Ak[3952,13,3953,0,4835,469,0]0Ak[3952,13,3953,0,3317,469,0]Ak[3952,13,3953,0,4711,469,0]Aj[3952,13,3953,0,485,469,0]Aj[3952,13,3953,0,119,469,0]Ak[3952,13,3953,0,2422,469,0]0Ak[3952,13,3953,0,2431,469,0]Aj[3952,13,3705,0,113,469,0]Ak[3952,13,3705,0,3116,469,0]Ak[3952,13,3705,0,1328,469,0]Ak[3952,13,3705,0,3992,469,0]Ak[3952,13,3705,0,1992,469,0]Ak[3952,13,3705,0,4284,469,0]Ak[3952,13,3705,0,4769,469,0]Ak[3952,13,1844,0,1987,469,0]Aj[3952,13,1844,0,472,469,0]Ak[3952,13,1844,0,5079,469,0]Ak[3952,13,1844,0,3990,469,0]0Ak[3952,13,1844,0,4839,469,0]Ak[3952,13,1923,0,3348,469,0]Ak[3952,13,1923,0,4267,469,0]Ak[3952,13,1923,0,4707,469,0]Ak[3952,13,1923,0,4709,469,0]Ak[3952,13,1923,0,4929,469,0]Df[3952,13,1923,0,1341,469,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]E`[3952,13,1923,0,1341,469,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Ak[3952,13,1923,0,1341,469,0]E`[3952,13,1923,0,1341,469,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Dn[3952,13,1923,0,1341,469,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]Dg[3952,13,1923,0,1341,469,0,\"impl-From%3CCertificateError%3E-for-Error\"]De[3952,13,1923,0,1341,469,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]Dg[3952,13,1923,0,1341,469,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]Da[3952,13,1923,0,1341,469,0,\"impl-From%3COtherError%3E-for-Error\"]Dg[3952,13,1923,0,1341,469,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]De[3952,13,1923,0,1341,469,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]Df[3952,13,1923,0,1341,469,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]An[4735,13,4736,4526,4257,469,0]An[4735,13,4736,3173,3123,469,0]An[4735,13,4736,1075,1566,469,0]An[4735,13,4736,2152,2012,469,0]An[4735,13,4736,2152,3334,469,0]An[4735,13,4736,2152,3340,469,0]An[4735,13,4736,3711,3309,469,0]An[4735,13,4736,1071,3331,469,0]An[4735,13,4736,1842,4297,469,0]An[4735,13,4736,2445,2400,469,0]An[4735,13,4736,2445,4263,469,0]An[4735,13,4736,1907,5138,469,0]An[4735,13,4736,4305,5108,469,0]An[4735,13,4736,1551,4514,469,0]An[4735,13,4736,1551,4151,469,0]An[4735,13,4736,1551,4392,469,0]An[4735,13,4736,1960,1604,469,0]Cm[4735,13,4736,1960,1604,469,0,\"impl-From%3Cu8%3E-for-VarInt\"]Co[4735,13,4736,1960,1604,469,0,\"impl-From%3CCode%3E-for-VarInt\"]Cn[4735,13,4736,1960,1604,469,0,\"impl-From%3Cu16%3E-for-VarInt\"]Cn[4735,13,4736,1960,1604,469,0,\"impl-From%3Cu32%3E-for-VarInt\"]Dc[4735,13,4736,1960,1604,469,0,\"impl-From%3CStreamId%3E-for-VarInt\"]An[4735,13,4736,1960,4934,469,0]An[4735,13,4736,3164,1325,469,0]An[4735,13,4736,3173,4778,469,0]An[4735,13,4736,1526,1587,469,0]0An[4735,13,4736,1526,4162,469,0]An[4735,13,4736,1526,3329,469,0]An[4735,13,4736,1526,3144,469,0]An[4735,13,4736,1526,4832,469,0]An[4735,13,4736,1526,3304,469,0]An[4735,13,4736,1542,2435,469,0]An[4735,13,4736,1542,3315,469,0]An[4735,13,4736,1542,3146,469,0]An[4735,13,4736,1542,4500,469,0]An[4735,13,4736,1075,3149,469,0]An[4735,13,4736,1075,4292,469,0]An[4735,13,4736,1080,2019,469,0]An[4735,13,4736,1080,3354,469,0]An[4735,13,4736,1080,3996,469,0]An[4735,13,4736,2152,4014,469,0]An[4735,13,4736,2152,4402,469,0]An[4735,13,4736,2152,4128,469,0]An[4735,13,4736,3711,4496,469,0]0An[4735,13,4736,3711,3141,469,0]An[4735,13,4736,3711,4368,469,0]An[4735,13,4736,3711,5017,469,0]An[4735,13,4736,3711,1346,469,0]0An[4735,13,4736,1071,4510,469,0]An[4735,13,4736,3291,4517,469,0]An[4735,13,4736,3291,4802,469,0]An[4735,13,4736,3291,4817,469,0]An[4735,13,4736,3291,3998,469,0]0An[4735,13,4736,1842,4378,469,0]An[4735,13,4736,1842,4168,469,0]An[4735,13,4736,1842,4991,469,0]An[4735,13,4736,1842,4120,469,0]An[4735,13,4736,1842,3988,469,0]Ed[4735,13,4736,1842,3988,469,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]Do[4735,13,4736,1842,3988,469,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]An[4735,13,4736,1516,3129,469,0]An[4735,13,4736,1516,4383,469,0]An[4735,13,4736,1516,3321,469,0]An[4735,13,4736,1516,3343,469,0]An[4735,13,4736,1516,4135,469,0]An[4735,13,4736,1516,4494,469,0]0An[4735,13,4736,1516,4682,469,0]An[4735,13,4736,1516,2393,469,0]An[4735,13,4736,2445,4686,469,0]An[4735,13,4736,2445,2404,469,0]An[4735,13,4736,2445,4131,469,0]0An[4735,13,4736,2445,3982,469,0]An[4735,13,4736,2445,3337,469,0]An[4735,13,4736,1907,4287,469,0]An[4735,13,4736,1907,4508,469,0]An[4735,13,4736,1907,5020,469,0]An[4735,13,4736,1907,2406,469,0]An[4735,13,4736,1907,4776,469,0]0An[4735,13,4736,1934,4498,469,0]An[4735,13,4736,1934,4264,469,0]An[4735,13,4736,1934,4134,469,0]An[4735,13,4736,1934,4139,469,0]Ak[4735,13,4736,0,4406,469,0]0Ak[4735,13,4736,0,4836,469,0]0An[4735,13,4736,4305,3318,469,0]An[4735,13,4736,4305,5113,469,0]An[4735,13,4736,4866,4712,469,0]Am[4735,13,4736,4736,486,469,0]Am[4735,13,4736,4736,120,469,0]An[4735,13,4736,4736,2423,469,0]0An[4735,13,4736,4736,2432,469,0]An[4735,13,4736,4736,3127,469,0]An[4735,13,1840,1840,4298,469,0]An[4735,13,4975,4975,4876,469,0]An[4735,13,4975,4975,1343,469,0]0An[4735,13,1845,1845,1988,469,0]Am[4735,13,1845,1845,473,469,0]An[4735,13,1845,1845,5080,469,0]An[4735,13,1845,1845,3991,469,0]0An[4735,13,1845,1845,4842,469,0]An[4735,13,1924,1924,3349,469,0]An[4735,13,1924,1924,4268,469,0]An[4735,13,1924,1924,4708,469,0]An[4735,13,1924,1924,4710,469,0]An[4735,13,1924,1924,4930,469,0]E`[4735,13,1924,0,1342,469,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Dg[4735,13,1924,0,1342,469,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]Ak[4735,13,1924,0,1342,469,0]E`[4735,13,1924,0,1342,469,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Da[4735,13,1924,0,1342,469,0,\"impl-From%3COtherError%3E-for-Error\"]Df[4735,13,1924,0,1342,469,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Df[4735,13,1924,0,1342,469,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]Dg[4735,13,1924,0,1342,469,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]De[4735,13,1924,0,1342,469,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]De[4735,13,1924,0,1342,469,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]Dg[4735,13,1924,0,1342,469,0,\"impl-From%3CCertificateError%3E-for-Error\"]Dn[4735,13,1924,0,1342,469,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]Al[4735,13,3706,135,114,469,0]Am[4735,13,3706,135,3117,469,0]An[4735,13,3706,1511,1329,469,0]An[4735,13,3706,1511,3993,469,0]An[4735,13,3706,2493,1993,469,0]An[4735,13,3706,2493,4285,469,0]An[4735,13,3706,3706,4770,469,0]An[4735,13,2123,2123,1344,469,0]An[4735,13,2123,2123,1347,469,0]An[4441,13,4442,4442,3352,469,0]0Am[4441,13,4442,393,4407,469,0]An[4441,13,4442,4442,2420,469,0]An[4441,13,4442,4442,2433,469,0]An[4441,13,4442,4442,4140,469,0]An[1050,13,1051,1051,3120,469,0]An[1050,13,1051,1051,4870,469,0]Am[1050,13,1834,1834,128,469,0]An[1050,13,1541,1541,1362,469,0]An[1050,13,1541,1541,4016,469,0]An[1050,13,1541,1541,4504,469,0]An[1050,13,4225,4225,5065,469,0]An[1050,13,4225,4225,5066,469,0]Am[1050,13,1929,1929,129,469,0]Am[3972,13,3973,3973,118,469,0]An[3972,13,3973,3973,2390,469,0]Ak[3952,13,3953,0,1603,470,0]Ak[3952,13,3953,0,1586,470,0]Ak[3952,13,3953,0,3997,470,0]Ak[3952,13,3953,0,4133,470,0]Aj[3952,13,3953,0,485,470,0]Aj[3952,13,3953,0,119,470,0]Ak[3952,13,3953,0,2422,470,0]An[4735,13,4736,1960,1604,470,0]An[4735,13,4736,3173,4778,470,0]An[4735,13,4736,1526,1587,470,0]An[4735,13,4736,1075,3149,470,0]An[4735,13,4736,1075,4292,470,0]An[4735,13,4736,1080,3354,470,0]An[4735,13,4736,3291,3998,470,0]An[4735,13,4736,2445,4686,470,0]An[4735,13,4736,1907,4776,470,0]An[4735,13,4736,1934,4134,470,0]Am[4735,13,4736,4736,486,470,0]Am[4735,13,4736,4736,120,470,0]An[4735,13,4736,4736,2423,470,0]An[4735,13,4736,4736,3127,470,0]Am[1365,13,1366,1366,127,471,0]An[1365,13,1366,1366,3984,471,0]An[1365,13,1540,1540,4015,471,0]An[1365,13,1540,1540,1361,471,0]An[1365,13,1540,1540,4403,471,0]Ak[495,13,496,496,3985,471,0]An[4021,13,4022,4022,5021,471,0]An[3952,13,3953,3710,4521,471,0]An[3952,13,3953,3710,2413,471,0]Ak[3952,13,3953,0,4256,471,0]An[3952,13,3953,1049,2414,471,0]An[3952,13,3953,1049,3119,471,0]An[3952,13,3953,1049,4678,471,0]An[3952,13,3953,3710,3306,471,0]An[3952,13,3953,3710,3308,471,0]An[3952,13,3953,3710,1995,471,0]An[3952,13,3953,3710,1579,471,0]An[3952,13,3953,3710,3109,471,0]An[3952,13,3953,3710,2387,471,0]An[3952,13,3953,3710,4158,471,0]An[3952,13,3953,3710,4163,471,0]An[3952,13,3953,3710,4936,471,0]An[3952,13,3953,3710,4777,471,0]An[3952,13,3953,2444,2399,471,0]An[3952,13,3953,2444,4265,471,0]An[3952,13,3953,2444,1558,471,0]An[3952,13,3953,2471,2403,471,0]An[3952,13,3953,2471,3336,471,0]An[3952,13,3953,2471,4382,471,0]Am[3952,13,3953,1049,478,471,0]An[3952,13,3953,4088,3333,471,0]An[3952,13,3953,4088,4877,471,0]An[3952,13,3953,4088,4401,471,0]An[3952,13,3953,4088,3148,471,0]An[3952,13,3953,4088,3335,471,0]An[3952,13,3953,4088,4400,471,0]An[3952,13,3953,1552,4173,471,0]An[3952,13,3953,1092,4010,471,0]An[3952,13,3953,4093,3339,471,0]An[3952,13,3953,4093,3353,471,0]An[3952,13,3953,4093,4172,471,0]Ak[3952,13,3953,0,3330,471,0]Ak[3952,13,3953,0,4296,471,0]Ak[3952,13,3953,0,4513,471,0]Ak[3952,13,3953,0,4150,471,0]Ak[3952,13,3953,0,4391,471,0]Ak[3952,13,3953,0,1603,471,0]Ak[3952,13,3953,0,4933,471,0]Ak[3952,13,3953,0,1324,471,0]Ak[3952,13,3953,0,1586,471,0]Ak[3952,13,3953,0,2434,471,0]Ak[3952,13,3953,0,3314,471,0]Ak[3952,13,3953,0,3145,471,0]Ak[3952,13,3953,0,4499,471,0]Ak[3952,13,3953,0,2018,471,0]Ak[3952,13,3953,0,4124,471,0]Ak[3952,13,3953,0,4495,471,0]Ak[3952,13,3953,0,4509,471,0]Ak[3952,13,3953,0,4516,471,0]Ak[3952,13,3953,0,4801,471,0]Ak[3952,13,3953,0,4816,471,0]Ak[3952,13,3953,0,3997,471,0]Ak[3952,13,3953,0,4377,471,0]Ak[3952,13,3953,0,4166,471,0]Ak[3952,13,3953,0,4990,471,0]Ak[3952,13,3953,0,4118,471,0]Ak[3952,13,3953,0,3987,471,0]Ak[3952,13,3953,0,3128,471,0]Ak[3952,13,3953,0,4492,471,0]Ak[3952,13,3953,0,4680,471,0]Ak[3952,13,3953,0,4130,471,0]Ak[3952,13,3953,0,4133,471,0]Ak[3952,13,3953,0,4138,471,0]Ak[3952,13,3953,0,4835,471,0]Ak[3952,13,3953,0,3317,471,0]Ak[3952,13,3953,0,4711,471,0]Aj[3952,13,3953,0,485,471,0]Aj[3952,13,3953,0,119,471,0]Ak[3952,13,3953,0,2422,471,0]Ak[3952,13,3953,0,2431,471,0]Aj[3952,13,3705,0,113,471,0]Ak[3952,13,3705,0,3116,471,0]Ak[3952,13,3705,0,1328,471,0]Ak[3952,13,3705,0,3992,471,0]Ak[3952,13,3705,0,1992,471,0]Ak[3952,13,3705,0,4284,471,0]Ak[3952,13,3705,0,4769,471,0]Ak[3952,13,1844,0,1987,471,0]Aj[3952,13,1844,0,472,471,0]Ak[3952,13,1844,0,5079,471,0]Ak[3952,13,1844,0,3990,471,0]Ak[3952,13,1844,0,4839,471,0]Ak[3952,13,1923,0,3348,471,0]Ak[3952,13,1923,0,4267,471,0]Ak[3952,13,1923,0,4707,471,0]Ak[3952,13,1923,0,4709,471,0]Ak[3952,13,1923,0,4929,471,0]Ak[3952,13,1923,0,1341,471,0]An[4735,13,4736,4526,4257,471,0]An[4735,13,4736,3173,3123,471,0]An[4735,13,4736,1075,1566,471,0]An[4735,13,4736,2152,2012,471,0]An[4735,13,4736,2152,3334,471,0]An[4735,13,4736,2152,3340,471,0]An[4735,13,4736,3711,3309,471,0]An[4735,13,4736,1071,3331,471,0]An[4735,13,4736,1842,4297,471,0]An[4735,13,4736,2445,2400,471,0]An[4735,13,4736,2445,4263,471,0]An[4735,13,4736,1907,5138,471,0]An[4735,13,4736,4305,5108,471,0]An[4735,13,4736,1551,4514,471,0]An[4735,13,4736,1551,4151,471,0]An[4735,13,4736,1551,4392,471,0]An[4735,13,4736,1960,1604,471,0]An[4735,13,4736,1960,4934,471,0]An[4735,13,4736,3164,1325,471,0]An[4735,13,4736,3173,4778,471,0]An[4735,13,4736,1526,1587,471,0]An[4735,13,4736,1526,4162,471,0]An[4735,13,4736,1526,3329,471,0]An[4735,13,4736,1526,3144,471,0]An[4735,13,4736,1526,4832,471,0]An[4735,13,4736,1526,3304,471,0]An[4735,13,4736,1542,2435,471,0]An[4735,13,4736,1542,3315,471,0]An[4735,13,4736,1542,3146,471,0]An[4735,13,4736,1542,4500,471,0]An[4735,13,4736,1075,3149,471,0]An[4735,13,4736,1075,4292,471,0]An[4735,13,4736,1080,2019,471,0]An[4735,13,4736,1080,3354,471,0]An[4735,13,4736,1080,3996,471,0]An[4735,13,4736,2152,4014,471,0]An[4735,13,4736,2152,4402,471,0]An[4735,13,4736,2152,4128,471,0]An[4735,13,4736,3711,4496,471,0]An[4735,13,4736,3711,3141,471,0]An[4735,13,4736,3711,4368,471,0]An[4735,13,4736,3711,5017,471,0]An[4735,13,4736,3711,1346,471,0]An[4735,13,4736,1071,4510,471,0]An[4735,13,4736,3291,4517,471,0]An[4735,13,4736,3291,4802,471,0]An[4735,13,4736,3291,4817,471,0]An[4735,13,4736,3291,3998,471,0]An[4735,13,4736,1842,4378,471,0]An[4735,13,4736,1842,4168,471,0]An[4735,13,4736,1842,4991,471,0]An[4735,13,4736,1842,4120,471,0]An[4735,13,4736,1842,3988,471,0]An[4735,13,4736,1516,3129,471,0]An[4735,13,4736,1516,4383,471,0]An[4735,13,4736,1516,3321,471,0]An[4735,13,4736,1516,3343,471,0]An[4735,13,4736,1516,4135,471,0]An[4735,13,4736,1516,4494,471,0]An[4735,13,4736,1516,4682,471,0]An[4735,13,4736,1516,2393,471,0]An[4735,13,4736,2445,4686,471,0]An[4735,13,4736,2445,2404,471,0]An[4735,13,4736,2445,4131,471,0]An[4735,13,4736,2445,3982,471,0]An[4735,13,4736,2445,3337,471,0]An[4735,13,4736,1907,4287,471,0]An[4735,13,4736,1907,4508,471,0]An[4735,13,4736,1907,5020,471,0]An[4735,13,4736,1907,2406,471,0]An[4735,13,4736,1907,4776,471,0]An[4735,13,4736,1934,4498,471,0]An[4735,13,4736,1934,4264,471,0]An[4735,13,4736,1934,4134,471,0]An[4735,13,4736,1934,4139,471,0]Ak[4735,13,4736,0,4406,471,0]Ak[4735,13,4736,0,4836,471,0]An[4735,13,4736,4305,3318,471,0]An[4735,13,4736,4305,5113,471,0]An[4735,13,4736,4866,4712,471,0]Am[4735,13,4736,4736,486,471,0]Am[4735,13,4736,4736,120,471,0]An[4735,13,4736,4736,2423,471,0]An[4735,13,4736,4736,2432,471,0]An[4735,13,4736,4736,3127,471,0]An[4735,13,1840,1840,4298,471,0]An[4735,13,4975,4975,4876,471,0]An[4735,13,4975,4975,1343,471,0]An[4735,13,1845,1845,1988,471,0]Am[4735,13,1845,1845,473,471,0]An[4735,13,1845,1845,5080,471,0]An[4735,13,1845,1845,3991,471,0]An[4735,13,1845,1845,4842,471,0]An[4735,13,1924,1924,3349,471,0]An[4735,13,1924,1924,4268,471,0]An[4735,13,1924,1924,4708,471,0]An[4735,13,1924,1924,4710,471,0]An[4735,13,1924,1924,4930,471,0]Ak[4735,13,1924,0,1342,471,0]Al[4735,13,3706,135,114,471,0]Am[4735,13,3706,135,3117,471,0]An[4735,13,3706,1511,1329,471,0]An[4735,13,3706,1511,3993,471,0]An[4735,13,3706,2493,1993,471,0]An[4735,13,3706,2493,4285,471,0]An[4735,13,3706,3706,4770,471,0]An[4735,13,2123,2123,1344,471,0]An[4735,13,2123,2123,1347,471,0]An[4441,13,4442,4442,3352,471,0]Am[4441,13,4442,393,4407,471,0]An[4441,13,4442,4442,2420,471,0]An[4441,13,4442,4442,2433,471,0]An[4441,13,4442,4442,4140,471,0]An[1050,13,1051,1051,3120,471,0]An[1050,13,1051,1051,4870,471,0]Am[1050,13,1834,1834,128,471,0]An[1050,13,1541,1541,1362,471,0]An[1050,13,1541,1541,4016,471,0]An[1050,13,1541,1541,4504,471,0]An[1050,13,4225,4225,5065,471,0]An[1050,13,4225,4225,5066,471,0]Am[1050,13,1929,1929,129,471,0]Am[3972,13,3973,3973,118,471,0]An[3972,13,3973,3973,2390,471,0]Ac[495,7,496,0,0,0,0]Ae[4021,7,4022,0,0,0,0]Ae[4308,7,4309,0,0,0,0]Ae[3972,7,3973,0,0,0,0]Ae[4796,7,4797,0,0,0,0]Al[4735,13,4736,1075,1566,0,0]Ai[3952,12,1844,0,1971,0,0]Al[4735,13,4736,2152,2012,0,0]Al[4735,12,1845,1845,1972,0,0]Al[3952,13,3953,3710,3308,0,0]Ab[1050,3,0,0,0,0,0]Ak[3952,13,3953,1049,478,0,0]Ai[3952,14,3953,0,3314,0,0]Al[4735,13,4736,3711,3309,0,0]Al[4735,14,4736,1542,3315,0,0]Al[3952,12,3953,2144,3297,0,0]Ao[3952,13,3953,3710,4521,1576,0]Ao[3952,13,3953,3710,2413,1576,0]Ao[3952,13,3953,1049,2414,1576,0]Ao[3952,13,3953,1049,3119,1576,0]Ao[3952,13,3953,3710,3306,1576,0]Ao[3952,13,3953,3710,1995,1576,0]Ao[3952,13,3953,3710,1579,1576,0]Ao[3952,13,3953,3710,3109,1576,0]Ao[3952,13,3953,3710,2387,1576,0]Ao[3952,13,3953,3710,4158,1576,0]Ao[3952,13,3953,3710,4163,1576,0]Ao[3952,13,3953,2444,1558,1576,0]Ao[3952,13,3953,2471,4382,1576,0]?Al[3952,13,3953,4088,3333,0,0]Al[4735,13,4736,2152,3334,0,0]Al[4735,13,4975,4975,4876,0,0]Al[4735,13,4736,3173,3123,0,0]Ak[4441,13,4442,393,4407,0,0]Ai[3952,12,1844,0,1971,0,0]Al[4735,12,1845,1845,1972,0,0]32Ai[3952,13,3953,0,4990,0,0]Al[4735,13,4736,1842,4991,0,0]Al[3952,13,3953,3710,3308,0,0]Al[4735,13,4736,3711,3309,0,0]Ai[3952,12,1844,0,1983,0,0]Al[4735,12,1845,1845,1984,0,0]Al[1365,14,1540,1540,4403,0,0]Ai[3952,14,3953,0,2431,0,0]Al[4735,14,4736,4736,2432,0,0]Ai[4735,14,4507,0,1358,0,0]Al[3952,13,3953,4088,3333,0,0]Al[3952,13,3953,4088,4877,0,0]Al[4735,13,4736,2152,3334,0,0]Ai[3952,12,3953,0,3350,0,0]Al[3952,13,3953,0,4391,3350,0]Al[3952,13,3953,0,4711,3350,0]Al[4735,12,4736,1551,3351,0,0]Ao[4735,13,4736,1551,4392,3351,0]Ao[4735,13,4736,4866,4712,3351,0]Am[1365,13,1366,1366,127,488,0]An[1365,13,1366,1366,3984,488,0]An[1365,13,1540,1540,4015,488,0]An[1365,13,1540,1540,1361,488,0]An[1365,13,1540,1540,4403,488,0]Ak[495,13,496,496,3985,488,0]An[4021,13,4022,4022,5021,488,0]An[3952,13,3953,3710,4521,488,0]An[3952,13,3953,3710,2413,488,0]Ak[3952,13,3953,0,4256,488,0]An[3952,13,3953,1049,2414,488,0]An[3952,13,3953,1049,3119,488,0]An[3952,13,3953,1049,4678,488,0]An[3952,13,3953,3710,3306,488,0]An[3952,13,3953,3710,3308,488,0]An[3952,13,3953,3710,1995,488,0]An[3952,13,3953,3710,1579,488,0]An[3952,13,3953,3710,3109,488,0]An[3952,13,3953,3710,2387,488,0]An[3952,13,3953,3710,4158,488,0]An[3952,13,3953,3710,4163,488,0]An[3952,13,3953,3710,4936,488,0]An[3952,13,3953,3710,4777,488,0]An[3952,13,3953,2444,2399,488,0]An[3952,13,3953,2444,4265,488,0]An[3952,13,3953,2444,1558,488,0]An[3952,13,3953,2471,2403,488,0]An[3952,13,3953,2471,3336,488,0]An[3952,13,3953,2471,4382,488,0]Am[3952,13,3953,1049,478,488,0]An[3952,13,3953,4088,3333,488,0]An[3952,13,3953,4088,4877,488,0]An[3952,13,3953,4088,4401,488,0]An[3952,13,3953,4088,3148,488,0]An[3952,13,3953,4088,3335,488,0]An[3952,13,3953,4088,4400,488,0]An[3952,13,3953,1552,4173,488,0]An[3952,13,3953,1092,4010,488,0]An[3952,13,3953,4093,3339,488,0]An[3952,13,3953,4093,3353,488,0]An[3952,13,3953,4093,4172,488,0]Ak[3952,13,3953,0,3330,488,0]Ak[3952,13,3953,0,4296,488,0]Ak[3952,13,3953,0,4513,488,0]Ak[3952,13,3953,0,4150,488,0]Ak[3952,13,3953,0,4391,488,0]Ak[3952,13,3953,0,1603,488,0]Ak[3952,13,3953,0,4933,488,0]Ak[3952,13,3953,0,1324,488,0]Ak[3952,13,3953,0,1586,488,0]Ak[3952,13,3953,0,2434,488,0]Ak[3952,13,3953,0,3314,488,0]Ak[3952,13,3953,0,3145,488,0]Ak[3952,13,3953,0,4499,488,0]Ak[3952,13,3953,0,2018,488,0]Ak[3952,13,3953,0,4124,488,0]Ak[3952,13,3953,0,4495,488,0]Ak[3952,13,3953,0,4509,488,0]Ak[3952,13,3953,0,4516,488,0]Ak[3952,13,3953,0,4801,488,0]Ak[3952,13,3953,0,4816,488,0]Ak[3952,13,3953,0,3997,488,0]Ak[3952,13,3953,0,4377,488,0]Ak[3952,13,3953,0,4166,488,0]Ak[3952,13,3953,0,4990,488,0]Ak[3952,13,3953,0,4118,488,0]Ak[3952,13,3953,0,3987,488,0]Ak[3952,13,3953,0,3128,488,0]Ak[3952,13,3953,0,4492,488,0]Ak[3952,13,3953,0,4680,488,0]Ak[3952,13,3953,0,4130,488,0]Ak[3952,13,3953,0,4133,488,0]Ak[3952,13,3953,0,4138,488,0]Ak[3952,13,3953,0,4835,488,0]Ak[3952,13,3953,0,3317,488,0]Ak[3952,13,3953,0,4711,488,0]Aj[3952,13,3953,0,485,488,0]Aj[3952,13,3953,0,119,488,0]Ak[3952,13,3953,0,2422,488,0]Ak[3952,13,3953,0,2431,488,0]Aj[3952,13,3705,0,113,488,0]Ak[3952,13,3705,0,3116,488,0]Ak[3952,13,3705,0,1328,488,0]Ak[3952,13,3705,0,3992,488,0]Ak[3952,13,3705,0,1992,488,0]Ak[3952,13,3705,0,4284,488,0]Ak[3952,13,3705,0,4769,488,0]Ak[3952,13,1844,0,1987,488,0]Aj[3952,13,1844,0,472,488,0]Ak[3952,13,1844,0,5079,488,0]Ak[3952,13,1844,0,3990,488,0]Ak[3952,13,1844,0,4839,488,0]Ak[3952,13,1923,0,3348,488,0]Ak[3952,13,1923,0,4267,488,0]Ak[3952,13,1923,0,4707,488,0]Ak[3952,13,1923,0,4709,488,0]Ak[3952,13,1923,0,4929,488,0]Ak[3952,13,1923,0,1341,488,0]An[4735,13,4736,4526,4257,488,0]An[4735,13,4736,3173,3123,488,0]An[4735,13,4736,1075,1566,488,0]An[4735,13,4736,2152,2012,488,0]An[4735,13,4736,2152,3334,488,0]An[4735,13,4736,2152,3340,488,0]An[4735,13,4736,3711,3309,488,0]An[4735,13,4736,1071,3331,488,0]An[4735,13,4736,1842,4297,488,0]An[4735,13,4736,2445,2400,488,0]An[4735,13,4736,2445,4263,488,0]An[4735,13,4736,1907,5138,488,0]An[4735,13,4736,4305,5108,488,0]An[4735,13,4736,1551,4514,488,0]An[4735,13,4736,1551,4151,488,0]An[4735,13,4736,1551,4392,488,0]An[4735,13,4736,1960,1604,488,0]An[4735,13,4736,1960,4934,488,0]An[4735,13,4736,3164,1325,488,0]An[4735,13,4736,3173,4778,488,0]An[4735,13,4736,1526,1587,488,0]An[4735,13,4736,1526,4162,488,0]An[4735,13,4736,1526,3329,488,0]An[4735,13,4736,1526,3144,488,0]An[4735,13,4736,1526,4832,488,0]An[4735,13,4736,1526,3304,488,0]An[4735,13,4736,1542,2435,488,0]An[4735,13,4736,1542,3315,488,0]An[4735,13,4736,1542,3146,488,0]An[4735,13,4736,1542,4500,488,0]An[4735,13,4736,1075,3149,488,0]An[4735,13,4736,1075,4292,488,0]An[4735,13,4736,1080,2019,488,0]An[4735,13,4736,1080,3354,488,0]An[4735,13,4736,1080,3996,488,0]An[4735,13,4736,2152,4014,488,0]An[4735,13,4736,2152,4402,488,0]An[4735,13,4736,2152,4128,488,0]An[4735,13,4736,3711,4496,488,0]An[4735,13,4736,3711,3141,488,0]An[4735,13,4736,3711,4368,488,0]An[4735,13,4736,3711,5017,488,0]An[4735,13,4736,3711,1346,488,0]An[4735,13,4736,1071,4510,488,0]An[4735,13,4736,3291,4517,488,0]An[4735,13,4736,3291,4802,488,0]An[4735,13,4736,3291,4817,488,0]An[4735,13,4736,3291,3998,488,0]An[4735,13,4736,1842,4378,488,0]An[4735,13,4736,1842,4168,488,0]An[4735,13,4736,1842,4991,488,0]An[4735,13,4736,1842,4120,488,0]An[4735,13,4736,1842,3988,488,0]An[4735,13,4736,1516,3129,488,0]An[4735,13,4736,1516,4383,488,0]An[4735,13,4736,1516,3321,488,0]An[4735,13,4736,1516,3343,488,0]An[4735,13,4736,1516,4135,488,0]An[4735,13,4736,1516,4494,488,0]An[4735,13,4736,1516,4682,488,0]An[4735,13,4736,1516,2393,488,0]An[4735,13,4736,2445,4686,488,0]An[4735,13,4736,2445,2404,488,0]An[4735,13,4736,2445,4131,488,0]An[4735,13,4736,2445,3982,488,0]An[4735,13,4736,2445,3337,488,0]An[4735,13,4736,1907,4287,488,0]An[4735,13,4736,1907,4508,488,0]An[4735,13,4736,1907,5020,488,0]An[4735,13,4736,1907,2406,488,0]An[4735,13,4736,1907,4776,488,0]An[4735,13,4736,1934,4498,488,0]An[4735,13,4736,1934,4264,488,0]An[4735,13,4736,1934,4134,488,0]An[4735,13,4736,1934,4139,488,0]Ak[4735,13,4736,0,4406,488,0]Ak[4735,13,4736,0,4836,488,0]An[4735,13,4736,4305,3318,488,0]An[4735,13,4736,4305,5113,488,0]An[4735,13,4736,4866,4712,488,0]Am[4735,13,4736,4736,486,488,0]Am[4735,13,4736,4736,120,488,0]An[4735,13,4736,4736,2423,488,0]An[4735,13,4736,4736,2432,488,0]An[4735,13,4736,4736,3127,488,0]An[4735,13,1840,1840,4298,488,0]An[4735,13,4975,4975,4876,488,0]An[4735,13,4975,4975,1343,488,0]An[4735,13,1845,1845,1988,488,0]Am[4735,13,1845,1845,473,488,0]An[4735,13,1845,1845,5080,488,0]An[4735,13,1845,1845,3991,488,0]An[4735,13,1845,1845,4842,488,0]An[4735,13,1924,1924,3349,488,0]An[4735,13,1924,1924,4268,488,0]An[4735,13,1924,1924,4708,488,0]An[4735,13,1924,1924,4710,488,0]An[4735,13,1924,1924,4930,488,0]Ak[4735,13,1924,0,1342,488,0]Al[4735,13,3706,135,114,488,0]Am[4735,13,3706,135,3117,488,0]An[4735,13,3706,1511,1329,488,0]An[4735,13,3706,1511,3993,488,0]An[4735,13,3706,2493,1993,488,0]An[4735,13,3706,2493,4285,488,0]An[4735,13,3706,3706,4770,488,0]An[4735,13,2123,2123,1344,488,0]An[4735,13,2123,2123,1347,488,0]An[1050,13,1051,1051,3120,488,0]An[1050,13,1051,1051,4870,488,0]Am[1050,13,1834,1834,128,488,0]An[1050,13,1541,1541,1362,488,0]An[1050,13,1541,1541,4016,488,0]An[1050,13,1541,1541,4504,488,0]An[1050,13,4225,4225,5065,488,0]An[1050,13,4225,4225,5066,488,0]Am[1050,13,1929,1929,129,488,0]Am[3972,13,3973,3973,118,488,0]An[3972,13,3973,3973,2390,488,0]Ah[3952,5,3953,3164,0,0,0]Ah[4735,5,4736,3164,0,0,0]Ah[3952,5,3705,1511,0,0,0]Ah[4735,5,3706,1511,0,0,0]Al[1050,15,1051,1051,4870,0,0]Ah[3952,6,1923,1514,0,0,0]Ah[4735,6,1924,1514,0,0,0]Ae[4735,6,4975,0,0,0,0]Ae[4735,6,2123,0,0,0,0]Ah[4735,6,4736,3711,0,0,0]1Al[4735,15,4736,1516,3321,0,0]Ai[3952,15,1923,0,1341,0,0]Ai[4735,15,1924,0,1342,0,0]Al[3952,15,3953,4088,3148,0,0]Ai[3952,15,3953,0,4495,0,0]Al[4735,15,4736,1075,3149,0,0]Al[4735,15,4736,3711,4496,0,0]Al[4735,15,4736,1907,4508,0,0]0Ae[1365,5,1540,0,0,0,0]Ae[1050,5,1541,0,0,0,0]Ab[1365,3,0,0,0,0,0]Ai[3952,12,3705,0,4767,0,0]Al[3952,13,3705,0,3116,4767,0]Al[3952,13,3705,0,3992,4767,0]Al[3952,13,3705,0,4284,4767,0]Al[4735,12,3706,3706,4768,0,0]An[4735,13,3706,135,3117,4768,0]Ao[4735,13,3706,1511,3993,4768,0]Ao[4735,13,3706,2493,4285,4768,0]Al[1050,13,1051,1051,4870,0,0]Ai[3952,14,3953,0,1324,0,0]Ai[3952,14,3953,0,2434,0,0]Ai[3952,14,3953,0,2018,0,0]Al[4735,14,4736,3164,1325,0,0]Al[4735,14,4736,1542,2435,0,0]Al[4735,14,4736,1080,2019,0,0]Al[4735,14,4736,2445,3982,0,0]An[1365,13,1366,1366,127,1326,0]Ao[1365,13,1366,1366,3984,1326,0]Ao[3952,13,3953,3710,3308,1326,0]Ao[3952,13,3953,3710,4936,1326,0]Ao[3952,13,3953,3710,4777,1326,0]Ao[3952,13,3953,2444,2399,1326,0]Ao[3952,13,3953,2444,4265,1326,0]Ao[3952,13,3953,4088,4401,1326,0]Ao[3952,13,3953,4088,3148,1326,0]Ao[3952,13,3953,4088,3335,1326,0]Ao[3952,13,3953,4088,4400,1326,0]Ao[3952,13,3953,4093,3353,1326,0]Ao[3952,13,3953,4093,4172,1326,0]Al[3952,13,3953,0,1603,1326,0]Al[3952,13,3953,0,4933,1326,0]Al[3952,13,3953,0,1586,1326,0]Al[3952,13,3953,0,2434,1326,0]Al[3952,13,3953,0,3314,1326,0]Al[3952,13,3953,0,3145,1326,0]Al[3952,13,3953,0,4499,1326,0]Al[3952,13,3953,0,2018,1326,0]Al[3952,13,3953,0,4124,1326,0]Al[3952,13,3953,0,4495,1326,0]Al[3952,13,3953,0,4516,1326,0]Al[3952,13,3953,0,4801,1326,0]Al[3952,13,3953,0,4816,1326,0]Al[3952,13,3953,0,3997,1326,0]Al[3952,13,3953,0,4377,1326,0]Al[3952,13,3953,0,4166,1326,0]Al[3952,13,3953,0,4990,1326,0]Al[3952,13,3953,0,4118,1326,0]Al[3952,13,3953,0,3987,1326,0]Al[3952,13,3953,0,3128,1326,0]Al[3952,13,3953,0,4492,1326,0]Al[3952,13,3953,0,4680,1326,0]Al[3952,13,3953,0,4130,1326,0]Al[3952,13,3953,0,4133,1326,0]Al[3952,13,3953,0,4138,1326,0]Al[3952,13,3953,0,4835,1326,0]Al[3952,13,3953,0,3317,1326,0]Ak[3952,13,3953,0,485,1326,0]Ak[3952,13,3953,0,119,1326,0]Al[3952,13,3953,0,2422,1326,0]Ak[3952,13,3705,0,113,1326,0]Al[3952,13,3705,0,3116,1326,0]Al[3952,13,3705,0,1328,1326,0]Al[3952,13,3705,0,3992,1326,0]Al[3952,13,3705,0,1992,1326,0]Al[3952,13,3705,0,4284,1326,0]Al[3952,13,1923,0,4929,1326,0]Al[3952,13,1923,0,1341,1326,0]Ao[4735,13,4736,1960,1604,1326,0]Ao[4735,13,4736,1960,4934,1326,0]Ao[4735,13,4736,3173,4778,1326,0]Ao[4735,13,4736,1526,1587,1326,0]Ao[4735,13,4736,1526,4162,1326,0]Ao[4735,13,4736,1526,3329,1326,0]Ao[4735,13,4736,1526,3144,1326,0]Ao[4735,13,4736,1526,4832,1326,0]Ao[4735,13,4736,1526,3304,1326,0]Ao[4735,13,4736,1542,2435,1326,0]Ao[4735,13,4736,1542,3315,1326,0]Ao[4735,13,4736,1542,3146,1326,0]Ao[4735,13,4736,1542,4500,1326,0]Ao[4735,13,4736,1075,3149,1326,0]Ao[4735,13,4736,1075,4292,1326,0]Ao[4735,13,4736,1080,2019,1326,0]Ao[4735,13,4736,1080,3354,1326,0]Ao[4735,13,4736,1080,3996,1326,0]Ao[4735,13,4736,2152,4402,1326,0]Ao[4735,13,4736,2152,4128,1326,0]Ao[4735,13,4736,3711,4496,1326,0]Ao[4735,13,4736,3711,3141,1326,0]Ao[4735,13,4736,3711,4368,1326,0]Ao[4735,13,4736,3711,5017,1326,0]Ao[4735,13,4736,3291,4517,1326,0]Ao[4735,13,4736,3291,4802,1326,0]Ao[4735,13,4736,3291,4817,1326,0]Ao[4735,13,4736,3291,3998,1326,0]Ao[4735,13,4736,1842,4378,1326,0]Ao[4735,13,4736,1842,4168,1326,0]Ao[4735,13,4736,1842,4991,1326,0]Ao[4735,13,4736,1842,4120,1326,0]Ao[4735,13,4736,1842,3988,1326,0]Ao[4735,13,4736,1516,3129,1326,0]Ao[4735,13,4736,1516,3321,1326,0]Ao[4735,13,4736,1516,3343,1326,0]Ao[4735,13,4736,1516,4135,1326,0]Ao[4735,13,4736,1516,4494,1326,0]Ao[4735,13,4736,1516,4682,1326,0]Ao[4735,13,4736,1516,2393,1326,0]Ao[4735,13,4736,2445,4686,1326,0]Ao[4735,13,4736,2445,4131,1326,0]Ao[4735,13,4736,1907,4508,1326,0]Ao[4735,13,4736,1907,5020,1326,0]Ao[4735,13,4736,1907,2406,1326,0]Ao[4735,13,4736,1907,4776,1326,0]Ao[4735,13,4736,1934,4134,1326,0]Ao[4735,13,4736,1934,4139,1326,0]Al[4735,13,4736,0,4406,1326,0]Al[4735,13,4736,0,4836,1326,0]Ao[4735,13,4736,4305,3318,1326,0]Ao[4735,13,4736,4305,5113,1326,0]An[4735,13,4736,4736,486,1326,0]An[4735,13,4736,4736,120,1326,0]Ao[4735,13,4736,4736,2423,1326,0]Ao[4735,13,4736,4736,3127,1326,0]Ao[4735,13,1840,1840,4298,1326,0]Ao[4735,13,4975,4975,4876,1326,0]Ao[4735,13,4975,4975,1343,1326,0]Ao[4735,13,1924,1924,4930,1326,0]Al[4735,13,1924,0,1342,1326,0]Am[4735,13,3706,135,114,1326,0]An[4735,13,3706,135,3117,1326,0]Ao[4735,13,3706,1511,1329,1326,0]Ao[4735,13,3706,1511,3993,1326,0]Ao[4735,13,3706,2493,1993,1326,0]Ao[4735,13,3706,2493,4285,1326,0]Ao[4735,13,2123,2123,1347,1326,0]Ao[4441,13,4442,4442,2420,1326,0]Ao[4441,13,4442,4442,2433,1326,0]Ao[4441,13,4442,4442,4140,1326,0]Ao[1050,13,1051,1051,4870,1326,0]Ao[1050,13,1541,1541,4504,1326,0]Al[3952,13,3953,3710,3308,0,0]Al[3952,13,3953,2444,2399,0,0]Ak[3952,13,3953,1049,478,0,0]Al[4735,13,4736,3711,3309,0,0]Al[3952,13,3953,0,4133,1332,0]Ao[4735,13,4736,1934,4134,1332,0]Ai[4735,14,3142,0,4275,0,0]Ai[4735,14,4736,0,4406,0,0]Ai[3952,13,3953,0,2422,0,0]Al[4735,13,4736,4736,2423,0,0]Ai[3952,13,3953,0,4138,0,0]Al[4735,13,4736,1934,4139,0,0]Ai[3952,14,1844,0,1987,0,0]Al[4735,14,1845,1845,1988,0,0]Al[4735,12,4736,1907,4807,0,0]Ao[4735,13,4736,1907,5138,4807,0]=Al[4735,13,4975,4975,4876,0,0]Ah[3952,5,3953,2444,0,0,0]Ai[3952,15,3953,0,3128,0,0]Al[4735,15,4736,1516,3129,0,0]Al[1365,15,1366,1366,3984,0,0]0Al[4735,15,4736,1526,3329,0,0]Ah[4735,5,4736,1075,0,0,0]Ah[3952,15,3953,0,485,0,0]Ak[4735,15,4736,4736,486,0,0]Al[3972,15,3973,3973,2390,0,0]Al[4735,15,4736,1526,3144,0,0]Al[4735,15,2123,2123,1344,0,0]98Ah[3952,5,3953,3710,0,0,0]2Al[4735,15,4736,2152,4014,0,0]Ah[3952,5,3953,1526,0,0,0]Ah[4735,5,4736,1526,0,0,0]Ae[4735,8,1840,0,0,0,0]987>Al[4735,15,4736,3711,1346,0,0]>Ah[3952,5,3953,1960,0,0,0]Ah[4735,5,4736,1960,0,0,0]Al[3952,13,3953,2444,2399,0,0]Al[3952,13,3953,2471,2403,0,0]Al[4735,13,4736,2152,2012,0,0]Al[4735,13,4736,2445,2400,0,0]An[1365,13,1366,1366,127,1564,0]Ao[1365,13,1366,1366,3984,1564,0]Ao[1365,13,1540,1540,4015,1564,0]Ao[1365,13,1540,1540,1361,1564,0]Ao[1365,13,1540,1540,4403,1564,0]Al[495,13,496,496,3985,1564,0]Ao[4021,13,4022,4022,5021,1564,0]Ao[3952,13,3953,3710,4521,1564,0]Ao[3952,13,3953,3710,2413,1564,0]Al[3952,13,3953,0,4256,1564,0]Ao[3952,13,3953,1049,2414,1564,0]Ao[3952,13,3953,1049,3119,1564,0]Ao[3952,13,3953,1049,4678,1564,0]Ao[3952,13,3953,3710,3306,1564,0]Ao[3952,13,3953,3710,3308,1564,0]Ao[3952,13,3953,3710,1995,1564,0]Ao[3952,13,3953,3710,1579,1564,0]Ao[3952,13,3953,3710,3109,1564,0]Ao[3952,13,3953,3710,2387,1564,0]Ao[3952,13,3953,3710,4158,1564,0]Ao[3952,13,3953,3710,4163,1564,0]Ao[3952,13,3953,3710,4936,1564,0]Ao[3952,13,3953,3710,4777,1564,0]Ao[3952,13,3953,2444,2399,1564,0]Ao[3952,13,3953,2444,4265,1564,0]Ao[3952,13,3953,2444,1558,1564,0]Ao[3952,13,3953,2471,2403,1564,0]Ao[3952,13,3953,2471,3336,1564,0]Ao[3952,13,3953,2471,4382,1564,0]An[3952,13,3953,1049,478,1564,0]Ao[3952,13,3953,4088,3333,1564,0]Ao[3952,13,3953,4088,4877,1564,0]Ao[3952,13,3953,4088,4401,1564,0]Ao[3952,13,3953,4088,3148,1564,0]Ao[3952,13,3953,4088,3335,1564,0]Ao[3952,13,3953,4088,4400,1564,0]Ao[3952,13,3953,1552,4173,1564,0]Ao[3952,13,3953,1092,4010,1564,0]Ao[3952,13,3953,4093,3339,1564,0]Ao[3952,13,3953,4093,3353,1564,0]Ao[3952,13,3953,4093,4172,1564,0]Al[3952,13,3953,0,3330,1564,0]Al[3952,13,3953,0,4296,1564,0]Al[3952,13,3953,0,4513,1564,0]Al[3952,13,3953,0,4150,1564,0]Al[3952,13,3953,0,4391,1564,0]Al[3952,13,3953,0,1603,1564,0]Al[3952,13,3953,0,4933,1564,0]Al[3952,13,3953,0,1324,1564,0]Al[3952,13,3953,0,1586,1564,0]Al[3952,13,3953,0,2434,1564,0]Al[3952,13,3953,0,3314,1564,0]Al[3952,13,3953,0,3145,1564,0]Al[3952,13,3953,0,4499,1564,0]Al[3952,13,3953,0,2018,1564,0]Al[3952,13,3953,0,4124,1564,0]Al[3952,13,3953,0,4495,1564,0]Al[3952,13,3953,0,4509,1564,0]Al[3952,13,3953,0,4516,1564,0]Al[3952,13,3953,0,4801,1564,0]Al[3952,13,3953,0,4816,1564,0]Al[3952,13,3953,0,3997,1564,0]Al[3952,13,3953,0,4377,1564,0]Al[3952,13,3953,0,4166,1564,0]Al[3952,13,3953,0,4990,1564,0]Al[3952,13,3953,0,4118,1564,0]Al[3952,13,3953,0,3987,1564,0]Al[3952,13,3953,0,3128,1564,0]Al[3952,13,3953,0,4492,1564,0]Al[3952,13,3953,0,4680,1564,0]Al[3952,13,3953,0,4130,1564,0]Al[3952,13,3953,0,4133,1564,0]Al[3952,13,3953,0,4138,1564,0]Al[3952,13,3953,0,4835,1564,0]Al[3952,13,3953,0,3317,1564,0]Al[3952,13,3953,0,4711,1564,0]Ak[3952,13,3953,0,485,1564,0]Ak[3952,13,3953,0,119,1564,0]Al[3952,13,3953,0,2422,1564,0]Al[3952,13,3953,0,2431,1564,0]Ak[3952,13,3705,0,113,1564,0]Al[3952,13,3705,0,3116,1564,0]Al[3952,13,3705,0,1328,1564,0]Al[3952,13,3705,0,3992,1564,0]Al[3952,13,3705,0,1992,1564,0]Al[3952,13,3705,0,4284,1564,0]Al[3952,13,3705,0,4769,1564,0]Al[3952,13,1844,0,1987,1564,0]Ak[3952,13,1844,0,472,1564,0]Al[3952,13,1844,0,5079,1564,0]Al[3952,13,1844,0,3990,1564,0]Al[3952,13,1844,0,4839,1564,0]Al[3952,13,1923,0,3348,1564,0]Al[3952,13,1923,0,4267,1564,0]Al[3952,13,1923,0,4707,1564,0]Al[3952,13,1923,0,4709,1564,0]Al[3952,13,1923,0,4929,1564,0]Al[3952,13,1923,0,1341,1564,0]Ao[4735,13,4736,4526,4257,1564,0]Ao[4735,13,4736,3173,3123,1564,0]Ao[4735,13,4736,1075,1566,1564,0]Ao[4735,13,4736,2152,2012,1564,0]Ao[4735,13,4736,2152,3334,1564,0]Ao[4735,13,4736,2152,3340,1564,0]Ao[4735,13,4736,3711,3309,1564,0]Ao[4735,13,4736,1071,3331,1564,0]Ao[4735,13,4736,1842,4297,1564,0]Ao[4735,13,4736,2445,2400,1564,0]Ao[4735,13,4736,2445,4263,1564,0]Ao[4735,13,4736,1907,5138,1564,0]Ao[4735,13,4736,4305,5108,1564,0]Ao[4735,13,4736,1551,4514,1564,0]Ao[4735,13,4736,1551,4151,1564,0]Ao[4735,13,4736,1551,4392,1564,0]Ao[4735,13,4736,1960,1604,1564,0]Ao[4735,13,4736,1960,4934,1564,0]Ao[4735,13,4736,3164,1325,1564,0]Ao[4735,13,4736,3173,4778,1564,0]Ao[4735,13,4736,1526,1587,1564,0]Ao[4735,13,4736,1526,4162,1564,0]Ao[4735,13,4736,1526,3329,1564,0]Ao[4735,13,4736,1526,3144,1564,0]Ao[4735,13,4736,1526,4832,1564,0]Ao[4735,13,4736,1526,3304,1564,0]Ao[4735,13,4736,1542,2435,1564,0]Ao[4735,13,4736,1542,3315,1564,0]Ao[4735,13,4736,1542,3146,1564,0]Ao[4735,13,4736,1542,4500,1564,0]Ao[4735,13,4736,1075,3149,1564,0]Ao[4735,13,4736,1075,4292,1564,0]Ao[4735,13,4736,1080,2019,1564,0]Ao[4735,13,4736,1080,3354,1564,0]Ao[4735,13,4736,1080,3996,1564,0]Ao[4735,13,4736,2152,4014,1564,0]Ao[4735,13,4736,2152,4402,1564,0]Ao[4735,13,4736,2152,4128,1564,0]Ao[4735,13,4736,3711,4496,1564,0]Ao[4735,13,4736,3711,3141,1564,0]Ao[4735,13,4736,3711,4368,1564,0]Ao[4735,13,4736,3711,5017,1564,0]Ao[4735,13,4736,3711,1346,1564,0]Ao[4735,13,4736,1071,4510,1564,0]Ao[4735,13,4736,3291,4517,1564,0]Ao[4735,13,4736,3291,4802,1564,0]Ao[4735,13,4736,3291,4817,1564,0]Ao[4735,13,4736,3291,3998,1564,0]Ao[4735,13,4736,1842,4378,1564,0]Ao[4735,13,4736,1842,4168,1564,0]Ao[4735,13,4736,1842,4991,1564,0]Ao[4735,13,4736,1842,4120,1564,0]Ao[4735,13,4736,1842,3988,1564,0]Ao[4735,13,4736,1516,3129,1564,0]Ao[4735,13,4736,1516,4383,1564,0]Ao[4735,13,4736,1516,3321,1564,0]Ao[4735,13,4736,1516,3343,1564,0]Ao[4735,13,4736,1516,4135,1564,0]Ao[4735,13,4736,1516,4494,1564,0]Ao[4735,13,4736,1516,4682,1564,0]Ao[4735,13,4736,1516,2393,1564,0]Ao[4735,13,4736,2445,4686,1564,0]Ao[4735,13,4736,2445,2404,1564,0]Ao[4735,13,4736,2445,4131,1564,0]Ao[4735,13,4736,2445,3982,1564,0]Ao[4735,13,4736,2445,3337,1564,0]Ao[4735,13,4736,1907,4287,1564,0]Ao[4735,13,4736,1907,4508,1564,0]Ao[4735,13,4736,1907,5020,1564,0]Ao[4735,13,4736,1907,2406,1564,0]Ao[4735,13,4736,1907,4776,1564,0]Ao[4735,13,4736,1934,4498,1564,0]Ao[4735,13,4736,1934,4264,1564,0]Ao[4735,13,4736,1934,4134,1564,0]Ao[4735,13,4736,1934,4139,1564,0]Al[4735,13,4736,0,4406,1564,0]Al[4735,13,4736,0,4836,1564,0]Ao[4735,13,4736,4305,3318,1564,0]Ao[4735,13,4736,4305,5113,1564,0]Ao[4735,13,4736,4866,4712,1564,0]An[4735,13,4736,4736,486,1564,0]An[4735,13,4736,4736,120,1564,0]Ao[4735,13,4736,4736,2423,1564,0]Ao[4735,13,4736,4736,2432,1564,0]Ao[4735,13,4736,4736,3127,1564,0]Ao[4735,13,1840,1840,4298,1564,0]Ao[4735,13,4975,4975,4876,1564,0]Ao[4735,13,4975,4975,1343,1564,0]Ao[4735,13,1845,1845,1988,1564,0]An[4735,13,1845,1845,473,1564,0]Ao[4735,13,1845,1845,5080,1564,0]Ao[4735,13,1845,1845,3991,1564,0]Ao[4735,13,1845,1845,4842,1564,0]Ao[4735,13,1924,1924,3349,1564,0]Ao[4735,13,1924,1924,4268,1564,0]Ao[4735,13,1924,1924,4708,1564,0]Ao[4735,13,1924,1924,4710,1564,0]Ao[4735,13,1924,1924,4930,1564,0]Al[4735,13,1924,0,1342,1564,0]Am[4735,13,3706,135,114,1564,0]An[4735,13,3706,135,3117,1564,0]Ao[4735,13,3706,1511,1329,1564,0]Ao[4735,13,3706,1511,3993,1564,0]Ao[4735,13,3706,2493,1993,1564,0]Ao[4735,13,3706,2493,4285,1564,0]Ao[4735,13,3706,3706,4770,1564,0]Ao[4735,13,2123,2123,1344,1564,0]Ao[4735,13,2123,2123,1347,1564,0]Ao[4441,13,4442,4442,3352,1564,0]An[4441,13,4442,393,4407,1564,0]Ao[4441,13,4442,4442,2420,1564,0]Ao[4441,13,4442,4442,2433,1564,0]Ao[4441,13,4442,4442,4140,1564,0]Ao[1050,13,1051,1051,3120,1564,0]Ao[1050,13,1051,1051,4870,1564,0]An[1050,13,1834,1834,128,1564,0]Ao[1050,13,1541,1541,1362,1564,0]Ao[1050,13,1541,1541,4016,1564,0]Ao[1050,13,1541,1541,4504,1564,0]Ao[1050,13,4225,4225,5065,1564,0]Ao[1050,13,4225,4225,5066,1564,0]An[1050,13,1929,1929,129,1564,0]An[3972,13,3973,3973,118,1564,0]Ao[3972,13,3973,3973,2390,1564,0]Ai[3952,14,3953,0,2018,0,0]Al[4735,14,4736,1080,2019,0,0]Ak[1365,14,1366,1366,127,0,0]Ac[495,7,496,0,0,0,0]Al[3952,13,3953,2444,2399,0,0]Ae[1050,2,1051,0,0,0,0]Ae[4796,7,4797,0,0,0,0]Al[3952,13,3953,3710,3308,0,0]Ae[4735,2,4736,0,0,0,0]Al[4735,13,4736,2445,2400,0,0]Ah[3952,2,3953,4736,0,0,0]Ai[3952,13,3953,0,4835,0,0]Ai[3952,14,3953,0,3314,0,0]Ai[3952,14,3953,0,4166,0,0]5Ai[4735,13,4736,0,4836,0,0]Al[4735,14,4736,1542,3315,0,0]Al[4735,14,4736,1842,4168,0,0]Ai[4735,14,4736,0,4406,0,0]Al[3952,13,3953,0,1603,3124,0]Al[3952,13,3953,0,1586,3124,0]Al[3952,13,3953,0,3128,3124,0]Al[3952,13,3953,0,4835,3124,0]Al[3952,13,3953,0,2422,3124,0]Ao[4735,13,4736,1960,1604,3124,0]Ao[4735,13,4736,1526,1587,3124,0]Ao[4735,13,4736,1516,3129,3124,0]Ao[4735,13,4736,1516,3321,3124,0]Al[4735,13,4736,1907,4508,0,0]Al[4735,13,4736,0,4836,3124,0]Ao[4735,13,4736,4736,2423,3124,0]Al[4735,12,1840,1840,3124,0,0]Al[4441,14,4442,4442,2420,0,0]Al[3952,13,3953,0,1603,3125,0]Al[3952,13,3953,0,1586,3125,0]Al[3952,13,3953,0,3128,3125,0]Al[3952,13,3953,0,4835,3125,0]Al[3952,13,3953,0,2422,3125,0]Ao[4735,13,4736,1960,1604,3125,0]Ao[4735,13,4736,1526,1587,3125,0]Ao[4735,13,4736,1516,3129,3125,0]Ao[4735,13,4736,1516,3321,3125,0]Ao[4735,13,4736,1516,2393,3125,0]Al[4735,13,4736,0,4836,3125,0]Ao[4735,13,4736,4736,2423,3125,0]Al[4735,12,1840,1840,3125,0,0]Al[3952,13,3953,4093,3339,0,0]Al[4735,13,4736,2152,3340,0,0]Al[1050,13,1541,1541,4016,0,0]Al[4735,13,4736,2445,2400,0,0]Ah[3952,14,1844,0,472,0,0]Ak[4735,14,1845,1845,473,0,0]Al[3952,13,3953,2471,2403,0,0]3Ai[3952,12,3953,0,3350,0,0]Al[3952,13,3953,0,4391,3350,0]Al[3952,13,3953,0,4711,3350,0]Al[4735,12,4736,1551,3351,0,0]Ao[4735,13,4736,1551,4392,3351,0]Ao[4735,13,4736,4866,4712,3351,0]Al[1050,14,1051,1051,3120,0,0]Ai[3952,14,3953,0,1324,0,0]Al[4735,14,4736,3164,1325,0,0]Ai[3952,13,3705,0,3310,0,0]Ak[3952,13,3705,0,113,3310,0]Al[3952,13,3705,0,1328,3310,0]Al[3952,13,3705,0,1992,3310,0]Al[4735,13,3706,3706,3311,0,0]Am[4735,13,3706,135,114,3311,0]Ao[4735,13,3706,1511,1329,3311,0]Ao[4735,13,3706,2493,1993,3311,0]Ah[3952,14,1844,0,472,0,0]Ak[4735,14,1845,1845,473,0,0]Ai[4735,14,4507,0,4715,0,0]Ai[3952,14,3953,0,4492,0,0]Ai[3952,14,3953,0,4680,0,0]Al[4735,14,4736,1516,4494,0,0]Al[4735,14,4736,1516,4682,0,0]Ai[4735,14,4736,0,4406,0,0]Ai[4735,14,1345,0,4370,0,0]Al[3952,13,3953,2444,2399,0,0]Al[3952,13,3953,2471,2403,0,0]Al[4735,13,4736,2445,2400,0,0]Ai[3952,14,1844,0,1987,0,0]Al[4735,14,4736,4736,3127,0,0]Al[4735,14,1845,1845,1988,0,0]Ae[3952,4,3953,0,0,0,0]Ah[3952,2,1844,1845,0,0,0]Ae[4735,4,4736,0,0,0,0]Ae[4735,2,1845,0,0,0,0]Ac[495,7,496,0,0,0,0]:Ae[1050,2,1051,0,0,0,0]Ae[4796,7,4797,0,0,0,0]Ao[3952,13,3953,3710,4777,1334,0]Ao[3952,13,3953,4088,4401,1334,0]Ao[3952,13,3953,4088,3148,1334,0]Ao[3952,13,3953,4088,3335,1334,0]Ao[3952,13,3953,4088,4400,1334,0]Ao[3952,13,3953,4093,3353,1334,0]Ao[3952,13,3953,4093,4172,1334,0]Al[3952,13,3953,0,4495,1334,0]Ao[4735,13,4736,3711,4496,1334,0]Ai[3952,14,3953,0,2431,0,0]Al[4735,14,4736,4736,2432,0,0]Al[4441,14,4442,4442,2433,0,0]Ak[3952,13,3953,1049,478,0,0]Ai[4735,14,3142,0,4159,0,0]Ai[3952,14,3953,0,3314,0,0]Al[4735,14,4736,1542,3315,0,0]Al[4441,14,4442,4442,2420,0,0]Ai[3952,14,3953,0,3145,0,0]Ai[3952,14,3953,0,4499,0,0]Al[4735,14,4736,1542,3146,0,0]Al[4735,14,4736,1542,4500,0,0]3210Ai[3952,12,1844,0,1983,0,0]Al[4735,12,1845,1845,1984,0,0]Ai[3952,12,3705,0,3310,0,0]Ak[3952,13,3705,0,113,3310,0]Al[3952,13,3705,0,1328,3310,0]Al[3952,13,3705,0,1992,3310,0]Al[4735,12,3706,3706,3311,0,0]Am[4735,13,3706,135,114,3311,0]Ao[4735,13,3706,1511,1329,3311,0]Ao[4735,13,3706,2493,1993,3311,0]Ai[3952,10,1844,1845,0,0,0]Af[4735,10,1845,0,0,0,0]Al[4735,15,4736,3173,4778,0,0]Al[4735,15,4736,1075,3149,0,0]Al[4735,15,4736,1080,3354,0,0]Ai[3952,15,1923,0,1341,0,0]Ai[4735,15,1924,0,1342,0,0]65Al[4735,15,4736,1907,4508,0,0]Ah[3952,5,1844,1845,0,0,0]Ae[4735,5,1845,0,0,0,0]Ai[3952,15,3953,0,3128,0,0]Al[4735,15,4736,1516,3129,0,0]Ah[3952,5,3705,2493,0,0,0]Ah[4735,5,3706,2493,0,0,0]Al[1050,15,1051,1051,4870,0,0]Ah[3952,5,3953,3710,0,0,0]5454Ai[3952,10,3953,2144,0,0,0]Ai[3952,10,1844,1845,0,0,0]Af[4735,10,1845,0,0,0,0]Al[3952,15,3953,4093,3353,0,0]?Al[4735,15,4736,1080,3996,0,0]Al[4735,15,4736,2152,4014,0,0]Ah[4735,5,4736,2152,0,0,0]Al[3952,15,3953,4088,4401,0,0]Al[4735,15,4736,1516,3321,0,0]Ah[3952,5,3953,1080,0,0,0]Ah[4735,5,4736,1080,0,0,0]Al[4735,15,4736,1907,2406,0,0]Ai[3952,12,3953,0,4981,0,0]Al[4735,12,4736,4305,4982,0,0]Ao[4735,13,4736,4305,5108,4982,0]Ao[4735,13,4736,4305,5113,4982,0]Ak[1365,14,1366,1366,127,0,0]An[1365,13,1366,1366,127,4369,0]Ao[1050,13,1051,1051,3120,4369,0]An[1050,13,1834,1834,128,4369,0]An[1050,13,1929,1929,129,4369,0]An[3972,13,3973,3973,118,4369,0]Ak[3972,14,3973,3973,118,0,0]Al[3952,13,3953,0,1603,3305,0]Al[3952,13,3953,0,1586,3305,0]Al[3952,13,3953,0,3997,3305,0]Al[3952,13,3953,0,4133,3305,0]Ak[3952,13,3953,0,485,3305,0]Ak[3952,13,3953,0,119,3305,0]Al[3952,13,3953,0,2422,3305,0]Ao[4735,13,4736,1960,1604,3305,0]Ao[4735,13,4736,3173,4778,3305,0]Ao[4735,13,4736,1526,1587,3305,0]Ao[4735,13,4736,1075,3149,3305,0]Ao[4735,13,4736,1075,4292,3305,0]Ao[4735,13,4736,1080,3354,3305,0]Ao[4735,13,4736,3291,3998,3305,0]Ao[4735,13,4736,2445,4686,3305,0]Ao[4735,13,4736,1907,4776,3305,0]Ao[4735,13,4736,1934,4134,3305,0]An[4735,13,4736,4736,486,3305,0]An[4735,13,4736,4736,120,3305,0]Ao[4735,13,4736,4736,2423,3305,0]Al[3952,13,3953,2444,2399,0,0]Al[4735,13,4736,2445,2400,0,0]Ai[3952,12,1844,0,3138,0,0]Ai[3952,12,1844,0,3131,0,0]Al[4735,12,1845,1845,3139,0,0]Al[4735,12,1845,1845,3132,0,0]Ao[1365,13,1540,1540,4015,1978,0]Ao[1365,13,1540,1540,1361,1978,0]Al[495,13,496,496,3985,1978,0]Al[3952,13,3953,0,4256,1978,0]Ao[3952,13,3953,2444,4265,1978,0]Al[3952,13,3953,0,1603,1978,0]Al[3952,13,3953,0,1586,1978,0]Al[3952,13,3953,0,2434,1978,0]Al[3952,13,3953,0,3314,1978,0]Al[3952,13,3953,0,3145,1978,0]Al[3952,13,3953,0,4499,1978,0]Al[3952,13,3953,0,2018,1978,0]Al[3952,13,3953,0,4124,1978,0]Al[3952,13,3953,0,4516,1978,0]Al[3952,13,3953,0,4801,1978,0]Al[3952,13,3953,0,4816,1978,0]Al[3952,13,3953,0,3997,1978,0]Al[3952,13,3953,0,4377,1978,0]Al[3952,13,3953,0,4990,1978,0]Al[3952,13,3953,0,4711,1978,0]Al[3952,13,3705,0,3116,1978,0]Al[3952,13,3705,0,3992,1978,0]Al[3952,13,3705,0,4284,1978,0]Al[3952,13,3705,0,4769,1978,0]Ao[4735,13,4736,4526,4257,1978,0]Ao[4735,13,4736,4305,5108,1978,0]Ao[4735,13,4736,1960,1604,1978,0]Ao[4735,13,4736,1526,1587,1978,0]Ao[4735,13,4736,1526,3329,1978,0]Ao[4735,13,4736,1526,3304,1978,0]Ao[4735,13,4736,1542,2435,1978,0]Ao[4735,13,4736,1542,3315,1978,0]Ao[4735,13,4736,1542,3146,1978,0]Ao[4735,13,4736,1542,4500,1978,0]Ao[4735,13,4736,1080,2019,1978,0]Ao[4735,13,4736,2152,4402,1978,0]Ao[4735,13,4736,2152,4128,1978,0]Ao[4735,13,4736,3291,4517,1978,0]Ao[4735,13,4736,3291,4802,1978,0]Ao[4735,13,4736,3291,4817,1978,0]Ao[4735,13,4736,3291,3998,1978,0]Ao[4735,13,4736,1842,4378,1978,0]Ao[4735,13,4736,1842,4991,1978,0]Ao[4735,13,4736,4305,5113,1978,0]Ao[4735,13,4736,4866,4712,1978,0]An[4735,13,3706,135,3117,1978,0]Ao[4735,13,3706,1511,3993,1978,0]Ao[4735,13,3706,2493,4285,1978,0]Ao[4735,13,3706,3706,4770,1978,0]Ao[4441,13,4442,4442,2420,1978,0]Ao[1050,13,1541,1541,1362,1978,0]Ao[1050,13,1541,1541,4504,1978,0]Al[4735,13,4736,1934,4264,0,0]Al[4735,13,4736,1907,4287,0,0]Al[4735,13,4736,1907,4508,0,0]Al[4735,14,4736,1907,5020,0,0]Ai[4735,14,4775,0,4837,0,0]Ah[4735,14,4507,0,474,0,0]Ai[4735,14,4507,0,1356,0,0]Ai[4735,14,4507,0,1358,0,0]Ai[4735,14,4507,0,4715,0,0]Ai[3952,12,1844,0,3138,0,0]Ai[3952,12,1844,0,3131,0,0]Al[4735,12,1845,1845,3139,0,0]Al[4735,12,1845,1845,3132,0,0]Ae[4735,2,4736,0,0,0,0]Al[3952,13,3953,4088,3333,0,0]Al[3952,13,3953,4088,4877,0,0]Ai[3952,13,3705,0,3310,0,0]Ak[3952,13,3705,0,113,3310,0]Al[3952,13,3705,0,1328,3310,0]Al[3952,13,3705,0,1992,3310,0]Al[4735,13,3706,3706,3311,0,0]Am[4735,13,3706,135,114,3311,0]Ao[4735,13,3706,1511,1329,3311,0]Ao[4735,13,3706,2493,1993,3311,0]Ai[3952,13,3953,0,4516,0,0]Al[4735,13,4736,3291,4517,0,0]9854Al[3952,13,3953,3710,3308,0,0]Al[3952,13,3953,1049,2414,0,0]Al[4735,14,4736,1907,5020,0,0]Ai[4735,14,4775,0,4837,0,0]Ah[4735,14,4507,0,474,0,0]Ai[4735,14,4507,0,1356,0,0]Ai[4735,14,4507,0,4715,0,0]Al[3952,13,3953,4093,3339,0,0]Al[4735,13,4736,2152,3340,0,0]Ak[1365,14,1366,1366,127,0,0]Al[1365,14,1540,1540,1361,0,0]Al[4735,13,4736,3711,3309,0,0]Ai[3952,12,1844,0,3138,0,0]Al[4735,12,1845,1845,3139,0,0]Am[1365,13,1366,1366,127,111,0]An[1365,13,1366,1366,3984,111,0]An[1365,13,1540,1540,4015,111,0]An[1365,13,1540,1540,1361,111,0]An[1365,13,1540,1540,4403,111,0]Ak[495,13,496,496,3985,111,0]An[4021,13,4022,4022,5021,111,0]An[3952,13,3953,3710,4521,111,0]An[3952,13,3953,3710,2413,111,0]Ak[3952,13,3953,0,4256,111,0]An[3952,13,3953,1049,2414,111,0]An[3952,13,3953,1049,3119,111,0]An[3952,13,3953,1049,4678,111,0]An[3952,13,3953,3710,3306,111,0]An[3952,13,3953,3710,3308,111,0]An[3952,13,3953,3710,1995,111,0]An[3952,13,3953,3710,1579,111,0]An[3952,13,3953,3710,3109,111,0]An[3952,13,3953,3710,2387,111,0]An[3952,13,3953,3710,4158,111,0]An[3952,13,3953,3710,4163,111,0]An[3952,13,3953,3710,4936,111,0]An[3952,13,3953,3710,4777,111,0]An[3952,13,3953,2444,2399,111,0]An[3952,13,3953,2444,4265,111,0]An[3952,13,3953,2444,1558,111,0]An[3952,13,3953,2471,2403,111,0]An[3952,13,3953,2471,3336,111,0]An[3952,13,3953,2471,4382,111,0]Am[3952,13,3953,1049,478,111,0]An[3952,13,3953,4088,3333,111,0]An[3952,13,3953,4088,4877,111,0]An[3952,13,3953,4088,4401,111,0]An[3952,13,3953,4088,3148,111,0]An[3952,13,3953,4088,3335,111,0]An[3952,13,3953,4088,4400,111,0]An[3952,13,3953,1552,4173,111,0]An[3952,13,3953,1092,4010,111,0]An[3952,13,3953,4093,3339,111,0]An[3952,13,3953,4093,3353,111,0]An[3952,13,3953,4093,4172,111,0]Ak[3952,13,3953,0,3330,111,0]Ak[3952,13,3953,0,4296,111,0]Ak[3952,13,3953,0,4513,111,0]Ak[3952,13,3953,0,4150,111,0]Ak[3952,13,3953,0,4391,111,0]Ak[3952,13,3953,0,1603,111,0]Ak[3952,13,3953,0,4933,111,0]Ak[3952,13,3953,0,1324,111,0]Ak[3952,13,3953,0,1586,111,0]Ak[3952,13,3953,0,2434,111,0]Ak[3952,13,3953,0,3314,111,0]Ak[3952,13,3953,0,3145,111,0]Ak[3952,13,3953,0,4499,111,0]Ak[3952,13,3953,0,2018,111,0]Ak[3952,13,3953,0,4124,111,0]Ak[3952,13,3953,0,4495,111,0]Ak[3952,13,3953,0,4509,111,0]Ak[3952,13,3953,0,4516,111,0]Ak[3952,13,3953,0,4801,111,0]Ak[3952,13,3953,0,4816,111,0]Ak[3952,13,3953,0,3997,111,0]Ak[3952,13,3953,0,4377,111,0]Ak[3952,13,3953,0,4166,111,0]Ak[3952,13,3953,0,4990,111,0]Ak[3952,13,3953,0,4118,111,0]Ak[3952,13,3953,0,3987,111,0]Ak[3952,13,3953,0,3128,111,0]Ak[3952,13,3953,0,4492,111,0]Ak[3952,13,3953,0,4680,111,0]Ak[3952,13,3953,0,4130,111,0]Ak[3952,13,3953,0,4133,111,0]Ak[3952,13,3953,0,4138,111,0]Ak[3952,13,3953,0,4835,111,0]Ak[3952,13,3953,0,3317,111,0]Ak[3952,13,3953,0,4711,111,0]Aj[3952,13,3953,0,485,111,0]Aj[3952,13,3953,0,119,111,0]Ak[3952,13,3953,0,2422,111,0]Ak[3952,13,3953,0,2431,111,0]Aj[3952,13,3705,0,113,111,0]Ak[3952,13,3705,0,3116,111,0]Ak[3952,13,3705,0,1328,111,0]Ak[3952,13,3705,0,3992,111,0]Ak[3952,13,3705,0,1992,111,0]Ak[3952,13,3705,0,4284,111,0]Ak[3952,13,3705,0,4769,111,0]Ak[3952,13,1844,0,1987,111,0]Aj[3952,13,1844,0,472,111,0]Ak[3952,13,1844,0,5079,111,0]Ak[3952,13,1844,0,3990,111,0]Ak[3952,13,1844,0,4839,111,0]Ak[3952,13,1923,0,3348,111,0]Ak[3952,13,1923,0,4267,111,0]Ak[3952,13,1923,0,4707,111,0]Ak[3952,13,1923,0,4709,111,0]Ak[3952,13,1923,0,4929,111,0]Ak[3952,13,1923,0,1341,111,0]An[4735,13,4736,4526,4257,111,0]An[4735,13,4736,3173,3123,111,0]An[4735,13,4736,1075,1566,111,0]An[4735,13,4736,2152,2012,111,0]An[4735,13,4736,2152,3334,111,0]An[4735,13,4736,2152,3340,111,0]An[4735,13,4736,3711,3309,111,0]An[4735,13,4736,1071,3331,111,0]An[4735,13,4736,1842,4297,111,0]An[4735,13,4736,2445,2400,111,0]An[4735,13,4736,2445,4263,111,0]An[4735,13,4736,1907,5138,111,0]An[4735,13,4736,4305,5108,111,0]An[4735,13,4736,1551,4514,111,0]An[4735,13,4736,1551,4151,111,0]An[4735,13,4736,1551,4392,111,0]An[4735,13,4736,1960,1604,111,0]An[4735,13,4736,1960,4934,111,0]An[4735,13,4736,3164,1325,111,0]An[4735,13,4736,3173,4778,111,0]An[4735,13,4736,1526,1587,111,0]An[4735,13,4736,1526,4162,111,0]An[4735,13,4736,1526,3329,111,0]An[4735,13,4736,1526,3144,111,0]An[4735,13,4736,1526,4832,111,0]An[4735,13,4736,1526,3304,111,0]An[4735,13,4736,1542,2435,111,0]An[4735,13,4736,1542,3315,111,0]An[4735,13,4736,1542,3146,111,0]An[4735,13,4736,1542,4500,111,0]An[4735,13,4736,1075,3149,111,0]An[4735,13,4736,1075,4292,111,0]An[4735,13,4736,1080,2019,111,0]An[4735,13,4736,1080,3354,111,0]An[4735,13,4736,1080,3996,111,0]An[4735,13,4736,2152,4014,111,0]An[4735,13,4736,2152,4402,111,0]An[4735,13,4736,2152,4128,111,0]An[4735,13,4736,3711,4496,111,0]An[4735,13,4736,3711,3141,111,0]An[4735,13,4736,3711,4368,111,0]An[4735,13,4736,3711,5017,111,0]An[4735,13,4736,3711,1346,111,0]An[4735,13,4736,1071,4510,111,0]An[4735,13,4736,3291,4517,111,0]An[4735,13,4736,3291,4802,111,0]An[4735,13,4736,3291,4817,111,0]An[4735,13,4736,3291,3998,111,0]An[4735,13,4736,1842,4378,111,0]An[4735,13,4736,1842,4168,111,0]An[4735,13,4736,1842,4991,111,0]An[4735,13,4736,1842,4120,111,0]An[4735,13,4736,1842,3988,111,0]An[4735,13,4736,1516,3129,111,0]An[4735,13,4736,1516,4383,111,0]An[4735,13,4736,1516,3321,111,0]An[4735,13,4736,1516,3343,111,0]An[4735,13,4736,1516,4135,111,0]An[4735,13,4736,1516,4494,111,0]An[4735,13,4736,1516,4682,111,0]An[4735,13,4736,1516,2393,111,0]An[4735,13,4736,2445,4686,111,0]An[4735,13,4736,2445,2404,111,0]An[4735,13,4736,2445,4131,111,0]An[4735,13,4736,2445,3982,111,0]An[4735,13,4736,2445,3337,111,0]An[4735,13,4736,1907,4287,111,0]An[4735,13,4736,1907,4508,111,0]An[4735,13,4736,1907,5020,111,0]An[4735,13,4736,1907,2406,111,0]An[4735,13,4736,1907,4776,111,0]An[4735,13,4736,1934,4498,111,0]An[4735,13,4736,1934,4264,111,0]An[4735,13,4736,1934,4134,111,0]An[4735,13,4736,1934,4139,111,0]Ak[4735,13,4736,0,4406,111,0]Ak[4735,13,4736,0,4836,111,0]An[4735,13,4736,4305,3318,111,0]An[4735,13,4736,4305,5113,111,0]An[4735,13,4736,4866,4712,111,0]Am[4735,13,4736,4736,486,111,0]Am[4735,13,4736,4736,120,111,0]An[4735,13,4736,4736,2423,111,0]An[4735,13,4736,4736,2432,111,0]An[4735,13,4736,4736,3127,111,0]An[4735,13,1840,1840,4298,111,0]An[4735,13,4975,4975,4876,111,0]An[4735,13,4975,4975,1343,111,0]An[4735,13,1845,1845,1988,111,0]Am[4735,13,1845,1845,473,111,0]An[4735,13,1845,1845,5080,111,0]An[4735,13,1845,1845,3991,111,0]An[4735,13,1845,1845,4842,111,0]An[4735,13,1924,1924,3349,111,0]An[4735,13,1924,1924,4268,111,0]An[4735,13,1924,1924,4708,111,0]An[4735,13,1924,1924,4710,111,0]An[4735,13,1924,1924,4930,111,0]Ak[4735,13,1924,0,1342,111,0]Al[4735,13,3706,135,114,111,0]Am[4735,13,3706,135,3117,111,0]An[4735,13,3706,1511,1329,111,0]An[4735,13,3706,1511,3993,111,0]An[4735,13,3706,2493,1993,111,0]An[4735,13,3706,2493,4285,111,0]An[4735,13,3706,3706,4770,111,0]An[4735,13,2123,2123,1344,111,0]An[4735,13,2123,2123,1347,111,0]An[4441,13,4442,4442,3352,111,0]Am[4441,13,4442,393,4407,111,0]An[4441,13,4442,4442,2420,111,0]An[4441,13,4442,4442,2433,111,0]An[4441,13,4442,4442,4140,111,0]An[1050,13,1051,1051,3120,111,0]An[1050,13,1051,1051,4870,111,0]Am[1050,13,1834,1834,128,111,0]An[1050,13,1541,1541,1362,111,0]An[1050,13,1541,1541,4016,111,0]An[1050,13,1541,1541,4504,111,0]An[1050,13,4225,4225,5065,111,0]An[1050,13,4225,4225,5066,111,0]Am[1050,13,1929,1929,129,111,0]Am[3972,13,3973,3973,118,111,0]An[3972,13,3973,3973,2390,111,0]Al[3952,13,3953,3710,4936,0,0]Ai[3952,13,3953,0,4118,0,0]Al[4735,13,4736,1842,4120,0,0]Al[4735,14,4736,1907,5020,0,0]Ai[4735,14,4775,0,4837,0,0]Ah[4735,14,4507,0,474,0,0]Ai[4735,14,4507,0,1356,0,0]Ah[3952,5,3953,3710,0,0,0]Al[1365,15,1366,1366,3984,0,0]Ae[3972,6,3973,0,0,0,0]Ai[3952,15,3953,0,3128,0,0]Al[4735,15,4736,1516,3129,0,0]Ah[4735,5,4736,1516,0,0,0]Al[3952,15,3953,3710,4777,0,0]Al[4735,15,4736,3173,4778,0,0]Ah[3952,5,3953,2444,0,0,0]Ah[4735,5,4736,2445,0,0,0]Al[4735,15,4736,2152,4014,0,0]Ah[3952,5,3953,2471,0,0,0]2Ah[4735,6,4736,1907,0,0,0]Ai[3952,19,3953,0,1603,0,0]Al[4735,19,4736,1960,1604,0,0]Ai[3952,19,3953,0,4835,0,0]Ai[4735,19,4736,0,4836,0,0]=Ai[3952,12,1844,0,4167,0,0]>Al[3952,13,1923,0,4709,4167,0]>Al[4735,12,1845,1845,4169,0,0]>Ao[4735,13,1924,1924,4710,4169,0]Ao[1050,13,4225,4225,5066,4169,0]An[3972,13,3973,3973,118,3999,0]Ao[3972,13,3973,3973,2390,3999,0]Ai[3952,14,3953,0,3145,0,0]Al[4735,14,4736,1542,3146,0,0]Ai[3952,13,3953,0,4166,0,0]Al[4735,13,4736,1842,4168,0,0]Al[3952,13,3953,2144,4365,0,0]0>=Al[1050,13,1541,1541,4504,0,0]Ae[1050,2,1051,0,0,0,0]Al[3952,13,3953,2471,2403,0,0]Al[4735,13,4736,2445,2404,0,0]Ai[3952,14,3953,0,3314,0,0]Al[4735,14,4736,1542,3315,0,0]Al[4735,13,4736,3711,3309,0,0]Ai[3952,13,3953,0,4516,0,0]Al[4735,13,4736,3291,4517,0,0]43Ai[3952,14,3953,0,2431,0,0]Al[4735,14,4736,4736,2432,0,0]Al[4441,14,4442,4442,2433,0,0]Al[4735,13,4736,2152,2012,0,0]Al[4735,13,4736,4305,5108,0,0]Al[4735,13,4736,4305,5113,0,0]Al[3952,13,3953,4093,3339,0,0]Al[4735,13,4736,2152,3340,0,0]<;Al[1365,14,1540,1540,1361,0,0]Ai[495,14,496,496,3985,0,0]Ai[3952,13,1923,0,4707,0,0]Ai[3952,13,1923,0,4709,0,0]Al[4735,13,1924,1924,4708,0,0]Al[4735,13,1924,1924,4710,0,0]76Ai[3952,15,1923,0,1341,0,0]Ai[4735,15,1924,0,1342,0,0]Ah[3952,5,3953,4526,0,0,0]Ah[4735,5,4736,4526,0,0,0]Ai[3952,15,3953,0,4495,0,0]Ai[3952,15,3953,0,4130,0,0]Al[4735,15,4736,3711,4496,0,0]Al[4735,15,4736,2445,4131,0,0]Ah[4735,6,4736,2445,0,0,0]Ah[4735,5,4736,1934,0,0,0]Ah[3952,5,3953,2444,0,0,0]Al[3952,15,3953,4088,4400,0,0]Ah[3952,5,1923,1924,0,0,0]Ae[4735,5,1924,0,0,0,0]Ai[3952,15,3953,0,3128,0,0]Al[4735,15,4736,1516,3129,0,0]Ai[3952,19,3953,0,4835,0,0]Ai[4735,19,4736,0,4836,0,0]Al[4735,15,4736,1907,4776,0,0]>Al[4735,15,4736,1526,3144,0,0]=5454Al[4735,15,4736,2445,4263,0,0]Ah[3952,5,3705,2493,0,0,0]Ah[4735,5,3706,2493,0,0,0]Ah[4735,5,4736,1907,0,0,0]98Ah[4735,6,4736,1075,0,0,0]:9Ah[3952,5,3953,1842,0,0,0]Ah[4735,5,4736,1842,0,0,0]Ae[4735,5,1840,0,0,0,0]Al[4735,13,4736,3711,3309,0,0]Ai[3952,14,3953,0,3314,0,0]Al[4735,14,4736,1542,3315,0,0]Al[1050,14,1051,1051,3120,0,0]Ae[4735,2,4736,0,0,0,0]Ai[3952,13,3953,0,4377,0,0]Al[4735,13,4736,1842,4378,0,0]Al[3952,12,3953,2144,4365,0,0]Ab[4308,3,0,0,0,0,0]Ak[1365,14,1366,1366,127,0,0]Al[1365,14,1540,1540,4015,0,0]98Al[3952,13,3953,2471,3336,0,0]Al[4735,13,4736,2445,3337,0,0]Ai[3952,13,3953,0,4801,0,0]Al[4735,13,4736,3291,4802,0,0];=Al[3952,13,3953,4088,4877,0,0]Al[4735,13,4736,2152,3334,0,0]Al[3952,13,3953,3710,3306,0,0]Al[3952,13,3953,3710,3308,0,0]Al[3952,13,3953,2471,2403,0,0]Ak[3952,13,3953,1049,478,0,0]Al[4735,13,4736,2445,2404,0,0]<;Ai[3952,13,3953,0,1603,0,0]Ai[3952,13,3953,0,1586,0,0]Al[4735,13,4736,1960,1604,0,0]Al[4735,13,4736,1526,1587,0,0]20Ae[1365,7,1540,0,0,0,0]?>Al[1365,14,1540,1540,1361,0,0]Ao[1050,13,1051,1051,4870,3153,0]Ai[3952,15,3953,0,3128,0,0]Ah[3952,5,3953,1516,0,0,0]Al[4735,15,4736,1516,3129,0,0]Ah[4735,5,4736,1516,0,0,0]Ah[3952,6,3953,3711,0,0,0]Ah[4735,6,4736,3711,0,0,0]Al[4735,15,4736,2445,4263,0,0]Ah[4735,5,4736,1934,0,0,0]Ah[3952,5,3953,1542,0,0,0]Ah[4735,5,4736,1542,0,0,0]97Ae[1050,5,1541,0,0,0,0]:8Ah[4735,6,4736,1907,0,0,0]Ah[3952,5,3953,1071,0,0,0]Ah[4735,5,4736,1071,0,0,0]Ah[3952,5,3953,1551,0,0,0]Ah[4735,5,4736,1551,0,0,0]Ah[3952,5,3953,3291,0,0,0]Ah[4735,5,4736,3291,0,0,0]Ai[3952,15,3953,0,4495,0,0]Al[4735,15,4736,3711,4496,0,0]Ah[3952,5,3953,3710,0,0,0]Al[3952,15,3953,4088,3148,0,0]Al[3952,15,3953,4088,3335,0,0]Al[3952,15,3953,4093,3353,0,0]Al[3952,15,3953,4093,4172,0,0]An[1365,13,1366,1366,127,4262,0]Ao[1365,13,1366,1366,3984,4262,0]Ao[3952,13,3953,3710,3308,4262,0]Ao[3952,13,3953,3710,4936,4262,0]Ao[3952,13,3953,3710,4777,4262,0]Ao[3952,13,3953,2444,2399,4262,0]Ao[3952,13,3953,2444,4265,4262,0]Ao[3952,13,3953,4088,4401,4262,0]Ao[3952,13,3953,4088,3148,4262,0]Ao[3952,13,3953,4088,3335,4262,0]Ao[3952,13,3953,4088,4400,4262,0]Ao[3952,13,3953,4093,3353,4262,0]Ao[3952,13,3953,4093,4172,4262,0]Al[3952,13,3953,0,1603,4262,0]Al[3952,13,3953,0,4933,4262,0]Al[3952,13,3953,0,1586,4262,0]Al[3952,13,3953,0,2434,4262,0]Al[3952,13,3953,0,3314,4262,0]Al[3952,13,3953,0,3145,4262,0]Al[3952,13,3953,0,4499,4262,0]Al[3952,13,3953,0,2018,4262,0]Al[3952,13,3953,0,4124,4262,0]Al[3952,13,3953,0,4495,4262,0]Al[3952,13,3953,0,4516,4262,0]Al[3952,13,3953,0,4801,4262,0]Al[3952,13,3953,0,4816,4262,0]Al[3952,13,3953,0,3997,4262,0]Al[3952,13,3953,0,4377,4262,0]Al[3952,13,3953,0,4166,4262,0]Al[3952,13,3953,0,4990,4262,0]Al[3952,13,3953,0,4118,4262,0]Al[3952,13,3953,0,3987,4262,0]Al[3952,13,3953,0,3128,4262,0]Al[3952,13,3953,0,4492,4262,0]Al[3952,13,3953,0,4680,4262,0]Al[3952,13,3953,0,4130,4262,0]Al[3952,13,3953,0,4133,4262,0]Al[3952,13,3953,0,4138,4262,0]Al[3952,13,3953,0,4835,4262,0]Al[3952,13,3953,0,3317,4262,0]Ak[3952,13,3953,0,485,4262,0]Ak[3952,13,3953,0,119,4262,0]Al[3952,13,3953,0,2422,4262,0]Ak[3952,13,3705,0,113,4262,0]Al[3952,13,3705,0,3116,4262,0]Al[3952,13,3705,0,1328,4262,0]Al[3952,13,3705,0,3992,4262,0]Al[3952,13,3705,0,1992,4262,0]Al[3952,13,3705,0,4284,4262,0]Al[3952,13,1923,0,4929,4262,0]Al[3952,13,1923,0,1341,4262,0]Ao[4735,13,4736,1960,1604,4262,0]Ao[4735,13,4736,1960,4934,4262,0]Ao[4735,13,4736,3173,4778,4262,0]Ao[4735,13,4736,1526,1587,4262,0]Ao[4735,13,4736,1526,4162,4262,0]Ao[4735,13,4736,1526,3329,4262,0]Ao[4735,13,4736,1526,3144,4262,0]Ao[4735,13,4736,1526,4832,4262,0]Ao[4735,13,4736,1526,3304,4262,0]Ao[4735,13,4736,1542,2435,4262,0]Ao[4735,13,4736,1542,3315,4262,0]Ao[4735,13,4736,1542,3146,4262,0]Ao[4735,13,4736,1542,4500,4262,0]Ao[4735,13,4736,1075,3149,4262,0]Ao[4735,13,4736,1075,4292,4262,0]Ao[4735,13,4736,1080,2019,4262,0]Ao[4735,13,4736,1080,3354,4262,0]Ao[4735,13,4736,1080,3996,4262,0]Ao[4735,13,4736,2152,4402,4262,0]Ao[4735,13,4736,2152,4128,4262,0]Ao[4735,13,4736,3711,4496,4262,0]Ao[4735,13,4736,3711,3141,4262,0]Ao[4735,13,4736,3711,4368,4262,0]Ao[4735,13,4736,3711,5017,4262,0]Ao[4735,13,4736,3291,4517,4262,0]Ao[4735,13,4736,3291,4802,4262,0]Ao[4735,13,4736,3291,4817,4262,0]Ao[4735,13,4736,3291,3998,4262,0]Ao[4735,13,4736,1842,4378,4262,0]Ao[4735,13,4736,1842,4168,4262,0]Ao[4735,13,4736,1842,4991,4262,0]Ao[4735,13,4736,1842,4120,4262,0]Ao[4735,13,4736,1842,3988,4262,0]Ao[4735,13,4736,1516,3129,4262,0]Ao[4735,13,4736,1516,3321,4262,0]Ao[4735,13,4736,1516,3343,4262,0]Ao[4735,13,4736,1516,4135,4262,0]Ao[4735,13,4736,1516,4494,4262,0]Ao[4735,13,4736,1516,4682,4262,0]Ao[4735,13,4736,1516,2393,4262,0]Ao[4735,13,4736,2445,4686,4262,0]Ao[4735,13,4736,2445,4131,4262,0]Ao[4735,13,4736,1907,4508,4262,0]Ao[4735,13,4736,1907,5020,4262,0]Ao[4735,13,4736,1907,2406,4262,0]Ao[4735,13,4736,1907,4776,4262,0]Ao[4735,13,4736,1934,4134,4262,0]Ao[4735,13,4736,1934,4139,4262,0]Al[4735,13,4736,0,4406,4262,0]Al[4735,13,4736,0,4836,4262,0]Ao[4735,13,4736,4305,3318,4262,0]Ao[4735,13,4736,4305,5113,4262,0]An[4735,13,4736,4736,486,4262,0]An[4735,13,4736,4736,120,4262,0]Ao[4735,13,4736,4736,2423,4262,0]Ao[4735,13,4736,4736,3127,4262,0]Ao[4735,13,1840,1840,4298,4262,0]Ao[4735,13,4975,4975,4876,4262,0]Ao[4735,13,4975,4975,1343,4262,0]Ao[4735,13,1924,1924,4930,4262,0]Al[4735,13,1924,0,1342,4262,0]Am[4735,13,3706,135,114,4262,0]An[4735,13,3706,135,3117,4262,0]Ao[4735,13,3706,1511,1329,4262,0]Ao[4735,13,3706,1511,3993,4262,0]Ao[4735,13,3706,2493,1993,4262,0]Ao[4735,13,3706,2493,4285,4262,0]Ao[4735,13,2123,2123,1347,4262,0]Ao[4441,13,4442,4442,2420,4262,0]Ao[4441,13,4442,4442,2433,4262,0]Ao[4441,13,4442,4442,4140,4262,0]Ao[1050,13,1051,1051,4870,4262,0]Ao[1050,13,1541,1541,4504,4262,0]Ah[3952,7,3953,2144,0,0,0]Ai[3952,13,3953,0,4377,0,0]Al[4735,13,4736,1842,4378,0,0]Ai[3952,12,1844,0,3138,0,0]Al[4735,12,1845,1845,3139,0,0]Al[4441,14,4442,4442,2420,0,0]Ai[3952,14,3953,0,3314,0,0]Al[4735,14,4736,1542,3315,0,0]10Ae[1050,7,1051,0,0,0,0]Al[3952,13,3953,2444,2399,0,0]Ae[4308,7,4309,0,0,0,0]Ae[1365,7,1366,0,0,0,0]Al[4735,13,4736,3711,3309,0,0]Al[3952,13,3953,3710,3308,0,0]1Al[4735,13,4736,2152,4402,0,0]Al[1365,13,1540,1540,1361,0,0]Al[1365,14,1540,1540,4015,0,0]Al[3952,12,3953,2144,2004,0,0]Ao[3952,13,3953,1552,4173,2004,0]Ao[3952,13,3953,1092,4010,2004,0]Ai[3952,12,1844,0,2005,0,0]Al[3952,13,1923,0,3348,2005,0]Al[4735,12,1845,1845,2006,0,0]Ao[4735,13,1924,1924,3349,2006,0]Ah[3952,5,3953,1049,0,0,0]Ai[3952,15,3953,0,3128,0,0]Ah[3952,5,3953,1516,0,0,0]Al[4735,15,4736,1516,3129,0,0]Ah[4735,5,4736,1516,0,0,0]Ai[3952,15,3953,0,4495,0,0]Al[4735,15,4736,3711,4496,0,0]Ah[4735,5,4736,2445,0,0,0]Al[4735,15,4736,3711,1346,0,0]Ai[3952,15,3953,0,4130,0,0]Al[4735,15,4736,2445,4131,0,0]Ai[3952,19,3953,0,4835,0,0]Ai[4735,19,4736,0,4836,0,0]Ai[3952,15,1923,0,1341,0,0]Ai[4735,15,1924,0,1342,0,0]32Al[4735,15,4736,3711,3141,0,0]><><><21Ah[3952,5,1923,1924,0,0,0]Ae[4735,5,1924,0,0,0,0]10Ah[3952,5,3953,4866,0,0,0]Ah[4735,5,4736,4866,0,0,0]Al[4735,15,2123,2123,1344,0,0]5Al[4735,15,4736,1907,4508,0,0]Ai[3952,12,3953,0,2427,0,0]Al[3952,13,3953,0,4256,2427,0]Al[3952,13,3953,0,4150,2427,0]Al[4735,12,4736,1551,2428,0,0]Ao[4735,13,4736,4526,4257,2428,0]Ao[4735,13,4736,1551,4151,2428,0]Ae[4021,7,4022,0,0,0,0]Al[3952,13,3953,3710,3308,0,0]Al[4735,13,4736,3711,3309,0,0]Ai[3952,14,3953,0,3314,0,0]Al[4735,14,4736,1542,3315,0,0]32An[1365,13,1366,1366,127,4379,0]Ao[1050,13,1051,1051,3120,4379,0]An[1050,13,1834,1834,128,4379,0]An[1050,13,1929,1929,129,4379,0]An[3972,13,3973,3973,118,4379,0]Ao[3972,13,3973,3973,2390,4379,0]Ab[4735,3,0,0,0,0,0]Ak[4441,13,4442,393,4407,0,0]Ai[3952,13,3953,0,4516,0,0]Al[4735,13,4736,3291,4517,0,0];:Al[3952,13,3953,2444,2399,0,0]Al[4735,13,4736,2445,2400,0,0]?>32Ae[4308,7,4309,0,0,0,0]5Al[1050,14,1051,1051,3120,0,0]60Ae[1365,7,1366,0,0,0,0]Ai[3952,13,3953,0,4166,0,0]Ai[3952,13,3953,0,4118,0,0]Al[4735,13,4736,1842,4168,0,0]Al[4735,13,4736,1842,4120,0,0]Ai[3952,14,3953,0,4166,0,0]Al[4735,14,4736,1842,4168,0,0]53Al[3952,13,3953,4093,3339,0,0]Ai[3952,19,3953,0,4835,0,0]Ai[4735,19,4736,0,4836,0,0]Ai[3952,15,3953,0,4495,0,0]Al[4735,15,4736,3711,4496,0,0]Ai[3952,10,3705,3706,0,0,0]Af[4735,10,3706,0,0,0,0]Ah[3952,5,3705,3706,0,0,0]Ae[4735,5,3706,0,0,0,0]Ai[3952,10,1844,1845,0,0,0]Af[4735,10,1845,0,0,0,0]Ai[3952,15,3953,0,4130,0,0]Al[4735,15,4736,2445,4131,0,0]Ah[4735,6,4736,1907,0,0,0]Ah[3952,6,3953,3710,0,0,0]Ah[4735,6,4736,3173,0,0,0]Ai[3952,15,3953,0,3128,0,0]Al[4735,15,4736,1516,3129,0,0]10Al[3952,15,3953,3710,4777,0,0]Al[4735,15,4736,3173,4778,0,0]Ai[3952,14,3953,0,3145,0,0]Al[4735,14,4736,1542,3146,0,0]Ai[3952,14,3705,0,4769,0,0]Al[4735,14,3706,3706,4770,0,0]Al[3952,13,3953,3710,3308,0,0]Ai[3952,14,3953,0,3314,0,0]Al[4735,14,4736,1542,3315,0,0]10Al[4735,13,4736,3173,3123,0,0]Al[3952,13,3953,2444,2399,0,0]Al[4735,13,4736,2445,2400,0,0]Ab[4796,3,0,0,0,0,0]Ao[1050,13,1051,1051,4870,3153,0]Ai[3952,19,3953,0,4835,0,0]Ai[4735,19,4736,0,4836,0,0]Ah[3952,5,3953,3291,0,0,0]Ah[4735,5,4736,3291,0,0,0]Ai[3952,15,1923,0,1341,0,0]Ai[4735,15,1924,0,1342,0,0]54Ai[4735,10,4736,1907,0,0,0]Al[4735,15,4736,3711,1346,0,0]7600Al[4735,15,4736,1075,4292,0,0]4365Al[4735,15,2123,2123,1344,0,0]Ae[1050,9,1051,0,0,0,0]:9Ai[3952,15,3953,0,3128,0,0]Al[4735,15,4736,1516,3129,0,0]<;<;Ah[4735,6,4736,1526,0,0,0]21Ae[3952,5,3953,0,0,0,0]Ae[4735,5,4736,0,0,0,0]Ai[3952,15,3953,0,4130,0,0]Ah[3952,5,1844,1845,0,0,0]Al[4735,15,4736,2445,4131,0,0]Al[4735,15,4736,1907,4776,0,0]Ae[4735,5,1845,0,0,0,0]An[1365,13,1366,1366,127,4369,0]Ao[1050,13,1051,1051,3120,4369,0]An[1050,13,1834,1834,128,4369,0]An[1050,13,1929,1929,129,4369,0]An[3972,13,3973,3973,118,4369,0]Ai[3952,13,3953,0,4516,0,0]Al[4735,13,4736,3291,4517,0,0]Ai[3952,13,3953,0,1603,0,0]Al[4735,13,4736,1960,1604,0,0]Al[3952,14,3953,2444,4265,0,0]Ai[3952,13,3953,0,4377,0,0]Al[4735,13,4736,1842,4378,0,0]Ai[3952,13,3953,0,4256,0,0]Al[4735,13,4736,4526,4257,0,0]Ai[3952,14,3953,0,3314,0,0]Al[4735,14,4736,1542,3315,0,0]6Al[4735,13,4736,3711,3309,0,0]Al[3952,13,3953,3710,3308,0,0]0176Ao[4021,13,4022,4022,5021,4830,0]Ai[3952,13,3953,0,4509,0,0]Al[4735,13,4736,1071,4510,0,0]Ae[1050,6,1051,0,0,0,0]Ai[3952,15,3953,0,3128,0,0]Al[4735,15,4736,1516,3129,0,0]10Al[4735,15,4736,3711,3141,0,0]Ae[4735,5,4975,0,0,0,0]Ah[3952,5,3953,4088,0,0,0]Ai[3952,15,1923,0,1341,0,0]Ai[4735,15,1924,0,1342,0,0]Al[4735,15,2123,2123,1344,0,0]Al[3952,14,3953,2444,4265,0,0]Ak[3952,13,3953,0,485,3110,0]Ak[3952,13,3953,0,119,3110,0]An[4735,13,4736,4736,486,3110,0]An[4735,13,4736,4736,120,3110,0]Ao[3972,13,3973,3973,2390,3344,0]Ai[3952,13,3953,0,4816,0,0]Al[4735,13,4736,3291,4817,0,0]Ai[3952,12,1844,0,2005,0,0]Al[3952,13,1923,0,3348,2005,0]Al[4735,12,1845,1845,2006,0,0]Ao[4735,13,1924,1924,3349,2006,0]Al[3952,13,3953,3710,3308,0,0]Ai[3952,13,3953,0,4516,0,0]Al[4735,13,4736,3291,4517,0,0]Ai[3952,14,3953,0,3145,0,0]Al[4735,14,4736,1542,3146,0,0]Ai[3952,12,3705,0,3310,0,0]Ak[3952,13,3705,0,113,3310,0]Al[3952,13,3705,0,1328,3310,0]Al[3952,13,3705,0,1992,3310,0]Al[4735,12,3706,3706,3311,0,0]Am[4735,13,3706,135,114,3311,0]Ao[4735,13,3706,1511,1329,3311,0]Ao[4735,13,3706,2493,1993,3311,0]Al[3952,14,3953,2444,4265,0,0]Al[4735,13,4736,2152,2012,0,0]Ah[4735,2,4303,4092,0,0,0]Ae[1365,7,1366,0,0,0,0]==Ak[3952,13,3953,1049,478,0,0]Ak[4441,13,4442,393,4407,0,0]032Ai[3952,12,1844,0,2005,0,0]Al[3952,13,1923,0,3348,2005,0]Ae[4735,2,4736,0,0,0,0]Al[4735,12,1845,1845,2006,0,0]Ao[4735,13,1924,1924,3349,2006,0]Ai[3952,10,3953,4305,0,0,0]Ai[4735,10,4736,4305,0,0,0]Ai[3952,15,1923,0,1341,0,0]Ai[4735,15,1924,0,1342,0,0]Ai[3952,15,3953,0,4130,0,0]Al[4735,15,4736,2445,4131,0,0]Ai[3952,19,3953,0,4835,0,0]Ai[4735,19,4736,0,4836,0,0]Ah[3952,5,3953,1842,0,0,0]Ah[4735,5,4736,1842,0,0,0]Ai[3952,12,1844,0,3138,0,0]Al[4735,12,1845,1845,3139,0,0]Al[4735,13,4736,2445,2400,0,0]Al[4735,13,4736,4736,3127,0,0]Al[4735,13,4736,3711,3309,0,0]Ai[3952,13,3705,0,4284,0,0]Al[4735,13,3706,2493,4285,0,0]Al[3952,13,3953,2144,3151,0,0]0Al[3952,13,3953,3710,3308,0,0]4Ai[3952,14,3953,0,3314,0,0]Al[4735,14,4736,1542,3315,0,0]Ai[3952,13,3953,0,4516,0,0]Al[4735,13,4736,3291,4517,0,0]Al[1050,14,1051,1051,3120,0,0]Ai[3952,19,3953,0,4835,0,0]Ai[4735,19,4736,0,4836,0,0]Al[4735,15,2123,2123,1344,0,0]Ai[3952,15,1923,0,1341,0,0]Ai[4735,15,1924,0,1342,0,0]10Al[4735,15,4736,1526,4832,0,0]Al[4735,15,4736,3711,3141,0,0]Ah[4735,5,4736,3711,0,0,0]Ai[3952,15,3953,0,3128,0,0]Al[4735,15,4736,1516,3129,0,0]Ah[4735,5,4736,1907,0,0,0]Ae[4021,5,4022,0,0,0,0]Al[1050,13,1051,1051,3120,0,0]Al[3952,13,3953,3710,3308,0,0]Ai[3952,12,1844,0,2005,0,0]Al[3952,13,1923,0,3348,2005,0]Al[4735,12,1845,1845,2006,0,0]Ao[4735,13,1924,1924,3349,2006,0]Ah[4735,7,4909,4908,0,0,0]0Al[4735,13,4736,3711,3309,0,0]6Ak[3952,13,3953,1049,478,0,0]Ai[3952,14,3953,0,3314,0,0]Al[4735,14,4736,1542,3315,0,0]Ao[4021,13,4022,4022,5021,4830,0]0Ai[3952,13,3953,0,4118,0,0]Al[4735,13,4736,1842,4120,0,0]Ai[3952,15,1923,0,1341,0,0]Ai[4735,15,1924,0,1342,0,0]10Ai[3952,13,3953,0,4801,0,0]Al[4735,13,4736,3291,4802,0,0]Am[1365,13,1366,1366,127,460,0]An[1050,13,1051,1051,3120,460,0]Am[1050,13,1834,1834,128,460,0]Am[1050,13,1929,1929,129,460,0]Am[3972,13,3973,3973,118,460,0]??>An[1365,13,1366,1366,127,4379,0]Ao[1050,13,1051,1051,3120,4379,0]An[1050,13,1834,1834,128,4379,0]An[1050,13,1929,1929,129,4379,0]An[3972,13,3973,3973,118,4379,0]Ao[3972,13,3973,3973,2390,4379,0]Ai[3952,13,3953,0,4166,0,0]Al[4735,13,4736,1842,4168,0,0]Ai[3952,19,3953,0,4835,0,0]Ai[4735,19,4736,0,4836,0,0]Ae[1050,5,4225,0,0,0,0]0Ai[3952,13,3953,0,4377,0,0]Al[4735,13,4736,1842,4378,0,0]Ai[3952,13,3953,0,4118,0,0]Al[4735,13,4736,1842,4120,0,0]Al[3952,13,3953,2444,2399,0,0]Al[3952,13,3953,2471,2403,0,0]Al[4735,13,4736,2445,2404,0,0]Al[4735,13,4736,3711,3309,0,0]Ao[4021,13,4022,4022,5021,4830,0];:Ah[3952,5,1844,1845,0,0,0]Ae[4735,5,1845,0,0,0,0]Ai[3952,15,1923,0,1341,0,0]Ai[4735,15,1924,0,1342,0,0]?>?>Al[3952,13,3953,3710,3308,0,0]6Ai[3952,13,3953,0,4516,0,0]Al[4735,13,4736,3291,4517,0,0]Ae[4021,7,4022,0,0,0,0]Ae[4308,7,4309,0,0,0,0]Ai[3952,14,3953,0,3314,0,0]Al[4735,14,4736,1542,3315,0,0]?=Ai[3952,19,3953,0,4835,0,0]Ai[4735,19,4736,0,4836,0,0]2=<==<>=>=>=<;<;Al[4735,14,4975,4975,4876,0,0]?>Ai[4735,18,4736,3291,0,0,0]") \ No newline at end of file diff --git a/pr/395/docs/search.index/function/2aa7c4c5510f.js b/pr/395/docs/search.index/function/2aa7c4c5510f.js new file mode 100644 index 000000000..77560d7d8 --- /dev/null +++ b/pr/395/docs/search.index/function/2aa7c4c5510f.js @@ -0,0 +1 @@ +rd_("Bf[\"{{{AIAj{AOB`}}{AIAj{AOB`}}}CMn}\",[]]Bf[\"{{{AIAj{BEEb}}{AIAj{BEEb}}}CMn}\",[]]Bf[\"{{{AIAj{BBFb}}{AIAj{BBFb}}}CMn}\",[]]Bf[\"{{{AIAj{AHIh}}{AIAj{AHIh}}}CMn}\",[]]Bf[\"{{{AIAj{AJ@n}}{AIAj{AJ@n}}}CMn}\",[]]Bf[\"{{{AIAj{BBF`}}{AIAj{BBF`}}}CMn}\",[]]Bf[\"{{{AIAj{AJCb}}{AIAj{AJCb}}}CMn}\",[]]Bf[\"{{{AIAj{B@Ih}}{AIAj{B@Ih}}}CMn}\",[]]Bd[\"{{{AIAj{LHf}}{AIAj{LHf}}}CMn}\",[]]Bf[\"{{{AIAj{BFHj}}{AIAj{BFHj}}}CMn}\",[]]Bd[\"{{{AIAj{JEh}}{AIAj{JEh}}}CMn}\",[]]Bd[\"{{{AIAj{LFd}}{AIAj{LFd}}}CMn}\",[]]Bf[\"{{{AIAj{AC@d}}{AIAj{AC@d}}}CMn}\",[]]Bf[\"{{{AIAj{AHIb}}{AIAj{AHIb}}}CMn}\",[]]Bd[\"{{{AIAj{OLd}}{AIAj{OLd}}}CMn}\",[]]Bf[\"{{{AIAj{B@Ch}}{AIAj{B@Ch}}}CMn}\",[]]Bf[\"{{{AIAj{BCAn}}{AIAj{BCAn}}}CMn}\",[]]Bf[\"{{{AIAj{AOCj}}{AIAj{AOCj}}}CMn}\",[]]Bf[\"{{{AIAj{AOBf}}{AIAj{AOBf}}}CMn}\",[]]Bf[\"{{{AIAj{AHG`}}{AIAj{AHG`}}}CMn}\",[]]Bf[\"{{{AIAj{BCAh}}{AIAj{BCAh}}}CMn}\",[]]Bf[\"{{{AIAj{BDI`}}{AIAj{BDI`}}}CMn}\",[]]Bf[\"{{{AIAj{B@Dd}}{AIAj{B@Dd}}}CMn}\",[]]Bf[\"{{{AIAj{B@Dj}}{AIAj{B@Dj}}}CMn}\",[]]Bf[\"{{{AIAj{B@Ed}}{AIAj{B@Ed}}}CMn}\",[]]Bf[\"{{{AIAj{BELf}}{AIAj{BELf}}}CMn}\",[]]Bd[\"{{{AIAj{CLj}}{AIAj{CLj}}}CMn}\",[]]Bb[\"{{{AIAj{Nn}}{AIAj{Nn}}}CMn}\",[]]Bf[\"{{{AIAj{ABNl}}{AIAj{ABNl}}}CMn}\",[]]Bf[\"{{{AIAj{BGJn}}{AIAj{BGJn}}}CMn}\",[]]Bd[\"{{{AIAj{JGj}}{AIAj{JGj}}}CMn}\",[]]Bd[\"{{{AIAj{LHh}}{AIAj{LHh}}}CMn}\",[]]Bf[\"{{{AIAj{BFHl}}{AIAj{BFHl}}}CMn}\",[]]Bd[\"{{{AIAj{JEj}}{AIAj{JEj}}}CMn}\",[]]Bf[\"{{{AIAj{BEEd}}{AIAj{BEEd}}}CMn}\",[]]Bd[\"{{{AIAj{LFf}}{AIAj{LFf}}}CMn}\",[]]Bf[\"{{{AIAj{AJ@b}}{AIAj{AJ@b}}}CMn}\",[]]Bf[\"{{{AIAj{AHI`}}{AIAj{AHI`}}}CMn}\",[]]Bf[\"{{{AIAj{BEL`}}{AIAj{BEL`}}}CMn}\",[]]Bf[\"{{{AIAj{AIM`}}{AIAj{AIM`}}}CMn}\",[]]Bf[\"{{{AIAj{AC@f}}{AIAj{AC@f}}}CMn}\",[]]Bf[\"{{{AIAj{AHId}}{AIAj{AHId}}}CMn}\",[]]Bf[\"{{{AIAj{AHIj}}{AIAj{AHIj}}}CMn}\",[]]Bf[\"{{{AIAj{BAHh}}{AIAj{BAHh}}}CMn}\",[]]Bd[\"{{{AIAj{OLf}}{AIAj{OLf}}}CMn}\",[]]Bf[\"{{{AIAj{AJCd}}{AIAj{AJCd}}}CMn}\",[]]Bf[\"{{{AIAj{AOCh}}{AIAj{AOCh}}}CMn}\",[]]Bf[\"{{{AIAj{AOEl}}{AIAj{AOEl}}}CMn}\",[]]Bf[\"{{{AIAj{BBFd}}{AIAj{BBFd}}}CMn}\",[]]Bf[\"{{{AIAj{B@D`}}{AIAj{B@D`}}}CMn}\",[]]Bf[\"{{{AIAj{BCB`}}{AIAj{BCB`}}}CMn}\",[]]Bf[\"{{{AIAj{AHHj}}{AIAj{AHHj}}}CMn}\",[]]Bf[\"{{{AIAj{BBB`}}{AIAj{BBB`}}}CMn}\",[]]Bf[\"{{{AIAj{AOCl}}{AIAj{AOCl}}}CMn}\",[]]Bf[\"{{{AIAj{AOBh}}{AIAj{AOBh}}}CMn}\",[]]Bf[\"{{{AIAj{AHGb}}{AIAj{AHGb}}}CMn}\",[]]Bf[\"{{{AIAj{BBCn}}{AIAj{BBCn}}}CMn}\",[]]Bf[\"{{{AIAj{AIOb}}{AIAj{AIOb}}}CMn}\",[]]Bf[\"{{{AIAj{AJAn}}{AIAj{AJAn}}}CMn}\",[]]Bf[\"{{{AIAj{B@Dn}}{AIAj{B@Dn}}}CMn}\",[]]Bf[\"{{{AIAj{BCAl}}{AIAj{BCAl}}}CMn}\",[]]Bf[\"{{{AIAj{BDId}}{AIAj{BDId}}}CMn}\",[]]Bf[\"{{{AIAj{BDIl}}{AIAj{BDIl}}}CMn}\",[]]Bf[\"{{{AIAj{B@Df}}{AIAj{B@Df}}}CMn}\",[]]Bf[\"{{{AIAj{ABLl}}{AIAj{ABLl}}}CMn}\",[]]Bf[\"{{{AIAj{BEE`}}{AIAj{BEE`}}}CMn}\",[]]Bf[\"{{{AIAj{B@Dl}}{AIAj{B@Dl}}}CMn}\",[]]Bf[\"{{{AIAj{B@Ef}}{AIAj{B@Ef}}}CMn}\",[]]Bf[\"{{{AIAj{BBFl}}{AIAj{BBFl}}}CMn}\",[]]Bf[\"{{{AIAj{BELh}}{AIAj{BELh}}}CMn}\",[]]Bd[\"{{{AIAj{CLl}}{AIAj{CLl}}}CMn}\",[]]Bb[\"{{{AIAj{O`}}{AIAj{O`}}}CMn}\",[]]Bf[\"{{{AIAj{ABNn}}{AIAj{ABNn}}}CMn}\",[]]Bf[\"{{{AIAj{AHFn}}{AIAj{AHFn}}}CMn}\",[]]Bf[\"{{{AIAj{BAId}}{AIAj{BAId}}}CMn}\",[]]Bf[\"{{{AIAj{BFAh}}{AIAj{BFAh}}}CMn}\",[]]Bd[\"{{{AIAj{JGn}}{AIAj{JGn}}}CMn}\",[]]Bf[\"{{{AIAj{BGK`}}{AIAj{BGK`}}}CMn}\",[]]Bd[\"{{{AIAj{JGl}}{AIAj{JGl}}}CMn}\",[]]Bf[\"{{{AIAj{B@Eh}}{AIAj{B@Eh}}}CMn}\",[]]Ai[\"{{{AIAj{CKl}}}LFd}\",[]]Ak[\"{{{AIAj{AJ@j}}}ABNl}\",[]]Ak[\"{{{AIAj{BFAj}}}ABNl}\",[]]Ak[\"{{{AIAj{AJAf}}}ABNl}\",[]]o[\"{LEhLFf}\",[]]o[\"{LDdLFf}\",[]]A`[\"{AHDfLFf}\",[]]A`[\"{BAFfLFf}\",[]]A`[\"{B@GnLFf}\",[]]A`[\"{B@G`LFf}\",[]]Aa[\"{ABN`ABNn}\",[]]Aa[\"{AC@jABNn}\",[]]Aa[\"{ABLbABNn}\",[]]A`[\"{OJnABNn}\",[]]n[\"{{}OJf}\",[]]A`[\"{CKdABLl}\",[]]Be[\"{{{AIAj{LHf}}{AIAj{LHf}}}ABMn}\",[]]Be[\"{{{AIAj{LFd}}{AIAj{LFd}}}ABMn}\",[]]Bg[\"{{{AIAj{AOCj}}{AIAj{AOCj}}}ABMn}\",[]]Bg[\"{{{AIAj{B@Dj}}{AIAj{B@Dj}}}ABMn}\",[]]Be[\"{{{AIAj{CLj}}{AIAj{CLj}}}ABMn}\",[]]Bc[\"{{{AIAj{Nn}}{AIAj{Nn}}}ABMn}\",[]]Bg[\"{{{AIAj{ABNl}}{AIAj{ABNl}}}ABMn}\",[]]Be[\"{{{AIAj{LHh}}{AIAj{LHh}}}ABMn}\",[]]Bg[\"{{{AIAj{BEEd}}{AIAj{BEEd}}}ABMn}\",[]]Be[\"{{{AIAj{LFf}}{AIAj{LFf}}}ABMn}\",[]]Bg[\"{{{AIAj{AHIj}}{AIAj{AHIj}}}ABMn}\",[]]Bg[\"{{{AIAj{BAHh}}{AIAj{BAHh}}}ABMn}\",[]]Bg[\"{{{AIAj{AJCd}}{AIAj{AJCd}}}ABMn}\",[]]Bg[\"{{{AIAj{AOCl}}{AIAj{AOCl}}}ABMn}\",[]]Bg[\"{{{AIAj{BDIl}}{AIAj{BDIl}}}ABMn}\",[]]Bg[\"{{{AIAj{BEE`}}{AIAj{BEE`}}}ABMn}\",[]]Bg[\"{{{AIAj{B@Dl}}{AIAj{B@Dl}}}ABMn}\",[]]Be[\"{{{AIAj{CLl}}{AIAj{CLl}}}ABMn}\",[]]Bc[\"{{{AIAj{O`}}{AIAj{O`}}}ABMn}\",[]]Bg[\"{{{AIAj{ABNn}}{AIAj{ABNn}}}ABMn}\",[]]o[\"{ABNlNn}\",[]]o[\"{ABNnO`}\",[]]n[\"{LEjO`}\",[]]o[\"{AHEbO`}\",[]]A`[\"{ABOnLF`}\",[]]A`[\"{AC@`LF`}\",[]]A`[\"{ABNhLF`}\",[]]A`[\"{AC@bLF`}\",[]]Bg[\"{{{AIAj{On}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AOB`}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BBFf}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BGCj}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AIMd}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AIMh}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BFI`}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BEEb}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{ABKn}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BAEb}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{ABLf}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AJA`}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BBCl}}{AIAj{CBfAHFl}}}LFh}\",[]]Bh[\"{{{AIAj{CKl}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AJ@j}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BFAj}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BBFb}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{AHIh}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{AJ@n}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BBF`}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{B@Ij}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AOEd}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AJAf}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AJCb}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{B@Ih}}{AIAj{CBfAHFl}}}LFh}\",[]]0Cd[\"{{{AIAj{LHf}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Ce[\"{{{AIAj{BFHj}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Cd[\"{{{AIAj{JEh}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Cd[\"{{{AIAj{LFd}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Ce[\"{{{AIAj{AC@d}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{AINd}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{AHIb}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{BCBf}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Cd[\"{{{AIAj{OLd}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{B@Ch}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Ce[\"{{{AIAj{BCAn}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Ce[\"{{{AIAj{BCCj}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{BCDh}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{BEHb}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{BEJ`}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{AOCj}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{BBCb}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{B@Hl}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{BFOl}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{B@Bl}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{AOBf}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Ce[\"{{{AIAj{AHG`}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Ce[\"{{{AIAj{BCAh}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Ce[\"{{{AIAj{BDI`}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Ce[\"{{{AIAj{B@Dd}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Ce[\"{{{AIAj{B@Dj}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Ce[\"{{{AIAj{B@Ed}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{BELf}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Ce[\"{{{AIAj{AINj}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{BDLn}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Cd[\"{{{AIAj{CLj}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Cc[\"{{{AIAj{Nn}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Ce[\"{{{AIAj{ABNl}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Ce[\"{{{AIAj{ABOn}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Cc[\"{{{AIAj{Nb}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{AHEh}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Cd[\"{{{AIAj{JF`}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{AOC`}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Cd[\"{{{AIAj{OI`}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{BAGh}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{BEDb}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{BGJn}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{AOBl}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{BELn}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]Ce[\"{{{AIAj{BFHb}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Cd[\"{{{AIAj{JGj}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Bi[\"{{{AIAj{AIMj}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{ABL`}}{AIAj{CBfAHFl}}}LFh}\",[]]Bh[\"{{{AIAj{LHh}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BFHl}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bh[\"{{{AIAj{JEj}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BEEd}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bh[\"{{{AIAj{LFf}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{B@Hd}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AJ@b}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AHI`}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BEL`}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{AIM`}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{AC@f}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AINf}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AHId}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BCBh}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AHIj}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BAHh}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bh[\"{{{AIAj{OLf}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AJCd}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{AOCh}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{AOEl}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BBFd}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{B@D`}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BCB`}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{AHHj}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BBB`}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BGCb}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bh[\"{{{AIAj{JHd}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BCCl}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BCDj}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BEHd}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BEJb}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AOCl}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BBCd}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{B@I`}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BFOn}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{B@C`}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AOBh}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{AHGb}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BBCn}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{AIOb}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{AJAn}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{B@Dn}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BCAl}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BDId}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{ABKb}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BDIl}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{ABLh}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{B@Df}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{AOAl}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AJAb}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BAGn}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BCCh}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BGCh}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{ABLl}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BEE`}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BCBd}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BAE`}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{B@Dl}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{B@Ef}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BBFl}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BELh}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{AINl}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BGOb}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BDM`}}{AIAj{CBfAHFl}}}LFh}\",[]]Bh[\"{{{AIAj{CLl}}{AIAj{CBfAHFl}}}LFh}\",[]]Bg[\"{{{AIAj{O`}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{ABNn}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{AC@`}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AHFn}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BAId}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BFAh}}{AIAj{CBfAHFl}}}LFh}\",[]]Bh[\"{{{AIAj{JGn}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bi[\"{{{AIAj{BGK`}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AOBn}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BEMd}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BFHd}}{AIAj{CBfAHFl}}}LFh}\",[]]0Cd[\"{{{AIAj{JGl}}{AIAj{CBfAHFl}}}{{LFj{HJ`JFj}}}}\",[]]0Bg[\"{{{AIAj{Nd}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AHEj}}{AIAj{CBfAHFl}}}LFh}\",[]]Bh[\"{{{AIAj{JFb}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AOCb}}{AIAj{CBfAHFl}}}LFh}\",[]]Bh[\"{{{AIAj{OIb}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BAGj}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BEDd}}{AIAj{CBfAHFl}}}LFh}\",[]]Bh[\"{{{AIAj{JH`}}{AIAj{CBfAHFl}}}LFh}\",[]]0Bh[\"{{{AIAj{JHf}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{BBFn}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{ABNh}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{AC@b}}{AIAj{CBfAHFl}}}LFh}\",[]]Bi[\"{{{AIAj{B@Eh}}{AIAj{CBfAHFl}}}LFh}\",[]]Ak[\"{{{AIAj{B@Hd}}}ABKj}\",[]]A`[\"{AC@dCId}\",[]]A`[\"{AC@fCId}\",[]]Aj[\"{{{AIAj{BAGn}}}LBd}\",[]]A`[\"{BGChCId}\",[]]o[\"{CKdCId}\",[]]A`[\"{ABNhLBd}\",[]]Ce[\"{{{AIAj{CBfBFOl}}{N`{ABOf}}}{{AIAj{CBfBFOl}}}}\",[]]Ce[\"{{{AIAj{CBfBFOn}}{N`{ABOh}}}{{AIAj{CBfBFOn}}}}\",[]]8Af[\"{{ABKjCId}BBFf}\",[]]Ag[\"{{}{{N`{BGCj}}}}\",[]]Ae[\"{{LBdCIb}BAD`}\",[]]Cd[\"{{BBCb{LF`{B@Hl}}AHJ`{N`{OJh}}}{{LFl{ABKn}}}}\",[]]Ah[\"{{{Nh{JJh}}}AJ@d}\",[]]A`[\"{OJ`BCCj}\",[]]Ah[\"{{{N`{OGn}}}BBCb}\",[]]Bc[\"{{{N`{B@Hn}}{N`{BEDf}}}B@Hl}\",[]]Ai[\"{{{N`{B@Bn}}}B@Bl}\",[]]B`[\"{{{AIAj{{L@`{Mh}}}}}B@Dj}\",[]]Ae[\"{{CIbLBd}BDLn}\",[]]Ag[\"{{CLjNnCId}ABNl}\",[]]Aj[\"{{{N`{AHEh}}CI`}Nb}\",[]]An[\"{{{N`{AOC`}}OHdCI`}JF`}\",[]]An[\"{{{N`{BAGh}}OHdCI`}OI`}\",[]]Ae[\"{{LBdCIb}BADb}\",[]]Ah[\"{{{Nh{JJh}}}AJ@f}\",[]]Bm[\"{{{N`{BBCd}}{LF`{{N`{B@I`}}}}CMn}ABL`}\",[]]A`[\"{LBdBHBd}\",[]]o[\"{{}BGNh}\",[]]A`[\"{OJ`BCCl}\",[]]Ah[\"{{{N`{OH`}}}BBCd}\",[]]Bc[\"{{{N`{B@Ib}}{N`{BEDh}}}B@I`}\",[]]Ai[\"{{{N`{B@Cb}}}B@C`}\",[]]Ek[\"{{ABJh{AIAj{c}}{AIAj{{L@`{CIb}}}}CMn}{{LFj{{LBb{BAGn{LF`{ABJh}}}}BEE`}}}{BEHnJJ`}}\",[\"\"]]B`[\"{{{AIAj{{L@`{Mh}}}}}B@Dl}\",[]]Af[\"{{BELhLHb}BBFl}\",[]]A`[\"{LBdBGOb}\",[]]Ae[\"{{CIbLBd}BDM`}\",[]]Ag[\"{{CLlO`CId}ABNn}\",[]]Aj[\"{{{N`{AHEj}}CI`}Nd}\",[]]An[\"{{{N`{AOCb}}OHdCI`}JFb}\",[]]An[\"{{{N`{BAGj}}OHdCI`}OIb}\",[]]Aj[\"{AJC`{{LFl{BBFn}}}}\",[]]Ai[\"{{{N`{BDLh}}}BGIb}\",[]]Ai[\"{{{N`{BDLl}}}BGId}\",[]]o[\"{CLjCLj}\",[]]o[\"{CLlCLl}\",[]]Ak[\"{{{AIAj{AJBd}}}AJBb}\",[]]Ai[\"{{{AIAj{OJh}}}OHd}\",[]]0Aj[\"{{{AIAj{B@Ij}}}OHd}\",[]]Ak[\"{{{AIAj{BAI`}}}AJBb}\",[]]Ak[\"{{{AIAj{AJBf}}}AJBb}\",[]]Ak[\"{{{AIAj{BAIb}}}AJBb}\",[]]Bg[\"{{{AIAj{AIMh}}LFd}{{LF`{ABKj}}}}\",[]]Aa[\"{AHIbABKj}\",[]]Bg[\"{{{AIAj{AIMj}}LFf}{{LF`{ABKj}}}}\",[]]Aa[\"{AHIdABKj}\",[]]Ah[\"{A@`{{LFn{HJ`}}}}\",[]]Ah[\"{A@b{{LFn{HJ`}}}}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]Aa[\"{B@G`AJAl}\",[]]Aa[\"{ABNhAJAl}\",[]]Aa[\"{BBFlBELh}\",[]]A`[\"{AHIbCId}\",[]]A`[\"{AHIdCId}\",[]]A`[\"{ABKbJEf}\",[]]Am[\"{{{AIAj{CBfABMj}}}HJ`}\",[]]Am[\"{{{AIAj{CBfABLf}}}HJ`}\",[]]Am[\"{{{AIAj{CBfAJ@j}}}HJ`}\",[]]Am[\"{{{AIAj{CBfAJAf}}}HJ`}\",[]]Al[\"{{{AIAj{CBfLCl}}}HJ`}\",[]]A`[\"{cc{}}\",[\"T\"]]000000000000000000000Aa[\"{BCAnBEEb}\",[]]1111111111Aa[\"{AHIhBBFb}\",[]]2Aa[\"{BCAnAHIh}\",[]]Aa[\"{AJ@nAHIh}\",[]]44Aa[\"{BCAnAJ@n}\",[]]5Aa[\"{AHIhBBF`}\",[]]6666Aa[\"{B@IhAJCb}\",[]]Aa[\"{B@ChAJCb}\",[]]Aa[\"{BCAnAJCb}\",[]]9Aa[\"{BCAnB@Ih}\",[]]:::::A`[\"{BELfLHf}\",[]]A`[\"{ABNlLHf}\",[]]o[\"{CIbLHf}\",[]]=o[\"{CI`LHf}\",[]]n[\"{MhLHf}\",[]]???Al[\"{cLFd{{CJn{CIb}}}}\",[\"T\"]]A`[\"{cc{}}\",[\"T\"]]00000Aa[\"{AOBdBCAn}\",[]]A`[\"{JEnBCAn}\",[]]A`[\"{JGfBCAn}\",[]]33333A`[\"{LHfAOCj}\",[]]44444Aa[\"{BCDlAOBf}\",[]]Aa[\"{BFHjAOBf}\",[]]666A`[\"{JGfBCAh}\",[]]7Aa[\"{BELnB@Dd}\",[]]888A`[\"{LHfBELf}\",[]]99999A`[\"{LHfABNl}\",[]]::::::::::::Aa[\"{AOFbAOBl}\",[]];;;;;;;A`[\"{BCBjJGj}\",[]]A`[\"{BGKnJGj}\",[]]=A`[\"{BGJlJGj}\",[]]A`[\"{BGEnJGj}\",[]]A`[\"{BDIfJGj}\",[]]A`[\"{BBD`JGj}\",[]]A`[\"{BDJhJGj}\",[]]A`[\"{AIOhJGj}\",[]]A`[\"{BDL`JGj}\",[]]A`[\"{BBEbJGj}\",[]]A`[\"{BCD`JGj}\",[]]A`[\"{cc{}}\",[\"T\"]]0000000000000000n[\"{MhLHh}\",[]]A`[\"{BELhLHh}\",[]]o[\"{CI`LHh}\",[]]o[\"{CIbLHh}\",[]]A`[\"{ABNnLHh}\",[]]5555Al[\"{cLFf{{CJn{CIb}}}}\",[\"T\"]]66666666666666666Aa[\"{BBFlBCB`}\",[]]7777A`[\"{AHI`JHd}\",[]]88888A`[\"{LHhAOCl}\",[]]999999Aa[\"{BFHlAOBh}\",[]]Aa[\"{BCDlAOBh}\",[]];;;;;;Aa[\"{BBFlBCAl}\",[]]<<<<>>>>>A`[\"{JGnBBFl}\",[]]?A`[\"{LHhBELh}\",[]]A`[\"{cc{}}\",[\"T\"]]0000A`[\"{LHhABNn}\",[]]11111A`[\"{BAIdJGn}\",[]]2222Aa[\"{AOFbAOBn}\",[]]3333333A`[\"{BGJlJGl}\",[]]A`[\"{BDL`JGl}\",[]]5A`[\"{BGKnJGl}\",[]]A`[\"{AIOhJGl}\",[]]A`[\"{BCBjJGl}\",[]]A`[\"{BCD`JGl}\",[]]A`[\"{BDJhJGl}\",[]]A`[\"{BBD`JGl}\",[]]A`[\"{BBEbJGl}\",[]]A`[\"{BDIfJGl}\",[]]A`[\"{BGEnJGl}\",[]]>>>>>>>>>An[\"{{{AIAj{c}}}AJC`CIj}\",[\"S\"]]????????????????Bm[\"{{{AIAj{LHf}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bk[\"{{{AIAj{LFd}}{AIAj{CBfc}}}HJ`LEb}\",[\"H\"]]Bn[\"{{{AIAj{AOCj}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bn[\"{{{AIAj{B@Dj}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bm[\"{{{AIAj{CLj}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bl[\"{{{AIAj{Nn}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bn[\"{{{AIAj{ABNl}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bm[\"{{{AIAj{LHh}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bn[\"{{{AIAj{BEEd}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bk[\"{{{AIAj{LFf}}{AIAj{CBfc}}}HJ`LEb}\",[\"H\"]]Bn[\"{{{AIAj{AHIj}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bn[\"{{{AIAj{BAHh}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bn[\"{{{AIAj{AJCd}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bn[\"{{{AIAj{AOCl}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bn[\"{{{AIAj{BDIl}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bn[\"{{{AIAj{BEE`}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bn[\"{{{AIAj{B@Dl}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bm[\"{{{AIAj{CLl}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bl[\"{{{AIAj{O`}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bn[\"{{{AIAj{ABNn}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Bn[\"{{{AIAj{AHFn}}{AIAj{CBfc}}}HJ`LEb}\",[\"__H\"]]Aa[\"{{}c{}}\",[\"U\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n[\"{{}HJ`}\",[]]0000Cc[\"{{{AIAj{CBfLCl}}LBd}{{LFj{{LF`{JEj}}AHIj}}}}\",[]]El[\"{{{AIAj{OFf}}{AIAj{CBf{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}}{{LFj{{AIAj{CBf{L@`{Mh}}}}AOBl}}}}\",[]]Bh[\"{{{AIAj{CBfOKh}}O`}{{LF`{ABNn}}}}\",[]]El[\"{{{AIAj{OFh}}{AIAj{CBf{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}}{{LFj{{AIAj{CBf{L@`{Mh}}}}AOBn}}}}\",[]]Bf[\"{{{AIAj{AIMh}}LFd}{{LF`{CKl}}}}\",[]]Bf[\"{{{AIAj{CKl}}}{{LFj{HJ`AILj}}}}\",[]]A`[\"{AINdCId}\",[]]Am[\"{{{AIAj{CBfAIMj}}}HJ`}\",[]]A`[\"{AINfCId}\",[]]Ck[\"{{{A@f{{AIAj{CBfAILb}}}}{AIAj{CBfOGb}}}{{CLd{HJ`}}}}\",[]]Dk[\"{{{A@f{{AIAj{CBfBCEb}}}}{AIAj{CBfOGb}}}{{CLd{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@f{{AIAj{CBfABMj}}}}{AIAj{CBfOGb}}}{{CLd{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@f{{AIAj{CBfABMl}}}}{AIAj{CBfOGb}}}{{CLd{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@f{{AIAj{CBfAHEn}}}}{AIAj{CBfOGb}}}{{CLd{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@f{{AIAj{CBfAIMd}}}}{AIAj{CBfOGb}}}{{CLd{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{A@f{{AIAj{CBfOIf}}}}{AIAj{CBfOGb}}}{{CLd{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{A@f{{AIAj{CBfLEf}}}}{AIAj{CBfOGb}}}{{CLd{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@f{{AIAj{CBfAHDj}}}}{AIAj{CBfOGb}}}{{CLd{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@f{{AIAj{CBfABJf}}}}{AIAj{CBfOGb}}}{{CLd{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@f{{AIAj{CBfB@Gl}}}}{AIAj{CBfOGb}}}{{CLd{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@f{{AIAj{CBfB@Hf}}}}{AIAj{CBfOGb}}}{{CLd{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{A@f{{AIAj{CBfLBl}}}}{AIAj{CBfOGb}}}{{CLd{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@f{{AIAj{CBfBBCl}}}}{AIAj{CBfOGb}}}{{CLd{c}}}{}}\",[\"Future::Output\"]]Bf[\"{{{AIAj{CBfAIMj}}}{{LF`{JHd}}}}\",[]]De[\"{{{AIAj{CBfAJ@j}}{AIAj{CBf{L@`{Mh}}}}}{{LFj{{LF`{LBd}}AHIh}}}}\",[]]Bm[\"{{{AIAj{CBfAJ@l}}CMn}{{LFj{LClBAHh}}}}\",[]]Bo[\"{{CLl{AIAj{CBfc}}}{{LFj{BFAhJGn}}}Nj}\",[\"R\"]]Bf[\"{{{AIAj{CBfAHFf}}}{{LF`{JEf}}}}\",[]]Ec[\"{{{AIAj{BBFn}}AJC`{AIAj{CBf{L@`{AINn}}}}{AIAj{CBf{L@`{ABNh}}}}}{{LFl{LBd}}}}\",[]]Dk[\"{{{AIAj{OFf}}{AIAj{CBf{A@l{Mh}}}}{AIAj{{L@`{Mh}}}}}{{LFj{HJ`AOBl}}}}\",[]]Dk[\"{{{AIAj{OFh}}{AIAj{CBf{A@l{Mh}}}}{AIAj{{L@`{Mh}}}}}{{LFj{HJ`AOBn}}}}\",[]]C`[\"{{{AIAj{CBfAHFf}}JEfCMn}{{LFj{HJ`BEEd}}}}\",[]]Cc[\"{{{AIAj{BBFn}}AJC`{AIAj{AC@b}}}{{LFl{HJ`}}}}\",[]]Bn[\"{{{AIAj{CBfBFOl}}CIb}{{AIAj{CBfBFOl}}}}\",[]]Bn[\"{{{AIAj{CBfBFOn}}CIb}{{AIAj{CBfBFOn}}}}\",[]]Aj[\"{{{AIAj{AIMh}}}CLj}\",[]]Aj[\"{{{AIAj{AIMj}}}CLl}\",[]]Cn[\"{{{AIAj{OGn}}{AIAj{{L@`{Mh}}}}{AIAj{CBf{L@`{Mh}}}}}HJ`}\",[]]Cn[\"{{{AIAj{OH`}}{AIAj{{L@`{Mh}}}}{AIAj{CBf{L@`{Mh}}}}}HJ`}\",[]]A`[\"{BBFfCId}\",[]]A`[\"{ABOnLBd}\",[]]A`[\"{AC@`LBd}\",[]]o[\"{JIlCMn}\",[]]Bm[\"{{{AIAj{CBfAJ@j}}LHf}{{LFj{HJ`B@Ch}}}}\",[]]Bm[\"{{{AIAj{CBfBFAj}}LHf}{{LFj{HJ`B@Ch}}}}\",[]]Bm[\"{{{AIAj{CBfAJ@l}}LHh}{{LFj{HJ`B@D`}}}}\",[]]Bn[\"{{{AIAj{AJBl}}{AIAj{CHn}}}{{LF`{JEf}}}}\",[]]Bn[\"{{{AIAj{BBDn}}{AIAj{CHn}}}{{LF`{JEf}}}}\",[]]Bn[\"{{{AIAj{BDLn}}{AIAj{CHn}}}{{LF`{JEf}}}}\",[]]Bn[\"{{{AIAj{AJBn}}{AIAj{CHn}}}{{LF`{JEf}}}}\",[]]Bn[\"{{{AIAj{BBE`}}{AIAj{CHn}}}{{LF`{JEf}}}}\",[]]Bn[\"{{{AIAj{BDM`}}{AIAj{CHn}}}{{LF`{JEf}}}}\",[]]Aa[\"{{}c{}}\",[\"V\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bg[\"{{{N`{BECn}}OHdCI`}{{Nh{AIMl}}}}\",[]]Bg[\"{{{N`{AHEh}}OHdCI`}{{Nh{AIMl}}}}\",[]]Bg[\"{{{N`{AOC`}}OHdCI`}{{Nh{AIMl}}}}\",[]]Bg[\"{{{N`{BAGh}}OHdCI`}{{Nh{AIMl}}}}\",[]]Bg[\"{{{N`{BED`}}OHdCI`}{{Nh{AIMn}}}}\",[]]Bg[\"{{{N`{AHEj}}OHdCI`}{{Nh{AIMn}}}}\",[]]Bg[\"{{{N`{AOCb}}OHdCI`}{{Nh{AIMn}}}}\",[]]Bg[\"{{{N`{BAGj}}OHdCI`}{{Nh{AIMn}}}}\",[]]Ai[\"{BF@l{{N`{BED`}}}}\",[]]o[\"{JEhJEf}\",[]]A`[\"{AC@dCId}\",[]]o[\"{OLdLBd}\",[]]o[\"{JEjJEf}\",[]]A`[\"{AC@fCId}\",[]]o[\"{OLfLBd}\",[]]Aa[\"{AOAlBCB`}\",[]]Ag[\"{{{AIAj{On}}}On}\",[]]Ak[\"{{{AIAj{AOB`}}}AOB`}\",[]]Ak[\"{{{AIAj{AIMh}}}AIMh}\",[]]Ak[\"{{{AIAj{BFI`}}}BFI`}\",[]]Ak[\"{{{AIAj{BEEb}}}BEEb}\",[]]Ak[\"{{{AIAj{ABKn}}}ABKn}\",[]]Ak[\"{{{AIAj{BAEb}}}BAEb}\",[]]Ak[\"{{{AIAj{BBFb}}}BBFb}\",[]]Ak[\"{{{AIAj{AHIh}}}AHIh}\",[]]Ak[\"{{{AIAj{AJ@n}}}AJ@n}\",[]]Ak[\"{{{AIAj{BBF`}}}BBF`}\",[]]Ak[\"{{{AIAj{AJCb}}}AJCb}\",[]]Ak[\"{{{AIAj{B@Ih}}}B@Ih}\",[]]Ai[\"{{{AIAj{LHf}}}LHf}\",[]]Ak[\"{{{AIAj{BFHj}}}BFHj}\",[]]Ai[\"{{{AIAj{LFd}}}LFd}\",[]]Ak[\"{{{AIAj{AC@d}}}AC@d}\",[]]Ak[\"{{{AIAj{AINd}}}AINd}\",[]]Ak[\"{{{AIAj{AHIb}}}AHIb}\",[]]Ak[\"{{{AIAj{BCBf}}}BCBf}\",[]]Ai[\"{{{AIAj{OLd}}}OLd}\",[]]Ak[\"{{{AIAj{B@Ch}}}B@Ch}\",[]]Ak[\"{{{AIAj{BCAn}}}BCAn}\",[]]Ak[\"{{{AIAj{BCDh}}}BCDh}\",[]]Ak[\"{{{AIAj{BEHb}}}BEHb}\",[]]Ak[\"{{{AIAj{BEJ`}}}BEJ`}\",[]]Ak[\"{{{AIAj{AOCj}}}AOCj}\",[]]Ak[\"{{{AIAj{BBCb}}}BBCb}\",[]]Ak[\"{{{AIAj{B@Hl}}}B@Hl}\",[]]Ak[\"{{{AIAj{BFOl}}}BFOl}\",[]]Ak[\"{{{AIAj{B@Bl}}}B@Bl}\",[]]Ak[\"{{{AIAj{AOBf}}}AOBf}\",[]]Ak[\"{{{AIAj{AHG`}}}AHG`}\",[]]Ak[\"{{{AIAj{BCAh}}}BCAh}\",[]]Ak[\"{{{AIAj{BDI`}}}BDI`}\",[]]Ak[\"{{{AIAj{B@Dd}}}B@Dd}\",[]]Ak[\"{{{AIAj{B@Dj}}}B@Dj}\",[]]Ak[\"{{{AIAj{B@Ed}}}B@Ed}\",[]]Ak[\"{{{AIAj{BELf}}}BELf}\",[]]Ak[\"{{{AIAj{AINj}}}AINj}\",[]]Ai[\"{{{AIAj{CLj}}}CLj}\",[]]Ag[\"{{{AIAj{Nn}}}Nn}\",[]]Ak[\"{{{AIAj{ABNl}}}ABNl}\",[]]Ag[\"{{{AIAj{Nb}}}Nb}\",[]]Ak[\"{{{AIAj{AHEh}}}AHEh}\",[]]Ai[\"{{{AIAj{JF`}}}JF`}\",[]]Ak[\"{{{AIAj{AOC`}}}AOC`}\",[]]Ai[\"{{{AIAj{OI`}}}OI`}\",[]]Ak[\"{{{AIAj{BAGh}}}BAGh}\",[]]Ak[\"{{{AIAj{BFHb}}}BFHb}\",[]]Ai[\"{{{AIAj{JGj}}}JGj}\",[]]Ai[\"{{{AIAj{LHh}}}LHh}\",[]]Ak[\"{{{AIAj{BFHl}}}BFHl}\",[]]Ak[\"{{{AIAj{BEEd}}}BEEd}\",[]]Ai[\"{{{AIAj{LFf}}}LFf}\",[]]Ak[\"{{{AIAj{B@Hd}}}B@Hd}\",[]]Ak[\"{{{AIAj{AJ@b}}}AJ@b}\",[]]Ak[\"{{{AIAj{AHI`}}}AHI`}\",[]]Ak[\"{{{AIAj{BEL`}}}BEL`}\",[]]Ak[\"{{{AIAj{AIM`}}}AIM`}\",[]]Ak[\"{{{AIAj{AC@f}}}AC@f}\",[]]Ak[\"{{{AIAj{AINf}}}AINf}\",[]]Ak[\"{{{AIAj{AHId}}}AHId}\",[]]Ak[\"{{{AIAj{BCBh}}}BCBh}\",[]]Ak[\"{{{AIAj{AHIj}}}AHIj}\",[]]Ak[\"{{{AIAj{BAHh}}}BAHh}\",[]]Ai[\"{{{AIAj{OLf}}}OLf}\",[]]Ak[\"{{{AIAj{AJCd}}}AJCd}\",[]]Ak[\"{{{AIAj{AOCh}}}AOCh}\",[]]Ak[\"{{{AIAj{BBFd}}}BBFd}\",[]]Ak[\"{{{AIAj{B@D`}}}B@D`}\",[]]Ak[\"{{{AIAj{BCB`}}}BCB`}\",[]]Ak[\"{{{AIAj{AHHj}}}AHHj}\",[]]Ak[\"{{{AIAj{BBB`}}}BBB`}\",[]]Ak[\"{{{AIAj{BGCb}}}BGCb}\",[]]Ak[\"{{{AIAj{BCDj}}}BCDj}\",[]]Ak[\"{{{AIAj{BEHd}}}BEHd}\",[]]Ak[\"{{{AIAj{BEJb}}}BEJb}\",[]]Ak[\"{{{AIAj{AOCl}}}AOCl}\",[]]Ak[\"{{{AIAj{BBCd}}}BBCd}\",[]]Ak[\"{{{AIAj{B@I`}}}B@I`}\",[]]Ak[\"{{{AIAj{BFOn}}}BFOn}\",[]]Ak[\"{{{AIAj{B@C`}}}B@C`}\",[]]Ak[\"{{{AIAj{AOBh}}}AOBh}\",[]]Ak[\"{{{AIAj{AHGb}}}AHGb}\",[]]Ak[\"{{{AIAj{AIOb}}}AIOb}\",[]]Ak[\"{{{AIAj{AJAn}}}AJAn}\",[]]Ak[\"{{{AIAj{B@Dn}}}B@Dn}\",[]]Ak[\"{{{AIAj{BCAl}}}BCAl}\",[]]Ak[\"{{{AIAj{BDId}}}BDId}\",[]]Ak[\"{{{AIAj{ABKb}}}ABKb}\",[]]Ak[\"{{{AIAj{BDIl}}}BDIl}\",[]]Ak[\"{{{AIAj{B@Df}}}B@Df}\",[]]Ak[\"{{{AIAj{BCCh}}}BCCh}\",[]]Ak[\"{{{AIAj{BGCh}}}BGCh}\",[]]Ak[\"{{{AIAj{ABLl}}}ABLl}\",[]]Ak[\"{{{AIAj{BEE`}}}BEE`}\",[]]Ak[\"{{{AIAj{B@Dl}}}B@Dl}\",[]]Ak[\"{{{AIAj{B@Ef}}}B@Ef}\",[]]Ak[\"{{{AIAj{BBFl}}}BBFl}\",[]]Ak[\"{{{AIAj{BELh}}}BELh}\",[]]Ak[\"{{{AIAj{AINl}}}AINl}\",[]]Ak[\"{{{AIAj{BGOb}}}BGOb}\",[]]Ai[\"{{{AIAj{CLl}}}CLl}\",[]]Ag[\"{{{AIAj{O`}}}O`}\",[]]Ak[\"{{{AIAj{ABNn}}}ABNn}\",[]]Ak[\"{{{AIAj{AHFn}}}AHFn}\",[]]Ak[\"{{{AIAj{BAId}}}BAId}\",[]]Ak[\"{{{AIAj{BFAh}}}BFAh}\",[]]Ai[\"{{{AIAj{JGn}}}JGn}\",[]]Ak[\"{{{AIAj{BFHd}}}BFHd}\",[]]Ai[\"{{{AIAj{JGl}}}JGl}\",[]]Ag[\"{{{AIAj{Nd}}}Nd}\",[]]Ak[\"{{{AIAj{AHEj}}}AHEj}\",[]]Ai[\"{{{AIAj{JFb}}}JFb}\",[]]Ak[\"{{{AIAj{AOCb}}}AOCb}\",[]]Ai[\"{{{AIAj{OIb}}}OIb}\",[]]Ak[\"{{{AIAj{BAGj}}}BAGj}\",[]]Ai[\"{{{AIAj{JHf}}}JHf}\",[]]Ak[\"{{{AIAj{ABNh}}}ABNh}\",[]]Ak[\"{{{AIAj{AC@b}}}AC@b}\",[]]Ak[\"{{{AIAj{B@Eh}}}B@Eh}\",[]]Ak[\"{{{AIAj{BF@l}}}BF@l}\",[]]Ak[\"{{{AIAj{BCC`}}}BCC`}\",[]]Bn[\"{{{AIAj{AIMh}}LHf{AIAj{{L@`{Mh}}}}}HJ`}\",[]]Bn[\"{{{AIAj{ABKn}}LHf{AIAj{{L@`{Mh}}}}}HJ`}\",[]]Bg[\"{{{AIAj{CKl}}}{{LFj{AHEnBBAn}}}}\",[]]Bf[\"{{{AIAj{CBfAIMj}}OHdLHhJEf}HJ`}\",[]]Bj[\"{{{AIAj{B@Dj}}}{{AIAj{{L@`{Mh}}}}}}\",[]]Bj[\"{{{AIAj{B@Dl}}}{{AIAj{{L@`{Mh}}}}}}\",[]]Aa[\"{BAFfAHHj}\",[]]Aa[\"{BBFlAIOb}\",[]]A`[\"{ABNlCId}\",[]]A`[\"{ABNnCId}\",[]]A`[\"{B@EdCMn}\",[]]A`[\"{B@EfCMn}\",[]]l[\"{OHf}\",[]]l[\"{OHh}\",[]]Ce[\"{{{AIAj{BEHn}}{AIAj{CBfNj}}}{{LFj{B@DlBEE`}}}}\",[]]Ce[\"{{{AIAj{BHBd}}{AIAj{CBfNj}}}{{LFj{B@DlBEE`}}}}\",[]]Bc[\"{{{AIAj{AIMj}}}{{A@l{LFf}}}}\",[]]Bd[\"{{{AIAj{JJb}}{AIAj{CHn}}}HJ`}\",[]]Am[\"{{{AIAj{AOBb}}LBd}HJ`}\",[]]Ai[\"{{{AIAj{JJd}}}HJ`}\",[]]Bg[\"{{{A@f{{AIAj{CBfAILb}}}}OHd}HJ`}\",[]]Bm[\"{{{AIAj{CBfAJAf}}LHf}{{LFj{HJ`B@Ch}}}}\",[]]Bm[\"{{{AIAj{CBfAJAh}}LHh}{{LFj{HJ`B@D`}}}}\",[]]Am[\"{ABLf{{LFj{HJ`AJA`}}}}\",[]]Dc[\"{{{AIAj{CBfABL`}}ABLh{AIAj{CBf{A@l{Mh}}}}}{{LFj{AC@`AJAb}}}}\",[]]Bi[\"{{{AIAj{OJh}}{A@f{{Nh{LE`}}}}}HJ`}\",[]]Bj[\"{{{AIAj{B@Ij}}{A@f{{Nh{LE`}}}}}HJ`}\",[]]Bj[\"{{{AIAj{AOEd}}{A@f{{Nh{LE`}}}}}HJ`}\",[]]n[\"{OnCMn}\",[]]Ak[\"{{{AIAj{AIMh}}}BCBf}\",[]]Ak[\"{{{AIAj{ABKn}}}BAEb}\",[]]An[\"{{{AIAj{CBfAIMj}}}BCBh}\",[]]Ce[\"{{{AIAj{CBfAJ@d}}{LF`{LHb}}}{{AIAj{CBfAJ@d}}}}\",[]]Ce[\"{{{AIAj{CBfAJ@f}}{LF`{LHb}}}{{AIAj{CBfAJ@f}}}}\",[]]Ck[\"{{{AIAj{CBfAJAf}}{AIAj{{L@`{Mh}}}}}{{LFj{LBdAJCb}}}}\",[]]Ck[\"{{{AIAj{CBfAJAh}}{AIAj{{L@`{Mh}}}}}{{LFj{LBdAJCd}}}}\",[]]Bl[\"{{{AIAj{BFAh}}{AIAj{CBfc}}}HJ`LCj}\",[\"W\"]]Aj[\"{{{AIAj{ABKn}}}LBl}\",[]]An[\"{ABLf{{LFj{AIMdBCAn}}}}\",[]]Bh[\"{{{AIAj{CBfOKh}}O`}{{LF`{ABNn}}}}\",[]]Fh[\"{{{AIAj{CBfABL`}}ABLhOHd{AIAj{CBf{A@l{Mh}}}}{LF`{{N`{B@I`}}}}}{{LFj{{LBb{BDIlAIMj}}{Nh{AOAl}}}}}}\",[]]Am[\"{AIAj{{AIAj{c}}}{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000o[\"{OLdLBd}\",[]]o[\"{OLfLBd}\",[]]o[\"{OnAOB`}\",[]]Bb[\"{{AJAlBBAlOn}{{LFn{AOBb}}}}\",[]]Aj[\"{AJAl{{LFl{ABKn}}}}\",[]]B`[\"{{}{{LFj{HJ`{Nh{JFl}}}}}}\",[]]Ak[\"{{{AIAj{AIMh}}}BCAn}\",[]]Be[\"{{{AIAj{ABL`}}}{{AIAj{BBCd}}}}\",[]]o[\"{MhBELf}\",[]]A`[\"{AINdCId}\",[]]o[\"{B@HlN`}\",[]]o[\"{MhBELh}\",[]]A`[\"{AINfCId}\",[]]o[\"{B@I`N`}\",[]]A`[\"{BBFlLF`}\",[]]Bl[\"{{{AIAj{CBfc}}}{{LFj{LHfBAId}}}Nj}\",[\"B\"]]Bl[\"{{{AIAj{CBfc}}}{{LFj{LFdBAId}}}Nj}\",[\"B\"]]Bm[\"{{{AIAj{CBfc}}}{{LFj{AHG`BAId}}}Nj}\",[\"B\"]]Bm[\"{{{AIAj{CBfc}}}{{LFj{BELfBAId}}}Nj}\",[\"B\"]]Bm[\"{{{AIAj{CBfc}}}{{LFj{ABNlBAId}}}Nj}\",[\"B\"]]Bh[\"{{{AIAj{CBfc}}}{{LGb{LHh}}}Nj}\",[\"B\"]]Bh[\"{{{AIAj{CBfc}}}{{LGb{LFf}}}Nj}\",[\"B\"]]Bi[\"{{{AIAj{CBfc}}}{{LGb{AHGb}}}Nj}\",[\"B\"]]Bi[\"{{{AIAj{CBfc}}}{{LGb{AIOb}}}Nj}\",[\"B\"]]Ek[\"{{{AIAj{CBf{LDn{ABJh}}}}{AIAj{c}}{AIAj{{L@`{CIb}}}}CMn}{{LFj{BCChBEE`}}}{BEHnJJ`}}\",[\"\"]]Bi[\"{{{AIAj{CBfc}}}{{LGb{BELh}}}Nj}\",[\"B\"]]Bi[\"{{{AIAj{CBfc}}}{{LGb{ABNn}}}Nj}\",[\"B\"]]Bi[\"{{{AIAj{CBfc}}}{{LGb{AHFh}}}Nj}\",[\"B\"]]A`[\"{ABNhLF`}\",[]]Bk[\"{{{AIAj{LHf}}{AIAj{CBfc}}}HJ`LCj}\",[\"B\"]]Bk[\"{{{AIAj{LFd}}{AIAj{CBfc}}}HJ`LCj}\",[\"B\"]]Bl[\"{{{AIAj{AHG`}}{AIAj{CBfc}}}HJ`LCj}\",[\"B\"]]Bl[\"{{{AIAj{BELf}}{AIAj{CBfc}}}HJ`LCj}\",[\"B\"]]Bl[\"{{{AIAj{ABNl}}{AIAj{CBfc}}}HJ`LCj}\",[\"B\"]]Bk[\"{{{AIAj{LHh}}{AIAj{CBfc}}}HJ`LCj}\",[\"B\"]]Bk[\"{{{AIAj{LFf}}{AIAj{CBfc}}}HJ`LCj}\",[\"B\"]]Bl[\"{{{AIAj{AHGb}}{AIAj{CBfc}}}HJ`LCj}\",[\"B\"]]Bl[\"{{{AIAj{AIOb}}{AIAj{CBfc}}}HJ`LCj}\",[\"B\"]]Bl[\"{{{AIAj{ABKb}}{AIAj{CBfc}}}HJ`LCj}\",[\"B\"]]Bl[\"{{{AIAj{BELh}}{AIAj{CBfc}}}HJ`LCj}\",[\"B\"]]Bl[\"{{{AIAj{ABNn}}{AIAj{CBfc}}}HJ`LCj}\",[\"B\"]]Bl[\"{{{AIAj{AHFj}}{AIAj{CBfc}}}HJ`LCj}\",[\"B\"]]Bj[\"{{{AIAj{CBfAJAf}}}{{LFj{HJ`B@Ch}}}}\",[]]Bj[\"{{{AIAj{CBfAJAh}}}{{LFj{HJ`AOCh}}}}\",[]]An[\"{{{AIAj{AOF`}}ABKj}HJ`}\",[]]Ea[\"{{{AIAj{CBfABL`}}OHdAHFn{LF`{B@Ef}}ABJh{AIAj{CBf{A@l{Mh}}}}}{{LF`{BADn}}}}\",[]]o[\"{CK`OHf}\",[]]o[\"{CKbOHh}\",[]]A`[\"{ABLfHJ`}\",[]]Ba[\"{{{AIAj{CBfABL`}}ABLh}HJ`}\",[]]Bh[\"{{{AIAj{AJBl}}{AIAj{CHn}}JEf}HJ`}\",[]]Bh[\"{{{AIAj{BBDn}}{AIAj{CHn}}JEf}HJ`}\",[]]Bh[\"{{{AIAj{BDLn}}{AIAj{CHn}}JEf}HJ`}\",[]]Bh[\"{{{AIAj{AJBn}}{AIAj{CHn}}JEf}HJ`}\",[]]Bh[\"{{{AIAj{BBE`}}{AIAj{CHn}}JEf}HJ`}\",[]]Bh[\"{{{AIAj{BDM`}}{AIAj{CHn}}JEf}HJ`}\",[]]A`[\"{AHF`CMn}\",[]]o[\"{JEhCId}\",[]]o[\"{JEjCId}\",[]]Ce[\"{{{AIAj{CBfAIMl}}OHdOHdCIdCMn{AIAj{B@Hb}}}HJ`}\",[]]Cc[\"{{{AIAj{CBfNb}}OHdOHdCIdCMn{AIAj{B@Hb}}}HJ`}\",[]]Cd[\"{{{AIAj{CBfJF`}}OHdOHdCIdCMn{AIAj{B@Hb}}}HJ`}\",[]]Cd[\"{{{AIAj{CBfOI`}}OHdOHdCIdCMn{AIAj{B@Hb}}}HJ`}\",[]]Ce[\"{{{AIAj{CBfAIMn}}OHdOHdCIdCMn{AIAj{B@Hd}}}HJ`}\",[]]Cc[\"{{{AIAj{CBfNd}}OHdOHdCIdCMn{AIAj{B@Hd}}}HJ`}\",[]]Cd[\"{{{AIAj{CBfJFb}}OHdOHdCIdCMn{AIAj{B@Hd}}}HJ`}\",[]]Cd[\"{{{AIAj{CBfOIb}}OHdOHdCIdCMn{AIAj{B@Hd}}}HJ`}\",[]]o[\"{CK`OHf}\",[]]o[\"{CKbOHh}\",[]]o[\"{BDMfMh}\",[]]A`[\"{BCAhJEf}\",[]]A`[\"{BDI`JEf}\",[]]A`[\"{BCAlJEf}\",[]]A`[\"{BDIdJEf}\",[]]A`[\"{BBFlLHb}\",[]]Aa[\"{BBBdBCB`}\",[]]Bg[\"{{{AIAj{ABKn}}AHJ`}{{LFl{HJ`}}}}\",[]]A`[\"{ABLfHJ`}\",[]]Cf[\"{{{AIAj{CBfABL`}}ABLh{AIAj{CBf{A@l{Mh}}}}}AC@`}\",[]]l[\"{OHf}\",[]]Aa[\"{AHFnAJAl}\",[]]l[\"{OHh}\",[]]Am[\"{{ABKlOn}{{LFn{HJ`}}}}\",[]]B`[\"{{B@HlAJAl}{{LFl{ABKn}}}}\",[]]Bb[\"{B@Hj{{LFj{HJ`{Nh{JFl}}}}}}\",[]]Bk[\"{{{AIAj{BEEb}}}{{LF`{{AIAj{JFl}}}}}}\",[]]Bk[\"{{{AIAj{BBFb}}}{{LF`{{AIAj{JFl}}}}}}\",[]]Bk[\"{{{AIAj{AHIh}}}{{LF`{{AIAj{JFl}}}}}}\",[]]Bk[\"{{{AIAj{AJ@n}}}{{LF`{{AIAj{JFl}}}}}}\",[]]Bk[\"{{{AIAj{BBF`}}}{{LF`{{AIAj{JFl}}}}}}\",[]]Bk[\"{{{AIAj{AJCb}}}{{LF`{{AIAj{JFl}}}}}}\",[]]Bk[\"{{{AIAj{B@Ih}}}{{LF`{{AIAj{JFl}}}}}}\",[]]Bk[\"{{{AIAj{BCAn}}}{{LF`{{AIAj{JFl}}}}}}\",[]]Bk[\"{{{AIAj{BCB`}}}{{LF`{{AIAj{JFl}}}}}}\",[]]A`[\"{ABOnLF`}\",[]]A`[\"{AC@`LF`}\",[]]A`[\"{AC@bLF`}\",[]]Bg[\"{{{AIAj{CKl}}}{{LFj{AJ@`AILj}}}}\",[]]Aa[\"{B@GnAJ@b}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]A`[\"{ABNhLBd}\",[]]Aa[\"{AHIbAC@d}\",[]]Aa[\"{BCBfAC@d}\",[]]Aa[\"{AHIdAC@f}\",[]]Aa[\"{BCBhAC@f}\",[]]3210Dh[\"{{{AIAj{OGn}}{AIAj{{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}}{{LFj{HJ`AOBl}}}}\",[]]Dh[\"{{{AIAj{OH`}}{AIAj{{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}}{{LFj{HJ`AOBn}}}}\",[]]Aj[\"{{{AIAj{AIMl}}}CId}\",[]]Ah[\"{{{AIAj{Nb}}}CId}\",[]]Ai[\"{{{AIAj{JF`}}}CId}\",[]]Ai[\"{{{AIAj{OI`}}}CId}\",[]]Aj[\"{{{AIAj{AIMn}}}CId}\",[]]Ah[\"{{{AIAj{Nd}}}CId}\",[]]Ai[\"{{{AIAj{JFb}}}CId}\",[]]Ai[\"{{{AIAj{OIb}}}CId}\",[]]Aj[\"{{{AIAj{BFNj}}}LBd}\",[]]Aj[\"{{{AIAj{BFNl}}}LBd}\",[]]Aj[\"{{{AIAj{BGNh}}}LBd}\",[]]Aj[\"{{{AIAj{BGOb}}}LBd}\",[]]n[\"{OnLBd}\",[]]n[\"{{}OG`}\",[]]0000o[\"{NlABJl}\",[]]Ba[\"{{AIAj{AIAj{c}}}ABMn{}}\",[\"K\"]]0000000000000000000Cg[\"{{{AIAj{ABKn}}AJAl{AIAj{CHn}}}{{LFj{AIMdB@Dd}}}}\",[]]Dl[\"{{{AIAj{CBfABL`}}OHdB@C`AJAl{AIAj{CHn}}}{{LFj{{LBb{BDIlAIMj}}B@Df}}}}\",[]]Dm[\"{{{AIAj{AHHd}}LFdCId{AIAj{{L@`{Mh}}}}{AIAj{CBfABJh}}}{{LFj{HJ`AOBl}}}}\",[]]Ca[\"{{{AIAj{AHGf}}LBd{AIAj{CBf{L@`{Mh}}}}}HJ`}\",[]]Dm[\"{{{AIAj{AHHf}}LFfCId{AIAj{{L@`{Mh}}}}{AIAj{CBfABJh}}}{{LFj{HJ`AOBn}}}}\",[]]Ca[\"{{{AIAj{AHGh}}LBd{AIAj{CBf{L@`{Mh}}}}}HJ`}\",[]]o[\"{{}AOEn}\",[]]n[\"{{}JJb}\",[]]o[\"{{}AOBb}\",[]]o[\"{{}BAD`}\",[]]o[\"{{}BAEb}\",[]]n[\"{{}LHf}\",[]]n[\"{{}LFd}\",[]]o[\"{{}AC@d}\",[]]o[\"{{}AINd}\",[]]o[\"{{}AHIb}\",[]]o[\"{{}BCBf}\",[]]n[\"{{}OLd}\",[]]o[\"{{}B@Ch}\",[]]o[\"{{}BCDh}\",[]]o[\"{{}BEHb}\",[]]o[\"{{}BEJ`}\",[]]o[\"{{}AOCj}\",[]]o[\"{{}BBCb}\",[]]o[\"{{}BFOl}\",[]]o[\"{{}BDLn}\",[]]o[\"{{}AHEh}\",[]]o[\"{{}AOC`}\",[]]o[\"{{}BAGh}\",[]]o[\"{{}BEDb}\",[]]o[\"{{}BADb}\",[]]o[\"{{}BGNh}\",[]]n[\"{{}LHh}\",[]]n[\"{{}LFf}\",[]]o[\"{{}AJ@b}\",[]]o[\"{{}AIM`}\",[]]o[\"{{}AC@f}\",[]]o[\"{{}AINf}\",[]]o[\"{{}AHId}\",[]]o[\"{{}BCBh}\",[]]n[\"{{}OLf}\",[]]o[\"{{}BBFd}\",[]]o[\"{{}B@D`}\",[]]o[\"{{}BCDj}\",[]]o[\"{{}BEHd}\",[]]o[\"{{}BEJb}\",[]]o[\"{{}AOCl}\",[]]o[\"{{}BBCd}\",[]]o[\"{{}BFOn}\",[]]o[\"{{}BGOb}\",[]]o[\"{{}BDM`}\",[]]o[\"{{}AHEj}\",[]]o[\"{{}AOCb}\",[]]o[\"{{}BAGj}\",[]]o[\"{{}BEDd}\",[]]o[\"{{}ABNh}\",[]]n[\"{{}JJd}\",[]]o[\"{{}BCC`}\",[]]o[\"{{}BAE`}\",[]]Ak[\"{{{AIAj{BAGn}}}B@Dl}\",[]]Ak[\"{{{AIAj{BCCh}}}B@Dl}\",[]]Aa[\"{BGChB@Dl}\",[]]Aa[\"{BELjB@Dl}\",[]]A`[\"{CKdB@Dl}\",[]]A`[\"{JIhB@Dl}\",[]]A`[\"{JIlB@Dl}\",[]]Aa[\"{BDMfB@Dl}\",[]]Cg[\"{{{AIAj{AHHd}}LFdCId{AIAj{CBf{L@`{Mh}}}}LBd}HJ`}\",[]]Ca[\"{{{AIAj{AHGf}}LBd{AIAj{CBf{L@`{Mh}}}}}HJ`}\",[]]Cg[\"{{{AIAj{AHHf}}LFfCId{AIAj{CBf{L@`{Mh}}}}LBd}HJ`}\",[]]Ca[\"{{{AIAj{AHGh}}LBd{AIAj{CBf{L@`{Mh}}}}}HJ`}\",[]]Aj[\"{{{AIAj{AJ@j}}}CMn}\",[]]Aj[\"{{{AIAj{BFAj}}}CMn}\",[]]Ak[\"{{{AIAj{AIMl}}}BEDb}\",[]]Ai[\"{{{AIAj{Nb}}}BEDb}\",[]]Aj[\"{{{AIAj{JF`}}}BEDb}\",[]]Aj[\"{{{AIAj{OI`}}}BEDb}\",[]]Ak[\"{{{AIAj{AIMn}}}BEDd}\",[]]Ai[\"{{{AIAj{Nd}}}BEDd}\",[]]Aj[\"{{{AIAj{JFb}}}BEDd}\",[]]Aj[\"{{{AIAj{OIb}}}BEDd}\",[]]Bn[\"{{{AIAj{CBfBCDh}}CI`}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}CI`}{{AIAj{CBfBCDj}}}}\",[]]Bf[\"{{{AIAj{CBfAIMl}}OHdCIdCId}HJ`}\",[]]Bd[\"{{{AIAj{CBfNb}}OHdCIdCId}HJ`}\",[]]Bf[\"{{{AIAj{CBfAIMn}}OHdCIdCId}HJ`}\",[]]Bd[\"{{{AIAj{CBfNd}}OHdCIdCId}HJ`}\",[]]Aj[\"{{{AIAj{AIMh}}}LEf}\",[]]Bc[\"{{{AIAj{ABMl}}}{{LF`{LFd}}}}\",[]]Aa[\"{BGChB@Dl}\",[]]Aa[\"{BELjB@Dl}\",[]]A`[\"{CKdB@Dl}\",[]]A`[\"{JIhB@Dl}\",[]]Aa[\"{BDMfB@Dl}\",[]]Di[\"{{{AIAj{AJAf}}}{{`{{LE`{}{{LFb{{LFj{{LF`{LHf}}B@Ih}}}}}}CLhCLn}}}}\",[]]Bn[\"{{{AIAj{AJAh}}}{{LFj{{LF`{LHh}}B@D`}}}}\",[]]n[\"{OnLBd}\",[]]o[\"{JJbLBd}\",[]]Am[\"{{{AIAj{CBfAIMj}}}OKh}\",[]]Aj[\"{{{AIAj{AHHd}}}LBd}\",[]]Aj[\"{{{AIAj{AHHf}}}LBd}\",[]]A`[\"{AIAjLHd}\",[]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bd[\"{{{AIAj{BFI`}}}{{LF`{AIMh}}}}\",[]]Bn[\"{{{AIAj{CBfB@Bl}}CIb}{{AIAj{CBfB@Bl}}}}\",[]]Bn[\"{{{AIAj{CBfB@C`}}CIb}{{AIAj{CBfB@C`}}}}\",[]]A`[\"{BGChCIb}\",[]]A`[\"{BELjCIb}\",[]]o[\"{CKdCIb}\",[]]o[\"{JIhCIb}\",[]]A`[\"{AHF`LF`}\",[]]Aa[\"{AC@bAIAj}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]Aa[\"{BBFfABKj}\",[]]A`[\"{LClBBFd}\",[]]Aa[\"{BCBfAINd}\",[]]Aa[\"{BCBhAINf}\",[]]10Bg[\"{{{AIAj{CBfc}}LBd}B@Dj{NjJJ`}}\",[\"\"]]Bg[\"{{{AIAj{CBfc}}LBd}B@Dl{NjJJ`}}\",[\"\"]]o[\"{{}BCCj}\",[]]o[\"{{}BCCl}\",[]]A`[\"{CIdBGNh}\",[]]Cd[\"{{{AIAj{CHn}}}{{LFn{AOB`c}}}{}}\",[\"FromStr::Err\"]]o[\"{CIbLHf}\",[]]o[\"{CIbLHh}\",[]]Al[\"{CId{{LFj{LHfBFHj}}}}\",[]]Al[\"{CId{{LFj{LHhBFHl}}}}\",[]]Aa[\"{BFGdAOBj}\",[]]Aa[\"{BHBfBAEn}\",[]]Aa[\"{BFGfAOBj}\",[]]Aa[\"{BHBhBAEn}\",[]]Af[\"{{}{{LF`{Ab}}}}\",[]]0000Aj[\"{{{AIAj{AIMj}}}CMn}\",[]]Bo[\"{{{AIAj{CBfBEJ`}}ABKj}{{AIAj{CBfBEJ`}}}}\",[]]Bo[\"{{{AIAj{CBfBEJb}}ABKj}{{AIAj{CBfBEJb}}}}\",[]]Ao[\"{{{Nh{AIMl}}}{{Nh{Mn}}}}\",[]]Am[\"{{{Nh{Nb}}}{{Nh{Mn}}}}\",[]]An[\"{{{Nh{JF`}}}{{Nh{Mn}}}}\",[]]An[\"{{{Nh{OI`}}}{{Nh{Mn}}}}\",[]]Ao[\"{{{Nh{AIMn}}}{{Nh{Mn}}}}\",[]]Am[\"{{{Nh{Nd}}}{{Nh{Mn}}}}\",[]]An[\"{{{Nh{JFb}}}{{Nh{Mn}}}}\",[]]An[\"{{{Nh{OIb}}}{{Nh{Mn}}}}\",[]]Aj[\"{{{AIAj{BFI`}}}CMn}\",[]]Bo[\"{{{AIAj{CBfBFOl}}ABKj}{{AIAj{CBfBFOl}}}}\",[]]Bo[\"{{{AIAj{CBfBFOn}}ABKj}{{AIAj{CBfBFOn}}}}\",[]]Bc[\"{{{AIAj{AIMd}}}{{LF`{LEd}}}}\",[]]Bc[\"{{{AIAj{AIMh}}}{{LF`{LEd}}}}\",[]]Bc[\"{{{AIAj{ABLf}}}{{LF`{LEd}}}}\",[]]Bc[\"{{{AIAj{ABLh}}}{{LF`{LEd}}}}\",[]]A`[\"{AHFnLF`}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]Bc[\"{{{AIAj{AHFf}}}{{LF`{LBd}}}}\",[]]Am[\"{{{AIAj{AOF`}}LBd}HJ`}\",[]]Aj[\"{{{AIAj{AIMh}}}OIf}\",[]]Bg[\"{{{AIAj{AJAf}}}{{LFj{CA`B@Ch}}}}\",[]]Bg[\"{{{AIAj{AJAh}}}{{LFj{CA`B@D`}}}}\",[]]A`[\"{BAEfLF`}\",[]]A`[\"{BAEhLF`}\",[]]A`[\"{AHF`LF`}\",[]]A`[\"{AOAlLF`}\",[]]Ck[\"{{{AIAj{CBfBBCb}}{LF`{{JJj{Mh}}}}}{{AIAj{CBfBBCb}}}}\",[]]Ck[\"{{{AIAj{CBfBBCd}}{LF`{{JJj{Mh}}}}}{{AIAj{CBfBBCd}}}}\",[]]A`[\"{BEDbLF`}\",[]]A`[\"{BEDdLF`}\",[]]Ac[\"{AIAjc{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Ao[\"{c{{LFj{e}}}{}{}}\",[\"U\",\"T\"]]000000000000000000000000000000000000000000000Al[\"{LBd{{LFj{LHfBFHj}}}}\",[]]Al[\"{HIn{{LFj{LHfBFHj}}}}\",[]]Al[\"{CId{{LFj{LHfBFHj}}}}\",[]]3333333333333333Aj[\"{ABKj{{LFj{AOCj}}}}\",[]]44444Ai[\"{CId{{LFj{AHG`}}}}\",[]]5555555555555555555555555555Aj[\"{B@Bh{{LFj{BDLf}}}}\",[]]Bb[\"{{{N`{B@Bh}}}{{LFj{BDLf}}}}\",[]]7Aj[\"{B@Hh{{LFj{BDLj}}}}\",[]]Bb[\"{{{N`{B@Hh}}}{{LFj{BDLj}}}}\",[]]9999999999999999999Al[\"{CId{{LFj{LHhBFHl}}}}\",[]]Al[\"{LBd{{LFj{LHhBFHl}}}}\",[]]Al[\"{HIn{{LFj{LHhBFHl}}}}\",[]]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Ce[\"{{{N`{B@Bh}}}{{LFj{BDLhc}}}{}}\",[\"TryFrom::Error\"]]Bm[\"{B@Bh{{LFj{BDLhc}}}{}}\",[\"TryFrom::Error\"]]Ce[\"{{{N`{B@Hh}}}{{LFj{BDLlc}}}{}}\",[\"TryFrom::Error\"]]Bm[\"{B@Hh{{LFj{BDLlc}}}{}}\",[\"TryFrom::Error\"]]Ao[\"{c{{LFj{e}}}{}{}}\",[\"U\",\"T\"]]000000000000000000000000000Aj[\"{{}{{LFj{c}}}{}}\",[\"U\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cd[\"{{{A@f{{AIAj{CBfc}}}}{AIAj{CBfOGb}}}CLd{}}\",[\"F\"]]000000000Cc[\"{{{AIAj{BBFn}}AJC`{AIAj{AC@b}}}{{LFl{HJ`}}}}\",[]]Bk[\"{{{AIAj{BFNj}}B@Dj}{{LFj{HJ`AINj}}}}\",[]]Bk[\"{{{AIAj{BFNl}}B@Dl}{{LFj{HJ`AINl}}}}\",[]]0Bk[\"{{{AIAj{BGNh}}B@Dl}{{LFj{HJ`AINl}}}}\",[]]Ak[\"{{{AIAj{AIMh}}}ABJf}\",[]]Bi[\"{{{AIAj{CBfB@Il}}}{{LFj{LHfJGh}}}}\",[]]Bi[\"{{{AIAj{CBfB@Il}}}{{LFj{CLjJGh}}}}\",[]]Bh[\"{{{AIAj{CBfB@Il}}}{{LFj{NnJGh}}}}\",[]]Bj[\"{{{AIAj{CBfB@Il}}}{{LFj{ABNlJGh}}}}\",[]]Bf[\"{{{AIAj{CBfB@Il}}}{{LG`{LHh}}}}\",[]]Bf[\"{{{AIAj{CBfB@Il}}}{{LG`{CLl}}}}\",[]]Be[\"{{{AIAj{CBfB@Il}}}{{LG`{O`}}}}\",[]]Bg[\"{{{AIAj{CBfB@Il}}}{{LG`{ABNn}}}}\",[]]Aa[\"{AOB`BFHh}\",[]]Bc[\"{{{AIAj{AIMl}}}{{Nh{AIMl}}}}\",[]]Ba[\"{{{AIAj{Nb}}}{{Nh{AIMl}}}}\",[]]Bb[\"{{{AIAj{JF`}}}{{Nh{AIMl}}}}\",[]]Bb[\"{{{AIAj{OI`}}}{{Nh{AIMl}}}}\",[]]Bc[\"{{{AIAj{AIMn}}}{{Nh{AIMn}}}}\",[]]Ba[\"{{{AIAj{Nd}}}{{Nh{AIMn}}}}\",[]]Bb[\"{{{AIAj{JFb}}}{{Nh{AIMn}}}}\",[]]Bb[\"{{{AIAj{OIb}}}{{Nh{AIMn}}}}\",[]]A`[\"{AC@dCId}\",[]]An[\"{{{AIAj{CBfAIMj}}}AHFf}\",[]]A`[\"{AC@fCId}\",[]]C`[\"{{{AIAj{CBfB@Dj}}}{{AIAj{CBf{L@`{Mh}}}}}}\",[]]C`[\"{{{AIAj{CBfB@Dl}}}{{AIAj{CBf{L@`{Mh}}}}}}\",[]]Ah[\"{Mh{{LF`{B@Ed}}}}\",[]]Ah[\"{Mh{{LF`{B@Ef}}}}\",[]]Ah[\"{Mh{{LF`{B@Eh}}}}\",[]]A`[\"{ABNlCLj}\",[]]A`[\"{ABNnCLl}\",[]]Bi[\"{AIMd{{LFj{{LBb{AIMhBCEb}}AIMd}}}}\",[]]o[\"{CLjCMn}\",[]]o[\"{CLlCMn}\",[]]Aj[\"{{{AIAj{AIMj}}}CMn}\",[]]21Aj[\"{{{AIAj{ABLf}}}CMn}\",[]]Aj[\"{{{AIAj{ABLh}}}CMn}\",[]]Bn[\"{{{AIAj{CBfB@Hl}}CMn}{{AIAj{CBfB@Hl}}}}\",[]]Bn[\"{{{AIAj{CBfB@I`}}CMn}{{AIAj{CBfB@I`}}}}\",[]]Bl[\"{{{AIAj{OJh}}OHd}{{A@f{{Nh{AILb}}}}}}\",[]]Bm[\"{{{AIAj{B@Ij}}OHd}{{A@f{{Nh{AILb}}}}}}\",[]]Bm[\"{{{AIAj{AOEd}}OHd}{{A@f{{Nh{AILb}}}}}}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]Ak[\"{{{AIAj{AIMh}}}ABMj}\",[]]Bc[\"{{{AIAj{AIMh}}AJAlAJ@`}ABMl}\",[]]Ce[\"{{{AIAj{CBfAIMj}}AHFnAJ@bOHd}{{LFj{LFfAHHj}}}}\",[]]43Bm[\"{{{AIAj{CBfAIMj}}LFf}{{LFj{HJ`AIM`}}}}\",[]]E`[\"{{{A@f{{AIAj{CBfBDHl}}}}{AIAj{CBfOGb}}}{{CLd{{LF`{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{A@f{{AIAj{CBfAJ@j}}}}{AIAj{CBfOGb}}{AIAj{CBfOJb}}}{{CLd{{LFl{HJ`}}}}}}\",[]]Ec[\"{{{AIAj{CBfAJ@j}}{AIAj{CBfOGb}}{AIAj{CBf{L@`{Mh}}}}}{{CLd{{LFj{LBdAHIh}}}}}}\",[]]Ef[\"{{{A@f{{AIAj{CBfAJ@j}}}}{AIAj{CBfOGb}}{AIAj{CBf{L@`{Mh}}}}}{{CLd{{LFl{LBd}}}}}}\",[]]Fg[\"{{{AIAj{CBfBBAj}}{AIAj{CBfOGb}}{AIAj{CBf{L@`{AINn}}}}{AIAj{CBf{L@`{ABNh}}}}}{{CLd{{LFl{LBd}}}}}}\",[]]Dn[\"{{{A@f{{AIAj{CBfAHIn}}}}{AIAj{AC@b}}{AIAj{CBfOGb}}}{{CLd{{LFl{HJ`}}}}}}\",[]];:Cd[\"{{{AIAj{CBfBBCb}}{N`{OGn}}}{{AIAj{CBfBBCb}}}}\",[]]Cd[\"{{{AIAj{CBfBBCd}}{N`{OH`}}}{{AIAj{CBfBBCd}}}}\",[]]Cj[\"{{{AIAj{B@Hn}}CIbB@Dj{AIAj{{L@`{Mh}}}}}{{JJj{Mh}}}}\",[]]Cj[\"{{{AIAj{BDLj}}CIbB@Dj{AIAj{{L@`{Mh}}}}}{{JJj{Mh}}}}\",[]]Cj[\"{{{AIAj{B@Ib}}CIbB@Dl{AIAj{{L@`{Mh}}}}}{{JJj{Mh}}}}\",[]]Cj[\"{{{AIAj{BDLl}}CIbB@Dl{AIAj{{L@`{Mh}}}}}{{JJj{Mh}}}}\",[]]Cj[\"{{{AIAj{BGId}}CIbB@Dh{AIAj{{L@`{Mh}}}}}{{JJj{Mh}}}}\",[]]Bb[\"{LBd{{LBb{LBd{LF`{LBd}}}}}}\",[]]000Aj[\"{{{AIAj{AIMh}}}LBd}\",[]]A`[\"{AIAjLHb}\",[]]0000000000000000000000000000000000000000000000000000000000000Ce[\"{{{AIAj{CBfB@Hl}}{N`{BEDf}}}{{AIAj{CBfB@Hl}}}}\",[]]Ce[\"{{{AIAj{CBfB@I`}}{N`{BEDh}}}{{AIAj{CBfB@I`}}}}\",[]]A`[\"{BGChJHn}\",[]]o[\"{B@HlN`}\",[]]o[\"{B@I`N`}\",[]]Aj[\"{{{AIAj{ABKn}}}HJ`}\",[]]Ck[\"{{{AIAj{CBfAJAf}}{AIAj{{L@`{Mh}}}}}{{LFj{HJ`AJCb}}}}\",[]]n[\"{{}LBd}\",[]]Ak[\"{{{AIAj{AIMh}}}AHDj}\",[]]Bn[\"{{{AIAj{CBfBCDh}}CMn}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}CMn}{{AIAj{CBfBCDj}}}}\",[]]Bi[\"{{{AIAj{CBf}}}{{AIAj{CBfc}}}{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bc[\"{{AIAj{AIAj{CBfc}}}HJ`{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cc[\"{{{AIAj{CBfAIMj}}OHdLFfLHh}{{LFj{HJ`BBB`}}}}\",[]]A`[\"{AHF`CMn}\",[]]B`[\"{{AIAj{AIAj{c}}}CMn{}}\",[\"K\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Aa[\"{BCAhBELf}\",[]]A`[\"{BDI`LHf}\",[]]Aa[\"{BCAlBELh}\",[]]A`[\"{BDIdLHh}\",[]]o[\"{LDdLHh}\",[]]o[\"{OJnLHh}\",[]]Aa[\"{BCAhAIO`}\",[]]Aa[\"{BCAlAIOb}\",[]]o[\"{LHfCId}\",[]]o[\"{LHhCId}\",[]]Aj[\"{{{AIAj{AIMj}}}CMn}\",[]]Aj[\"{{{AIAj{BAE`}}}CMn}\",[]]Bd[\"{{{AIAj{BBAj}}}{{LFl{AJAl}}}}\",[]]Bd[\"{{{AIAj{ABKn}}}{{LFl{AJAl}}}}\",[]]A`[\"{AHIbCId}\",[]]A`[\"{AHIdCId}\",[]]Cb[\"{{{AIAj{BCC`}}{AIAj{AJAd}}CId}{{N`{AOF`}}}}\",[]]Bn[\"{{{AIAj{CBfBCDh}}CMn}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}CMn}{{AIAj{CBfBCDj}}}}\",[]]Bg[\"{{{AIAj{AIMh}}LFd}{{LF`{AHIb}}}}\",[]]Bj[\"{{{AIAj{CBfAIMj}}LFf}{{LF`{AHId}}}}\",[]]Aa[\"{AHDfAHId}\",[]]Db[\"{{{A@f{{AIAj{CBfAJAf}}}}{AIAj{CBfOGb}}}{{CLd{{LFl{HJ`}}}}}}\",[]]00Eg[\"{{{A@f{{AIAj{CBfAJAf}}}}{AIAj{CBfOGb}}{AIAj{{L@`{Mh}}}}}{{CLd{{LFj{LBdAJCb}}}}}}\",[]]Ec[\"{{{A@f{{AIAj{CBfAJAf}}}}{AIAj{CBfOGb}}{AIAj{{L@`{Mh}}}}}{{CLd{{LFl{LBd}}}}}}\",[]]0Bm[\"{{{AIAj{JJd}}{AIAj{CKj}}}{{LFl{HJ`}}}}\",[]]Cd[\"{{{AIAj{CBfAJ@j}}LBd}{{LFj{{LF`{JEh}}AHIh}}}}\",[]]Cd[\"{{{AIAj{CBfBFAj}}LBd}{{LFj{{LF`{JEh}}AHIh}}}}\",[]]Cn[\"{{{AIAj{CBfAJ@j}}{AIAj{CBf{L@`{Mh}}}}}{{LFj{HJ`BBF`}}}}\",[]]Bj[\"{{{AIAj{CKl}}AJ@`}{{LFj{HJ`BEKn}}}}\",[]]Bn[\"{{{AIAj{CBfAJ@d}}OHd}{{AIAj{CBfAJ@d}}}}\",[]]Bn[\"{{{AIAj{CBfAJ@f}}OHd}{{AIAj{CBfAJ@f}}}}\",[]]A`[\"{BBFfADj}\",[]]o[\"{JJbCId}\",[]]o[\"{{}AJAl}\",[]]0o[\"{{}AIAj}\",[]]Bj[\"{{ABLf{N`{B@Hl}}}{{LFj{AIMdBCAn}}}}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]Bh[\"{{{AIAj{AHF`}}AJAl}{{LFn{AHJ`}}}}\",[]]A`[\"{AHIbCI`}\",[]]Aj[\"{{{AIAj{AIMj}}}CI`}\",[]]A`[\"{AHIdCI`}\",[]]Ce[\"{{{AIAj{CBfAJ@d}}{LF`{LHb}}}{{AIAj{CBfAJ@d}}}}\",[]]Ce[\"{{{AIAj{CBfAJ@f}}{LF`{LHb}}}{{AIAj{CBfAJ@f}}}}\",[]]Aa[\"{ABOnAJAl}\",[]]Aa[\"{AC@`AJAl}\",[]]Aa[\"{AC@bAJAl}\",[]]n[\"{OnCI`}\",[]]Bn[\"{{{AIAj{CBfBCDh}}CI`}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}CI`}{{AIAj{CBfBCDj}}}}\",[]]A`[\"{AHF`CI`}\",[]]Bo[\"{{{AIAj{CBfBCDh}}ABKj}{{AIAj{CBfBCDh}}}}\",[]]Bo[\"{{{AIAj{CBfBCDj}}ABKj}{{AIAj{CBfBCDj}}}}\",[]]A`[\"{AHF`LF`}\",[]]k[\"{{}}\",[]]00000000000Bh[\"{ABLfc{}}\",[\"IntoFuture::IntoFuture\"]]1A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]n[\"{OnLBd}\",[]]Ao[\"{{AIAjc}{{OHj{c}}}{}}\",[\"S\"]]0C`[\"{{{AIAj{CBfAIMl}}OHdCIdCMn{LF`{CId}}}HJ`}\",[]]Bn[\"{{{AIAj{CBfNb}}OHdCIdCMn{LF`{CId}}}HJ`}\",[]]C`[\"{{{AIAj{CBfAIMn}}OHdCIdCMn{LF`{CId}}}HJ`}\",[]]Bn[\"{{{AIAj{CBfNd}}OHdCIdCMn{LF`{CId}}}HJ`}\",[]]Bk[\"{{{AIAj{CBfJJd}}OHd{AIAj{BCC`}}}HJ`}\",[]]A`[\"{BEDbLF`}\",[]]A`[\"{BEDdLF`}\",[]]Bn[\"{{{AIAj{LHf}}{AIAj{LHf}}}{{LF`{ABMn}}}}\",[]]Bn[\"{{{AIAj{LFd}}{AIAj{LFd}}}{{LF`{ABMn}}}}\",[]]C`[\"{{{AIAj{AOCj}}{AIAj{AOCj}}}{{LF`{ABMn}}}}\",[]]C`[\"{{{AIAj{B@Dj}}{AIAj{B@Dj}}}{{LF`{ABMn}}}}\",[]]Bn[\"{{{AIAj{CLj}}{AIAj{CLj}}}{{LF`{ABMn}}}}\",[]]Bl[\"{{{AIAj{Nn}}{AIAj{Nn}}}{{LF`{ABMn}}}}\",[]]C`[\"{{{AIAj{ABNl}}{AIAj{ABNl}}}{{LF`{ABMn}}}}\",[]]Bn[\"{{{AIAj{LHh}}{AIAj{LHh}}}{{LF`{ABMn}}}}\",[]]C`[\"{{{AIAj{BEEd}}{AIAj{BEEd}}}{{LF`{ABMn}}}}\",[]]Bn[\"{{{AIAj{LFf}}{AIAj{LFf}}}{{LF`{ABMn}}}}\",[]]C`[\"{{{AIAj{AHIj}}{AIAj{AHIj}}}{{LF`{ABMn}}}}\",[]]C`[\"{{{AIAj{BAHh}}{AIAj{BAHh}}}{{LF`{ABMn}}}}\",[]]C`[\"{{{AIAj{AJCd}}{AIAj{AJCd}}}{{LF`{ABMn}}}}\",[]]C`[\"{{{AIAj{AOCl}}{AIAj{AOCl}}}{{LF`{ABMn}}}}\",[]]C`[\"{{{AIAj{BDIl}}{AIAj{BDIl}}}{{LF`{ABMn}}}}\",[]]C`[\"{{{AIAj{BEE`}}{AIAj{BEE`}}}{{LF`{ABMn}}}}\",[]]C`[\"{{{AIAj{B@Dl}}{AIAj{B@Dl}}}{{LF`{ABMn}}}}\",[]]Bn[\"{{{AIAj{CLl}}{AIAj{CLl}}}{{LF`{ABMn}}}}\",[]]Bl[\"{{{AIAj{O`}}{AIAj{O`}}}{{LF`{ABMn}}}}\",[]]C`[\"{{{AIAj{ABNn}}{AIAj{ABNn}}}{{LF`{ABMn}}}}\",[]]Be[\"{{{AIAj{AIMh}}}{{ABNd{AHHn}}}}\",[]]Bk[\"{{{AIAj{AIMj}}LFf}{{LFj{AJ@bAIM`}}}}\",[]]Df[\"{{{AIAj{CBfAJ@j}}{AIAj{CBf{L@`{JEf}}}}}{{LFj{{LF`{LBd}}AHIh}}}}\",[]]Cc[\"{{{AIAj{CBfAJ@j}}LBd}{{LFj{{A@l{Mh}}BBFb}}}}\",[]]Bb[\"{{{AIAj{CBfAIMj}}ABNn}AJ@l}\",[]]Aj[\"{{{AIAj{AHGf}}}LBd}\",[]]Aj[\"{{{AIAj{AHGh}}}LBd}\",[]]Bb[\"{{{AIAj{CBfAIMj}}ABNn}AJAh}\",[]]Bn[\"{{{AIAj{CBfBCDh}}CId}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}CId}{{AIAj{CBfBCDj}}}}\",[]]A`[\"{AHF`LF`}\",[]]A`[\"{BAEfLF`}\",[]]A`[\"{BAEhLF`}\",[]]Ce[\"{{{AIAj{CBfB@Hl}}{N`{AJBd}}}{{AIAj{CBfB@Hl}}}}\",[]]Ce[\"{{{AIAj{CBfB@I`}}{N`{AJBf}}}{{AIAj{CBfB@I`}}}}\",[]]Ce[\"{{{AIAj{CBfB@Bl}}{N`{AJBl}}}{{AIAj{CBfB@Bl}}}}\",[]]Ce[\"{{{AIAj{CBfB@C`}}{N`{AJBn}}}{{AIAj{CBfB@C`}}}}\",[]]n[\"{OnCId}\",[]]Bn[\"{{{AIAj{CBfBEJ`}}CI`}{{AIAj{CBfBEJ`}}}}\",[]]Bn[\"{{{AIAj{CBfBEJb}}CI`}{{AIAj{CBfBEJb}}}}\",[]]Ak[\"{{{AIAj{AIMh}}}BFI`}\",[]]Ai[\"{{{N`{B@Hn}}}B@Hl}\",[]]Ai[\"{{{N`{B@Ib}}}B@I`}\",[]]Aj[\"{{BCCjc}BCCjABOd}\",[\"\"]]Aj[\"{{BCClc}BCClABOd}\",[\"\"]]Bm[\"{{{AIAj{CBfAJAf}}JEf}{{LFj{HJ`AJCb}}}}\",[]]n[\"{{}LEd}\",[]]o[\"{OG`OG`}\",[]]0000Bd[\"{{{AIAj{BFNj}}}{{LF`{ABKj}}}}\",[]]Bd[\"{{{AIAj{BFNl}}}{{LF`{ABKj}}}}\",[]]Bd[\"{{{AIAj{BGNh}}}{{LF`{ABKj}}}}\",[]]Bd[\"{{{AIAj{BGOb}}}{{LF`{ABKj}}}}\",[]]Bd[\"{{{AIAj{AIMh}}}{{LF`{BCAn}}}}\",[]]Ck[\"{{{AIAj{ABKn}}B@BlAJAl{AIAj{CHn}}}{{LFj{AIMdB@Dd}}}}\",[]]Ak[\"{{{AIAj{B@Hd}}}ABKj}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]An[\"{{AJ@hCMn}{{LFn{LBd}}}}\",[]]Cj[\"{{{AIAj{OJj}}}{{LF`{{LBb{{Nh{AHGf}}{Nh{AHHd}}}}}}}}\",[]]Ck[\"{{{AIAj{AJBh}}}{{LF`{{LBb{{Nh{AHGf}}{Nh{AHHd}}}}}}}}\",[]]Cj[\"{{{AIAj{OJl}}}{{LF`{{LBb{{Nh{AHGh}}{Nh{AHHf}}}}}}}}\",[]]Ck[\"{{{AIAj{AJBj}}}{{LF`{{LBb{{Nh{AHGh}}{Nh{AHHf}}}}}}}}\",[]]A`[\"{BFGdA@l}\",[]]A`[\"{BHBfA@l}\",[]]A`[\"{BFGfA@l}\",[]]A`[\"{BHBhA@l}\",[]]An[\"{{{AIAj{CBfBFNj}}}B@Dj}\",[]]An[\"{{{AIAj{CBfBFNl}}}B@Dl}\",[]]An[\"{{{AIAj{CBfBGNh}}}B@Dl}\",[]]An[\"{{{AIAj{CBfBGOb}}}B@Dl}\",[]]Ak[\"{{{AIAj{BBFn}}}B@Fj}\",[]]Ba[\"{{{AIAj{CBfAIMj}}BCBd}HJ`}\",[]]Bo[\"{{{AIAj{CBfABL`}}BDIlBAE`}{{LF`{BCBd}}}}\",[]]B`[\"{{{AIAj{OJj}}B@DjCLj}CK`}\",[]]Bn[\"{{{AIAj{B@Hn}}CIbB@Dj}{{LFj{CK`BELn}}}}\",[]]Ba[\"{{{AIAj{AJBh}}B@DjCLj}CK`}\",[]]Bn[\"{{{AIAj{BDLj}}CIbB@Dj}{{LFj{CK`BELn}}}}\",[]]B`[\"{{{AIAj{OJl}}B@DlCLl}CKb}\",[]]Bn[\"{{{AIAj{B@Ib}}CIbB@Dl}{{LFj{CKbBEMd}}}}\",[]]Ba[\"{{{AIAj{AJBj}}B@DlCLl}CKb}\",[]]Bn[\"{{{AIAj{BDLl}}CIbB@Dl}{{LFj{CKbBEMd}}}}\",[]]Bn[\"{{{AIAj{BGId}}CIbB@Dh}{{LFj{CKbBEMd}}}}\",[]]Aj[\"{{}{{OJd{c}}}{}}\",[\"T\"]]0A`[\"{AHIbCId}\",[]]A`[\"{AHIdCId}\",[]]Bn[\"{{{AIAj{CBfB@Hl}}LBd}{{AIAj{CBfB@Hl}}}}\",[]]Bn[\"{{{AIAj{CBfB@I`}}LBd}{{AIAj{CBfB@I`}}}}\",[]]Aj[\"{{{AIAj{BBAj}}}CMn}\",[]]0Aj[\"{{{AIAj{BBFn}}}CMn}\",[]]Bk[\"{{{AIAj{AIMj}}LFf}{{LFj{AHFnAIM`}}}}\",[]]Cb[\"{{{AIAj{BCC`}}{AIAj{AJ@h}}CId}{{N`{AOF`}}}}\",[]]Ak[\"{{{AIAj{ABLf}}}B@Dj}\",[]]Ak[\"{{{AIAj{ABLh}}}B@Dl}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]Bf[\"{{{AIAj{CBfAIMj}}}{{LF`{OHd}}}}\",[]]Ce[\"{{{AIAj{CBfBCDh}}{N`{AODh}}}{{AIAj{CBfBCDh}}}}\",[]]Ce[\"{{{AIAj{CBfBCDj}}{N`{AODj}}}{{AIAj{CBfBCDj}}}}\",[]]43A`[\"{ABOnLF`}\",[]]A`[\"{AC@`LF`}\",[]]A`[\"{AC@bLF`}\",[]]Ai[\"{{{AIAj{OKh}}}LBd}\",[]]Bo[\"{{{AIAj{CBfBGNh}}ABKj}{{AIAj{CBfBGNh}}}}\",[]]Bo[\"{{{AIAj{CBfBGOb}}ABKj}{{AIAj{CBfBGOb}}}}\",[]]Bj[\"{{{AIAj{AJAf}}CA`}{{LFj{HJ`B@Ch}}}}\",[]]Bm[\"{{{AIAj{CBfAJAh}}CA`}{{LFj{HJ`B@D`}}}}\",[]]<;A`[\"{JJbAOEn}\",[]]A`[\"{AOBbJJb}\",[]]Bi[\"{{{N`{B@Bh}}JJf}{{LFj{BDLfBFHb}}}}\",[]]Bi[\"{{{N`{B@Hh}}JJf}{{LFj{BDLjBFHb}}}}\",[]]Bi[\"{{{N`{B@Bh}}JJf}{{LFj{BDLhBFHd}}}}\",[]]Bi[\"{{{N`{B@Hh}}JJf}{{LFj{BDLlBFHd}}}}\",[]]Co[\"{{{AIAj{CBfAJAf}}{AIAj{CBf{L@`{JEf}}}}}{{LFj{OLdAJCb}}}}\",[]]Co[\"{{{AIAj{CBfAJAh}}{AIAj{CBf{L@`{JEf}}}}}{{LFj{OLfAJCd}}}}\",[]]Aj[\"{{{AIAj{AIMj}}}CMn}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]A`[\"{AHF`CMn}\",[]]E`[\"{{{AIAj{CBfBBCb}}c}{{AIAj{CBfBBCb}}}{{A`{}{{LFb{{Nh{BFNj}}}}}}CLhCLn}}\",[\"F\"]]E`[\"{{{AIAj{CBfBBCd}}c}{{AIAj{CBfBBCd}}}{{A`{}{{LFb{{Nh{BFNl}}}}}}CLhCLn}}\",[\"F\"]]Bj[\"{{{AIAj{BBAj}}}{{A@f{{Nh{AHIn}}}}}}\",[]]n[\"{OnCId}\",[]]Aa[\"{AOEnAHGj}\",[]]76Aa[\"{AJA`ABLf}\",[]]Aa[\"{AJAbABLh}\",[]]Cf[\"{{{AIAj{CBfBEHb}}{LF`{ABKj}}}{{AIAj{CBfBEHb}}}}\",[]]Cf[\"{{{AIAj{CBfBEHd}}{LF`{ABKj}}}{{AIAj{CBfBEHd}}}}\",[]]9;:An[\"{{{AIAj{AOF`}}ABKj}HJ`}\",[]]B`[\"{{{AIAj{CBfAIMl}}CI`}HJ`}\",[]]An[\"{{{AIAj{CBfNb}}CI`}HJ`}\",[]]Ao[\"{{{AIAj{CBfJF`}}CI`}HJ`}\",[]]Ao[\"{{{AIAj{CBfOI`}}CI`}HJ`}\",[]]B`[\"{{{AIAj{CBfAIMn}}CI`}HJ`}\",[]]An[\"{{{AIAj{CBfNd}}CI`}HJ`}\",[]]Ao[\"{{{AIAj{CBfJFb}}CI`}HJ`}\",[]]Ao[\"{{{AIAj{CBfOIb}}CI`}HJ`}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]10Bh[\"{{{AIAj{AIMh}}}{{LF`{{Nh{Mn}}}}}}\",[]]Bg[\"{{{AIAj{OJj}}}{{LF`{{Nh{Mn}}}}}}\",[]]Bh[\"{{{AIAj{AJBh}}}{{LF`{{Nh{Mn}}}}}}\",[]]Bg[\"{{{AIAj{OJl}}}{{LF`{{Nh{Mn}}}}}}\",[]]Bh[\"{{{AIAj{AJBj}}}{{LF`{{Nh{Mn}}}}}}\",[]]Dm[\"{{{AIAj{CBfAJ@j}}{AIAj{CBfOGb}}{AIAj{CBfOJb}}}{{CLd{{LFj{HJ`AHIh}}}}}}\",[]]Db[\"{{{A@f{{AIAj{CBfAJAf}}}}{AIAj{CBfOGb}}}{{CLd{{LFl{HJ`}}}}}}\",[]]Db[\"{{{AIAj{CBfAIMj}}OHdB@Fj{AIAj{CBf{A@l{Mh}}}}}{{LF`{AC@`}}}}\",[]]Cf[\"{{{AIAj{CBfBCDh}}{AIAj{CHn}}}{{AIAj{CBfBCDh}}}}\",[]]Cf[\"{{{AIAj{CBfBCDj}}{AIAj{CHn}}}{{AIAj{CBfBCDj}}}}\",[]]Ak[\"{{{AIAj{AIMh}}}B@Gl}\",[]]Bj[\"{{{AIAj{AIMh}}JEf}{{LFj{HJ`BEEb}}}}\",[]]Bn[\"{{{AIAj{CBfBCDh}}CMn}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}CMn}{{AIAj{CBfBCDj}}}}\",[]]Ai[\"{{{AIAj{OGn}}}LBd}\",[]]Ai[\"{{{AIAj{OH`}}}LBd}\",[]]De[\"{{{N`{B@Bn}}CIb{AIAj{CHn}}{AIAj{BFAh}}}{{LFj{{Nh{OJj}}B@Dd}}}}\",[]]Bo[\"{{{N`{B@Hn}}CIb{AIAj{BFAh}}}{{Nh{OJj}}}}\",[]]De[\"{{{N`{BDLf}}CIb{AIAj{CHn}}{AIAj{BFAh}}}{{LFj{{Nh{OJj}}B@Dd}}}}\",[]]Bo[\"{{{N`{BDLj}}CIb{AIAj{BFAh}}}{{Nh{OJj}}}}\",[]]De[\"{{{N`{B@Cb}}CIb{AIAj{CHn}}{AIAj{BFAh}}}{{LFj{{Nh{OJl}}B@Df}}}}\",[]]Bo[\"{{{N`{B@Ib}}CIb{AIAj{BFAh}}}{{Nh{OJl}}}}\",[]]De[\"{{{N`{BDLh}}CIb{AIAj{CHn}}{AIAj{BFAh}}}{{LFj{{Nh{OJl}}B@Df}}}}\",[]]Bo[\"{{{N`{BDLl}}CIb{AIAj{BFAh}}}{{Nh{OJl}}}}\",[]]De[\"{{{N`{BGIb}}CIb{AIAj{CHn}}{AIAj{BFAh}}}{{LFj{{Nh{OJl}}B@Db}}}}\",[]]Bo[\"{{{N`{BGId}}CIb{AIAj{BFAh}}}{{Nh{OJl}}}}\",[]]Cc[\"{{{AIAj{BEDf}}{AIAj{{L@`{Mh}}}}}{{Nh{OFf}}}}\",[]]Cc[\"{{{AIAj{BEDh}}{AIAj{{L@`{Mh}}}}}{{Nh{OFh}}}}\",[]]Bm[\"{{AJAlBBAlOn}{{LFn{{LBb{ABKlAIMf}}}}}}\",[]]Bd[\"{{{AIAj{AIMj}}}{{AIAj{OJl}}}}\",[]]A`[\"{AOBbJJb}\",[]]Cb[\"{{{AIAj{AODh}}CLjAJAlB@DjOHd}{{LF`{AJ@d}}}}\",[]]Cb[\"{{{AIAj{BCCj}}CLjAJAlB@DjOHd}{{LF`{AJ@d}}}}\",[]]Cb[\"{{{AIAj{AODj}}CLlAJAlB@DlOHd}{{LF`{AJ@f}}}}\",[]]Cb[\"{{{AIAj{BCCl}}CLlAJAlB@DlOHd}{{LF`{AJ@f}}}}\",[]]B`[\"{{{AIAj{CBfAIMj}}OHd}HJ`}\",[]]Bo[\"{{{AIAj{CBfAIMd}}}{{LFj{{Nh{Mn}}BCAn}}}}\",[]]Bh[\"{{{AIAj{AIMh}}}{{LF`{{Nh{Mn}}}}}}\",[]]Bg[\"{{{AIAj{OJj}}}{{LF`{{Nh{Mn}}}}}}\",[]]Bh[\"{{{AIAj{AJBh}}}{{LF`{{Nh{Mn}}}}}}\",[]]Bg[\"{{{AIAj{OJl}}}{{LF`{{Nh{Mn}}}}}}\",[]]Bh[\"{{{AIAj{AJBj}}}{{LF`{{Nh{Mn}}}}}}\",[]]o[\"{AINdMh}\",[]]o[\"{AINfMh}\",[]]Ai[\"{{{AIAj{CHn}}}CMn}\",[]]Aj[\"{{{AIAj{AIMl}}}CId}\",[]]Ah[\"{{{AIAj{Nb}}}CId}\",[]]Bn[\"{{{AIAj{CBfAHEh}}CId}{{AIAj{CBfAHEh}}}}\",[]]Ai[\"{{{AIAj{JF`}}}CId}\",[]]Bn[\"{{{AIAj{CBfAOC`}}CId}{{AIAj{CBfAOC`}}}}\",[]]Ai[\"{{{AIAj{OI`}}}CId}\",[]]Bn[\"{{{AIAj{CBfBAGh}}CId}{{AIAj{CBfBAGh}}}}\",[]]Aj[\"{{{AIAj{AIMn}}}CId}\",[]]Ah[\"{{{AIAj{Nd}}}CId}\",[]]Bn[\"{{{AIAj{CBfAHEj}}CId}{{AIAj{CBfAHEj}}}}\",[]]Ai[\"{{{AIAj{JFb}}}CId}\",[]]Bn[\"{{{AIAj{CBfAOCb}}CId}{{AIAj{CBfAOCb}}}}\",[]]Ai[\"{{{AIAj{OIb}}}CId}\",[]]Bn[\"{{{AIAj{CBfBAGj}}CId}{{AIAj{CBfBAGj}}}}\",[]]Aa[\"{AJ@jBFAj}\",[]]Ai[\"{{{AIAj{OJj}}}CMn}\",[]]Aj[\"{{{AIAj{AJBh}}}CMn}\",[]]Aj[\"{{{AIAj{AIMj}}}CMn}\",[]]Ai[\"{{{AIAj{OJl}}}CMn}\",[]]Aj[\"{{{AIAj{AJBj}}}CMn}\",[]]Co[\"{{{AIAj{OJj}}B@Dj{AIAj{{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}}CMn}\",[]]D`[\"{{{AIAj{AJBh}}B@Dj{AIAj{{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}}CMn}\",[]]Co[\"{{{AIAj{OJl}}B@Dl{AIAj{{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}}CMn}\",[]]D`[\"{{{AIAj{AJBj}}B@Dl{AIAj{{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}}CMn}\",[]]Bn[\"{{{AIAj{CBfBEJ`}}CI`}{{AIAj{CBfBEJ`}}}}\",[]]Bn[\"{{{AIAj{CBfBEJb}}CI`}{{AIAj{CBfBEJb}}}}\",[]]Cc[\"{{{AIAj{CBfOJj}}}{{LF`{{OHf{{Nh{AHHd}}}}}}}}\",[]]Cd[\"{{{AIAj{CBfAJBh}}}{{LF`{{OHf{{Nh{AHHd}}}}}}}}\",[]]Cc[\"{{{AIAj{CBfOJl}}}{{LF`{{OHh{{Nh{AHHf}}}}}}}}\",[]]Cd[\"{{{AIAj{CBfAJBj}}}{{LF`{{OHh{{Nh{AHHf}}}}}}}}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]De[\"{{{AIAj{CBfBCDh}}c{AIAj{CHn}}}{{AIAj{CBfBCDh}}}{{JEd{CKj}}}}\",[\"\"]]De[\"{{{AIAj{CBfBCDj}}c{AIAj{CHn}}}{{AIAj{CBfBCDj}}}{{JEd{CKj}}}}\",[\"\"]]Ci[\"{{{AIAj{CBfOJj}}{AIAj{{L@`{Mh}}}}}{{LFj{CMnJGf}}}}\",[]]Cj[\"{{{AIAj{CBfAJBh}}{AIAj{{L@`{Mh}}}}}{{LFj{CMnJGf}}}}\",[]]Cj[\"{{{AIAj{CBfOJl}}{AIAj{{L@`{Mh}}}}}{{LFj{CMnBBFl}}}}\",[]]Ck[\"{{{AIAj{CBfAJBj}}{AIAj{{L@`{Mh}}}}}{{LFj{CMnBBFl}}}}\",[]]n[\"{OnCMn}\",[]]Bn[\"{{{AIAj{CBfBCDh}}LHf}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}LHh}{{AIAj{CBfBCDj}}}}\",[]]A`[\"{AHF`LF`}\",[]]Ca[\"{{{AIAj{CBfAJ@j}}}{{LFj{{LF`{LHf}}AJ@n}}}}\",[]]Ca[\"{{{AIAj{CBfBFAj}}}{{LFj{{LF`{LHf}}AJ@n}}}}\",[]]Ca[\"{{{AIAj{CBfAJ@l}}}{{LFj{{LF`{LHh}}B@D`}}}}\",[]]Ak[\"{{{AIAj{AIMd}}}AJAl}\",[]]Ak[\"{{{AIAj{AIMh}}}AJAl}\",[]]Ak[\"{{{AIAj{ABLf}}}AJAl}\",[]]Bg[\"{{{AIAj{CKl}}}{{LFj{AJAlAILj}}}}\",[]]Ak[\"{{{AIAj{ABLh}}}AJAl}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]B`[\"{{LHfc}LHf{{CJn{LHf}}}}\",[\"\"]]B`[\"{{LFdc}LFd{{CJn{LFd}}}}\",[\"\"]]B`[\"{{LHhc}LHh{{CJn{LHh}}}}\",[\"\"]]B`[\"{{LFfc}LFf{{CJn{LFf}}}}\",[\"\"]]20Ae[\"{{ABKjCId}ADj}\",[]]Bn[\"{{{AIAj{CBfBCDh}}ADh}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}ADh}{{AIAj{CBfBCDj}}}}\",[]]A`[\"{JJbABKj}\",[]]B`[\"{{}{{AIAj{{L@`{BF@l}}}}}}\",[]]Ao[\"{{AIAj{A@Kn{CBfMh}}}HJ`}\",[]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Am[\"{{}{{LF`{{N`{OJh}}}}}}\",[]]Bn[\"{{{AIAj{CBfBBCb}}CMn}{{AIAj{CBfBBCb}}}}\",[]]Bn[\"{{{AIAj{CBfBBCd}}CMn}{{AIAj{CBfBBCd}}}}\",[]]Aj[\"{{{AIAj{AHHd}}}CId}\",[]]Aj[\"{{{AIAj{AHHf}}}CId}\",[]]A`[\"{ABNhLF`}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]10Bf[\"{{{AIAj{CHn}}}{{LFn{CIdBAGl}}}}\",[]]Bm[\"{{{AIAj{ABKn}}{Nh{BBAj}}}{{LFl{HJ`}}}}\",[]]Ai[\"{AIMf{{LFn{HJ`}}}}\",[]]Ci[\"{{{AIAj{OJf}}BBAlBAHf{AIAj{On}}}{{LBb{AJAlABKl}}}}\",[]]Cb[\"{{{AIAj{CBfAIMj}}LFfAJ@b}{{LFj{AJ@bBEL`}}}}\",[]]Am[\"{{{AIAj{AIMh}}CId}HJ`}\",[]]B`[\"{{{AIAj{CBfAIMj}}CId}HJ`}\",[]]A`[\"{BBFdCMn}\",[]]B`[\"{{{AIAj{CBfJJb}}BBFf}HJ`}\",[]]Aa[\"{AOEnAHGj}\",[]]Bm[\"{{{AIAj{OJh}}AHJ`}{{LFl{{Nh{BBAj}}}}}}\",[]]Bn[\"{{{AIAj{B@Ij}}AHJ`}{{LFl{{Nh{BBAj}}}}}}\",[]]Bn[\"{{{AIAj{AOEd}}AHJ`}{{LFl{{Nh{BBAj}}}}}}\",[]]Ci[\"{{{AIAj{CBfOJj}}{AIAj{CBf{A@l{Mh}}}}}{{LF`{CK`}}}}\",[]]Cj[\"{{{AIAj{CBfAJBh}}{AIAj{CBf{A@l{Mh}}}}}{{LF`{CK`}}}}\",[]]Ci[\"{{{AIAj{CBfOJl}}{AIAj{CBf{A@l{Mh}}}}}{{LF`{CKb}}}}\",[]]Cj[\"{{{AIAj{CBfAJBj}}{AIAj{CBf{A@l{Mh}}}}}{{LF`{CKb}}}}\",[]]Cb[\"{{{AIAj{ABOf}}HInAJBbABKj}{{LFj{HJ`BCDb}}}}\",[]]Cb[\"{{{AIAj{BAD`}}HInAJBbABKj}{{LFj{HJ`BCDb}}}}\",[]]Cb[\"{{{AIAj{B@Fl}}HInAJBbABKj}{{LFj{HJ`BCDb}}}}\",[]]Cb[\"{{{AIAj{ABOh}}HInAJBbABKj}{{LFj{HJ`BCDd}}}}\",[]]Cb[\"{{{AIAj{BADb}}HInAJBbABKj}{{LFj{HJ`BCDd}}}}\",[]]Cb[\"{{{AIAj{B@Fn}}HInAJBbABKj}{{LFj{HJ`BCDd}}}}\",[]]Al[\"{{}{{LFj{B@BjBFHd}}}}\",[]]Bm[\"{{{AIAj{AIMh}}LFd}{{LF`{{Nh{AIMl}}}}}}\",[]]Bo[\"{{{AIAj{AIMj}}LFf}{{LF`{{AIAj{AIMn}}}}}}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]Aj[\"{{{AIAj{AIMh}}}HJ`}\",[]]Am[\"{{{AIAj{CBfAIMj}}}HJ`}\",[]]Be[\"{{{AIAj{AIL`}}}{{LFj{OnJG`}}}}\",[]]Bg[\"{{{AIAj{AIL`}}}{{LFj{AHF`JG`}}}}\",[]]Bf[\"{{{AIAj{AIL`}}}{{LFj{A@`JG`}}}}\",[]]Bf[\"{{{AIAj{AIL`}}}{{LFj{A@bJG`}}}}\",[]]Be[\"{{{AIAj{AIL`}}}{{LFj{NlJG`}}}}\",[]]Bg[\"{{{AIAj{AIL`}}}{{LFj{ABJlJG`}}}}\",[]]Ak[\"{{{AIAj{BBFn}}}B@Fj}\",[]]Cf[\"{{{AIAj{CBfBCDh}}{LF`{AOCj}}}{{AIAj{CBfBCDh}}}}\",[]]Cf[\"{{{AIAj{CBfBCDj}}{LF`{AOCl}}}{{AIAj{CBfBCDj}}}}\",[]]<;Aj[\"{{{AIAj{ABKn}}}LBd}\",[]]Aj[\"{{{AIAj{ABL`}}}LBd}\",[]]Bc[\"{{{AIAj{AIMh}}AJAlAJ@`}ABMl}\",[]]Co[\"{{{AIAj{CBfAIMj}}AHFnAJ@bOHd}{{LFj{{LBb{LFfCMn}}AHHj}}}}\",[]]Bn[\"{{{AIAj{CBfBCDh}}CIb}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}CIb}{{AIAj{CBfBCDj}}}}\",[]]Ai[\"{AIMf{{LFn{HJ`}}}}\",[]]Bg[\"{{{AIAj{BBFn}}AJC`}{{LFl{LBd}}}}\",[]]A`[\"{AHF`CId}\",[]]10Ai[\"{{{AIAj{On}}}BCDf}\",[]]Ce[\"{{{AIAj{CBfB@Hl}}{N`{BCDh}}}{{AIAj{CBfB@Hl}}}}\",[]]Ce[\"{{{AIAj{CBfB@Bl}}{N`{BCDh}}}{{AIAj{CBfB@Bl}}}}\",[]]Ce[\"{{{AIAj{CBfB@I`}}{N`{BCDj}}}{{AIAj{CBfB@I`}}}}\",[]]Ce[\"{{{AIAj{CBfB@C`}}{N`{BCDj}}}{{AIAj{CBfB@C`}}}}\",[]]Aa[\"{B@HlBFOl}\",[]]Aa[\"{B@I`BFOn}\",[]]Bj[\"{{{A@l{BBAl}}BAHf}{{LFj{B@HlJGj}}}}\",[]]Bj[\"{{{A@l{BBAl}}BAHf}{{LFj{B@I`JGl}}}}\",[]]Co[\"{{{AIAj{CBfAJAf}}{AIAj{CBf{L@`{JEf}}}}}{{LFj{HJ`AJCb}}}}\",[]]A`[\"{AHIbCId}\",[]]A`[\"{AHIdCId}\",[]]A`[\"{BEDbCId}\",[]]A`[\"{BEDdCId}\",[]]Bc[\"{{{AIAj{AIMh}}}{{LF`{LBd}}}}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]10Aj[\"{{{AIAj{AHFf}}}LBd}\",[]]Be[\"{{{AIAj{ABKn}}{LF`{B@Hl}}}HJ`}\",[]]Bn[\"{{{AIAj{CBfABL`}}{LF`{{N`{B@I`}}}}}HJ`}\",[]]Bd[\"{{{AIAj{BF@l}}}{{LF`{BAHd}}}}\",[]]o[\"{{}AIAj}\",[]]n[\"{{}OG`}\",[]]0000Bn[\"{{{AIAj{CBfBCDh}}LBd}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}LBd}{{AIAj{CBfBCDj}}}}\",[]]o[\"{CIdLHf}\",[]]o[\"{CIdLHh}\",[]]A`[\"{BAEbCId}\",[]]Bo[\"{{{AIAj{CBfBBCb}}ABKj}{{AIAj{CBfBBCb}}}}\",[]]Bo[\"{{{AIAj{CBfBBCd}}ABKj}{{AIAj{CBfBBCd}}}}\",[]]Ae[\"{{LBdCId}BAD`}\",[]]Ae[\"{{LBdCId}BADb}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]6Bg[\"{{{AIAj{AIMj}}LFf}{{LF`{AJ@b}}}}\",[]]An[\"{{{AIAj{AIMh}}JEf}B@Hf}\",[]]Am[\"{{{AIAj{AIMh}}LHf}HJ`}\",[]]B`[\"{{{AIAj{CBfAIMj}}LHh}HJ`}\",[]]Ce[\"{{{AIAj{CBfBBCb}}{A@l{CIb}}}{{AIAj{CBfBBCb}}}}\",[]]Ce[\"{{{AIAj{CBfBBCd}}{A@l{CIb}}}{{AIAj{CBfBBCd}}}}\",[]]Fg[\"{{{AIAj{BGCj}}{AIAj{BBAl}}{AIAj{{L@`{BBAl}}}}{AIAj{AJAj}}{AIAj{{L@`{Mh}}}}AC@h}{{LFj{BEKjJFn}}}}\",[]]Af[\"{{BCCjOHd}BCCj}\",[]]Af[\"{{BCClOHd}BCCl}\",[]]?Al[\"{B@Il{{LFj{CLjJGh}}}}\",[]]Ak[\"{B@Il{{LFj{NnJGh}}}}\",[]]Ai[\"{B@Il{{LG`{CLl}}}}\",[]]Ah[\"{B@Il{{LG`{O`}}}}\",[]]o[\"{OG`OG`}\",[]]Bo[\"{{{AIAj{CBfBEJ`}}ABKj}{{AIAj{CBfBEJ`}}}}\",[]]Bo[\"{{{AIAj{CBfBEJb}}ABKj}{{AIAj{CBfBEJb}}}}\",[]]Bb[\"{{{AIAj{OJj}}}{{LF`{CMn}}}}\",[]]Bc[\"{{{AIAj{AJBh}}}{{LF`{CMn}}}}\",[]]Bb[\"{{{AIAj{OJl}}}{{LF`{CMn}}}}\",[]]Bc[\"{{{AIAj{AJBj}}}{{LF`{CMn}}}}\",[]]Bg[\"{{{AIAj{AIMh}}}{{LFj{HJ`BCAn}}}}\",[]]Cf[\"{{{AIAj{CBfBCDh}}{LF`{ABKj}}}{{AIAj{CBfBCDh}}}}\",[]]Cf[\"{{{AIAj{CBfBCDj}}{LF`{ABKj}}}{{AIAj{CBfBCDj}}}}\",[]]A`[\"{AHIbCId}\",[]]A`[\"{AHIdCId}\",[]]Bl[\"{{{AIAj{CBfAIMl}}OHdOHdCMnCMnCId}HJ`}\",[]]Bj[\"{{{AIAj{CBfNb}}OHdOHdCMnCMnCId}HJ`}\",[]]Bk[\"{{{AIAj{CBfJF`}}OHdOHdCMnCMnCId}HJ`}\",[]]Bk[\"{{{AIAj{CBfOI`}}OHdOHdCMnCMnCId}HJ`}\",[]]Bl[\"{{{AIAj{CBfAIMn}}OHdOHdCMnCMnCId}HJ`}\",[]]Bj[\"{{{AIAj{CBfNd}}OHdOHdCMnCMnCId}HJ`}\",[]]Bk[\"{{{AIAj{CBfJFb}}OHdOHdCMnCMnCId}HJ`}\",[]]Bk[\"{{{AIAj{CBfOIb}}OHdOHdCMnCMnCId}HJ`}\",[]]A`[\"{BAEbCId}\",[]]Ak[\"{{{AIAj{OKh}}O`}CId}\",[]]Bi[\"{{{AIAj{CBfAJAd}}CId}{{LFn{HJ`}}}}\",[]]<;A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]10Cf[\"{{{AIAj{CBfBCDh}}{LF`{BEHb}}}{{AIAj{CBfBCDh}}}}\",[]]Cf[\"{{{AIAj{CBfBCDj}}{LF`{BEHd}}}{{AIAj{CBfBCDj}}}}\",[]]A`[\"{AHIbCId}\",[]]A`[\"{AHIdCId}\",[]]Bh[\"{{{AIAj{CBfAIL`}}}{{LFj{OnJG`}}}}\",[]]Bj[\"{{{AIAj{CBfAIL`}}}{{LFj{AHF`JG`}}}}\",[]]Bi[\"{{{AIAj{CBfAIL`}}}{{LFj{A@`JG`}}}}\",[]]Bi[\"{{{AIAj{CBfAIL`}}}{{LFj{A@bJG`}}}}\",[]]Bh[\"{{{AIAj{CBfAIL`}}}{{LFj{NlJG`}}}}\",[]]Bj[\"{{{AIAj{CBfAIL`}}}{{LFj{ABJlJG`}}}}\",[]]Cc[\"{{{N`{AIMf}}CIdCMn}{{LFn{{LBb{BBFfBBFf}}}}}}\",[]]Bn[\"{{{AIAj{CBfB@Hl}}CId}{{AIAj{CBfB@Hl}}}}\",[]]Bn[\"{{{AIAj{CBfB@I`}}CId}{{AIAj{CBfB@I`}}}}\",[]]Aj[\"{{{AIAj{AIMh}}}CMn}\",[]]Ak[\"{{{AIAj{BBAj}}}B@Fj}\",[]]0Ci[\"{{{AIAj{CBfBBCb}}CI`}{{LFj{{AIAj{CBfBBCb}}AOBf}}}}\",[]]Ci[\"{{{AIAj{CBfBBCd}}CI`}{{LFj{{AIAj{CBfBBCd}}AOBh}}}}\",[]]A`[\"{AHF`CI`}\",[]]Cf[\"{{{AIAj{CBfBCDh}}{LF`{BEJ`}}}{{AIAj{CBfBCDh}}}}\",[]]Cf[\"{{{AIAj{CBfBCDj}}{LF`{BEJb}}}{{AIAj{CBfBCDj}}}}\",[]]Bg[\"{{{AIAj{CBfAIMj}}}{{LF`{BAE`}}}}\",[]]Cf[\"{{{AIAj{CBfB@Hl}}{LF`{B@Id}}}{{AIAj{CBfB@Hl}}}}\",[]]Cf[\"{{{AIAj{CBfB@I`}}{LF`{B@Id}}}{{AIAj{CBfB@I`}}}}\",[]]Cf[\"{{{AIAj{CBfB@Hl}}{LF`{B@If}}}{{AIAj{CBfB@Hl}}}}\",[]]Cf[\"{{{AIAj{CBfB@I`}}{LF`{B@If}}}{{AIAj{CBfB@I`}}}}\",[]]Cc[\"{{}{{LFj{{LBb{{A@l{BBAl}}BAHf}}{Nh{JFl}}}}}}\",[]]Bn[\"{{{AIAj{CBfBEHb}}LHf}{{AIAj{CBfBEHb}}}}\",[]]Bn[\"{{{AIAj{CBfBEHd}}LHh}{{AIAj{CBfBEHd}}}}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]Bo[\"{{{AIAj{CBfB@Hl}}ABKj}{{AIAj{CBfB@Hl}}}}\",[]]Bo[\"{{{AIAj{CBfB@I`}}ABKj}{{AIAj{CBfB@I`}}}}\",[]]Ci[\"{{{AIAj{CKl}}{LF`{ABKj}}}{{LFj{{LF`{ABKj}}AILj}}}}\",[]]Bj[\"{{{AIAj{BBFn}}AJC`LBd}{{LFl{HJ`}}}}\",[]]054Bm[\"{{{AIAj{OJj}}}{{LFj{{LF`{BFAh}}JGf}}}}\",[]]Bn[\"{{{AIAj{AJBh}}}{{LFj{{LF`{BFAh}}JGf}}}}\",[]]Bn[\"{{{AIAj{OJl}}}{{LFj{{LF`{BFAh}}BBFl}}}}\",[]]Bo[\"{{{AIAj{AJBj}}}{{LFj{{LF`{BFAh}}BBFl}}}}\",[]]Aj[\"{{{AIAj{AHHd}}}CId}\",[]]Aj[\"{{{AIAj{AHHf}}}CId}\",[]]Aj[\"{{{AIAj{ABL`}}}CId}\",[]]Bf[\"{{{AIAj{AHFn}}{AIAj{AHFn}}}CMn}\",[]]Am[\"{{{AIAj{CBfAIMj}}}HJ`}\",[]]Bn[\"{{{AIAj{CBfBAGh}}ADh}{{AIAj{CBfBAGh}}}}\",[]]Bn[\"{{{AIAj{CBfBAGj}}ADh}{{AIAj{CBfBAGj}}}}\",[]]Ak[\"{{{AIAj{AHIn}}}B@Fj}\",[]]0Bd[\"{{{AIAj{AIMh}}}{{ABNd{JHf}}}}\",[]]Cb[\"{{{AIAj{AIMj}}LFf}{{LFj{{LF`{AJAl}}AIM`}}}}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]Bn[\"{{{AIAj{CBfBCDh}}LHf}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}LHh}{{AIAj{CBfBCDj}}}}\",[]]A`[\"{AHF`LF`}\",[]]Bo[\"{{{AIAj{AHF`}}{AIAj{CHn}}}{{LFl{BCDf}}}}\",[]]Em[\"{{{AIAj{AIMh}}{AIAj{CBf{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}}{{LFj{HJ`BGJn}}}}\",[]]El[\"{{{AIAj{OJj}}{AIAj{CBf{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}}{{LFj{HJ`BGJn}}}}\",[]]Em[\"{{{AIAj{AJBh}}{AIAj{CBf{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}}{{LFj{HJ`BGJn}}}}\",[]]El[\"{{{AIAj{OJl}}{AIAj{CBf{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}}{{LFj{HJ`BGK`}}}}\",[]]Em[\"{{{AIAj{AJBj}}{AIAj{CBf{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}{AIAj{{L@`{Mh}}}}}{{LFj{HJ`BGK`}}}}\",[]]Am[\"{{{AIAj{CBfAHFd}}}HJ`}\",[]]0Al[\"{{{AIAj{AIMj}}O`}CId}\",[]]Bl[\"{{{AIAj{AIMh}}}{{ABNf{{LF`{AJAl}}}}}}\",[]]Bg[\"{{{AIAj{CKl}}}{{LFj{BDHlAILj}}}}\",[]]>=E`[\"{{{AIAj{BGCj}}{AIAj{{L@`{Mh}}}}{AIAj{BBAl}}{AIAj{BFNn}}}{{LFj{BGF`JFn}}}}\",[]]0Bf[\"{{{N`{BAHn}}}{{LFj{B@BlBFHn}}}}\",[]]Bf[\"{{{N`{BAHn}}}{{LFj{B@C`BFHn}}}}\",[]]Bn[\"{{{AIAj{CBfBEHb}}LHf}{{AIAj{CBfBEHb}}}}\",[]]Bn[\"{{{AIAj{CBfBEHd}}LHh}{{AIAj{CBfBEHd}}}}\",[]]o[\"{OG`OG`}\",[]]0000Aj[\"{{{AIAj{AIMj}}}CMn}\",[]]0Ci[\"{{{AIAj{CKl}}{LF`{ABKj}}}{{LFj{{LF`{ABKj}}AILj}}}}\",[]]Cc[\"{{{AIAj{CBfOn}}{AIAj{AIL`}}}{{LFj{HJ`JG`}}}}\",[]]Ce[\"{{{AIAj{CBfAHF`}}{AIAj{AIL`}}}{{LFj{HJ`JG`}}}}\",[]]Cd[\"{{{AIAj{CBfA@`}}{AIAj{AIL`}}}{{LFj{HJ`JG`}}}}\",[]]Cd[\"{{{AIAj{CBfA@b}}{AIAj{AIL`}}}{{LFj{HJ`JG`}}}}\",[]]Cc[\"{{{AIAj{CBfNl}}{AIAj{AIL`}}}{{LFj{HJ`JG`}}}}\",[]]Ce[\"{{{AIAj{CBfABJl}}{AIAj{AIL`}}}{{LFj{HJ`JG`}}}}\",[]]Bo[\"{{{AIAj{CBfB@Hl}}BFOl}{{AIAj{CBfB@Hl}}}}\",[]]Bo[\"{{{AIAj{CBfB@I`}}BFOn}{{AIAj{CBfB@I`}}}}\",[]]Aj[\"{{{AIAj{BBCb}}}CId}\",[]]Aj[\"{{{AIAj{BBCd}}}CId}\",[]]Cc[\"{{{AIAj{CBfB@Bl}}{N`{A`}}}{{AIAj{CBfB@Bl}}}}\",[]]Cc[\"{{{AIAj{CBfB@C`}}{N`{A`}}}{{AIAj{CBfB@C`}}}}\",[]]Cj[\"{{BBCb{LF`{B@Hl}}{Nh{BBAj}}{N`{OJh}}}{{LFl{ABKn}}}}\",[]]Aj[\"{{{AIAj{ABLf}}}CMn}\",[]]Aj[\"{{{AIAj{ABLh}}}CMn}\",[]]Ca[\"{{{AIAj{CBfAIMj}}OHdAIOf}{{LFj{HJ`BGCb}}}}\",[]]Bd[\"{{{AIAj{BGCj}}}{{A@l{BCCn}}}}\",[]]Bj[\"{{{AIAj{AIMh}}AJAl}{{LFj{HJ`JH`}}}}\",[]]Bm[\"{{{AIAj{CBfAIMj}}AJAl}{{LFj{HJ`JH`}}}}\",[]]Bn[\"{{{AIAj{CBfBCDh}}LBd}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}LBd}{{AIAj{CBfBCDj}}}}\",[]]Bl[\"{{}{{LFj{{LBb{BBAlBEJl}}{Nh{JFl}}}}}}\",[]]Ai[\"{AIMf{{LFn{HJ`}}}}\",[]]A`[\"{AINdCId}\",[]]A`[\"{AINfCId}\",[]]An[\"{{{AIAj{ABKn}}B@Bl}HJ`}\",[]]D`[\"{{{AIAj{CBfAIMj}}LFf{LF`{ABKj}}}{{LFj{{LF`{ABKj}}AIM`}}}}\",[]]o[\"{{}AIAj}\",[]]Aj[\"{{{AIAj{AIMh}}}LBd}\",[]]Bn[\"{{{AIAj{CBfB@Hl}}CId}{{AIAj{CBfB@Hl}}}}\",[]]Bn[\"{{{AIAj{CBfB@I`}}CId}{{AIAj{CBfB@I`}}}}\",[]]Bn[\"{{{AIAj{CBfBCDh}}LHf}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}LHh}{{AIAj{CBfBCDj}}}}\",[]]:Bb[\"{{{AIAj{CBfAIMj}}O`LHh}HJ`}\",[]]Ak[\"{{}{{LFj{B@BlJGj}}}}\",[]]Ak[\"{{}{{LFj{B@C`JGl}}}}\",[]]Bn[\"{{{AIAj{CBfBCDh}}CMn}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}CMn}{{AIAj{CBfBCDj}}}}\",[]]65Cf[\"{{{AIAj{CBfOn}}{AIAj{CBfAIL`}}}{{LFj{HJ`JG`}}}}\",[]]Ch[\"{{{AIAj{CBfAHF`}}{AIAj{CBfAIL`}}}{{LFj{HJ`JG`}}}}\",[]]Cg[\"{{{AIAj{CBfA@`}}{AIAj{CBfAIL`}}}{{LFj{HJ`JG`}}}}\",[]]Cg[\"{{{AIAj{CBfA@b}}{AIAj{CBfAIL`}}}{{LFj{HJ`JG`}}}}\",[]]Cf[\"{{{AIAj{CBfNl}}{AIAj{CBfAIL`}}}{{LFj{HJ`JG`}}}}\",[]]Ch[\"{{{AIAj{CBfABJl}}{AIAj{CBfAIL`}}}{{LFj{HJ`JG`}}}}\",[]]Bi[\"{{{AIAj{CBfAHFn}}{AIAj{AHFn}}}CMn}\",[]]n[\"{{}HJ`}\",[]]Ce[\"{{{AIAj{CBfBCDh}}{LF`{LBd}}}{{AIAj{CBfBCDh}}}}\",[]]Ce[\"{{{AIAj{CBfBCDj}}{LF`{LBd}}}{{AIAj{CBfBCDj}}}}\",[]]Bn[\"{{{AIAj{AIMh}}}{{LFj{{A@l{AJAl}}JH`}}}}\",[]]Cd[\"{{{AIAj{CBfAIMj}}OHd}{{LFj{{A@l{AJAl}}JH`}}}}\",[]]Am[\"{{{AIAj{CBfAIMl}}}HJ`}\",[]]Al[\"{{{AIAj{CBfJF`}}}HJ`}\",[]]Am[\"{{{AIAj{CBfAIMn}}}HJ`}\",[]]Al[\"{{{AIAj{CBfJFb}}}HJ`}\",[]]Ai[\"{AIMf{{LFn{HJ`}}}}\",[]]Bj[\"{{{AIAj{AIMh}}AJAl}{{LFj{HJ`JH`}}}}\",[]]Bm[\"{{{AIAj{CBfAIMj}}AJAl}{{LFj{HJ`JH`}}}}\",[]]D`[\"{{{AIAj{CBfAIMj}}LFf{LF`{ABKj}}}{{LFj{{LF`{ABKj}}AIM`}}}}\",[]]Ce[\"{{{AIAj{CBfBCDh}}{N`{BECn}}}{{AIAj{CBfBCDh}}}}\",[]]Ce[\"{{{AIAj{CBfBCDj}}{N`{BED`}}}{{AIAj{CBfBCDj}}}}\",[]]Cf[\"{{{AIAj{CBfBCDh}}{LF`{ABKj}}}{{AIAj{CBfBCDh}}}}\",[]]Cf[\"{{{AIAj{CBfBCDj}}{LF`{ABKj}}}{{AIAj{CBfBCDj}}}}\",[]]Aa[\"{BAEfAIOd}\",[]]Aa[\"{BAEhAIOd}\",[]]9Bn[\"{{{AIAj{CBfBCDh}}CMn}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}CMn}{{AIAj{CBfBCDj}}}}\",[]]Am[\"{{{AIAj{AIMh}}LHf}HJ`}\",[]]o[\"{OG`OG`}\",[]]Bn[\"{{{AIAj{CBfBCDh}}CIb}{{AIAj{CBfBCDh}}}}\",[]]Bn[\"{{{AIAj{CBfBCDj}}CIb}{{AIAj{CBfBCDj}}}}\",[]]3109854Bn[\"{{{AIAj{AIMh}}}{{LFj{{A@l{AJAl}}JH`}}}}\",[]]Bn[\"{{{AIAj{AIMj}}}{{LFj{{A@l{AJAl}}JH`}}}}\",[]]10A`[\"{BFAhLF`}\",[]]Bm[\"{{{AIAj{CBfBCDh}}Mh}{{AIAj{CBfBCDh}}}}\",[]]Bm[\"{{{AIAj{CBfBCDj}}Mh}{{AIAj{CBfBCDj}}}}\",[]]o[\"{{}AIOf}\",[]]") \ No newline at end of file diff --git a/pr/395/docs/search.index/generic_inverted_index/5d8b801915db.js b/pr/395/docs/search.index/generic_inverted_index/5d8b801915db.js new file mode 100644 index 000000000..f2170009e --- /dev/null +++ b/pr/395/docs/search.index/generic_inverted_index/5d8b801915db.js @@ -0,0 +1 @@ +rb_("QUxqADswAAABAAC2AQIAMQPdAFAE2AA7MAAAAQAAOgIqAPkBFQAQAgkAGwIAAB4CAQAhAgAAIwIDACoCAAAsAgQANAIAADcCAgA7AgUARAIEAEoCBABRAgIAVQIAAFcCAgBbAgQAYQILAG4CBgB3AgAAgQIQAJcCAwCcAhAArgIDALMCBAC5AgUAwQIFAMgCBADOAgUA1QIFANwCAADeAgQA5AIEAOoCAwDvAgYA+AIAAAIDCAAMAw8AyQl7ADUL3QDRDwAAdRABADswAAABAAC7AQsASAbdAEUKLQB2Cg8AhwoEAI0KGwCrCgAArgoSAMQKHQDjCgUA6goqABkLGwA7MAAAAQAABQELADoCAACbAgAACwMAAO8HEwDiCgAA6QoAABYLAAAYCwAAfw7mANYPAQANEAEAOzAAAAEAAGIBBQCXCQAAFQsAABcLAAAdDVkBfxEFADswAAABAAAGAAIAQwcDAEgHAgA7MAAAAQAAKQAFABwDFAAUBgAAPgcEAEwHDACSCQEAOzAAAAEAAAoAAgAxBAAAEwwJADswAAABAAAMAAEAIAQMAAGEDAAAAmsRAABsEQAAAALREAAA0hAAAAACqwEAAEcHAABDZgAAADswAAABAADdAAoARQotAHYKDwCHCgQAjQobAKsKAACuChIAxAodAOMKBQDqCioAGQsbAA==") \ No newline at end of file diff --git a/pr/395/docs/search.index/name/7c550d2a41fa.js b/pr/395/docs/search.index/name/7c550d2a41fa.js new file mode 100644 index 000000000..52e0ed627 --- /dev/null +++ b/pr/395/docs/search.index/name/7c550d2a41fa.js @@ -0,0 +1 @@ +rd_("b()b->bBi0bCe00bFnbIdbOkb[]beq0000000000000000000000000000000000000000000000000000000000000000000000000000000bid0000000000000brtbtybu8cAck0cAnycArccBbr00cBoxcBufcClicDir0cErrcMAX000cNotcOpt00cOrdcPincUni0cVeccbbrccmp0000000000000000000cdir000cecn000cf32cf64cfmt00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cgetci32cimpcios0clen000clog0cmincmutcnew00000000000000000000000000000000000cnot0cnow000000crtt000crun0cstrcu16cu32cu64cudpdArgsdAsFddDropdEct000dEct100dFromdHashdIntodKeys0dLong0dNonedPath00dPing0dPolldReaddSenddSide0dSyncdVZipdZERO0dacks0daddr0dbooldbulkdcodedcwnd0ddataddrop0000dfrom00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dhash00000000000000000000dinto00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dmain0000dnextdopen00dpath0dperfdping000dpoll00000000000000dqlogdread00drecv00dseal0dsend00dsent0dside0dsign0dsize00dsmoldspindstop00dtake00000du128dunitdvzip000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eAsRefeByteseChunk0eCloneeCloseeCubic00eDebugeDerefeError00000000000eEvent00eKnowneOther0eRangeeReset000eRetry0eShort0eSizedeStats0eSuiteeWriteearrayebenchebuild00000000ebytes00000ecauseeclone000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eclose000ecubicederef0eerror0eframe0eindex0eis_ce0elocal0eparse0epaths0eprint00ereset00eretry0esliceespawn00estats00000000etitle0etokenetokioetupleeusizeewrite00fAcceptfAckEcn0fAes128fAes256fBackupfBorrowfBufMutfChunksfClient00fClosed00fCrypto0fCursorfFuturefHasherfIpAddrfOpenBifOpened000fOptionfOutputfPathId0fResult00000fServer00fStream000fStringfTypeIdfVarInt0faccept000fborrow00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fchunks0fcipherfclient0000fclosedfcoding0fconfig0fcrypto0000000000fdecode000000000000fdst_ipfencode000000000000ffinish00fhandlefheader0fignore0finsert00000fkeylogfoffset0fon_ack0000000fpacket00frandomfreason00000frebindfrefuse0fremote00frustls00000fserver0000fsharedfsource00000000fsrc_ip00fstatus0fstream0fstridefudp_rx000fudp_tx000fvarintfverify0fwindow0000000gAeadKey0gBlocked00gCommandgContextgDefaultgDisplaygFromStrgGeneral0gHmacKey0gInitialgInstantgKeyPair0gLayeredgMaxData0gNewReno00gOpenUnigPadding0gPathAck0gPathBufgReadBufgRequestgRuntime0gSession0gStopped0000gStreamsgToOwnedgTooLonggTryFromgTryIntogUnknowngWritten0gZeroRttgcid_len000gclientsgcommand00000gcompare0000000000000000000gconnect0gdecrypt000gdefault000000000000000000000000000000000000000000000000000gdrainedgdst_cid0000000gencrypt000giroh_hp0gis_0rtt0gmetrics0000000gmin_mtu0gon_sent000gopen_bigpath_idgpointergruntimegsrc_cid0000gstopped0gstreams000gtag_len0gtype_id00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000gupgradegversion00000hAcceptBihBytesMuthChacha20hCommandshDatagram00hDerefMuthDisabled0hDurationhEndpoint00hFinished0hIncoming0hLayerExthLongTypehMAX_SIZE0hNO_ERROR0hNewToken0hOnClosedhOpenPathhOrderinghReadable0hReceiver0hRecvMetahResponsehStreamId0hTimedOut0hToStringhTokenLog0hTooLarge0hTransmit00hUdpStats0hUnixTimehWritable0hcong_alghcontentshdatagram0hdurationhendpoint0hfinalizehframe_rx0hframe_tx0hfrom_buf0hfrom_env0hfrom_keyhfrom_strhfrom_u320hfrom_u640hgot_type000hgroup_id0000hhas_0rtthincominghinterval0hinto_any0000000his_alivehlifetime0hlocal_ip0000hmax_data0hmax_sizehnew_renohon_byteshopen_unihpriority0hprotocol0hqlog_dirhresponsehrng_seed0hssthresh0hto_owned000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_from00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_into00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_poll000000000htry_sendhvalidate000iAbandoned0iAcceptUniiArbitraryiAsyncRead0iAvailable00iBbrConfig0iBorrowMutiClosePathiCommonOptiConnectediCriterioniDatagramsiDecodableiEncodableiFormatteriFourTupleiFrameType0iHandshakeiHeaderKey0iHistogramiMalformediMaxPathId0iMultipathiPacketKey0iPartialEqiPathErroriPathEvent00iPathStats0iReadError00iTryFutureiUdpSenderiUdpSocketiValueEnumiaccept_biiarbitrary0000000ias_rustlsiassemblericlone_box0000000idatagrams000ideref_mut0ifrom_bits00iinitiator0iinto_0rttiis_client0iis_closediis_server0imay_retry0imigration0inew_timer00inew_token0ion_closediopen_path0ipath_acks0iping_pathipoll_nextipoll_read00ipoll_recvipoll_sendireach_out0ireferenceireset_key0iretry_tag0000isize_hint000istable_idito_string0000000000000000000000000000000000000000000000000000000000000itoken_key0itoken_positransport00iwait_idleiwrite_alljArgMatchesjAsyncTimerjAsyncWrite0jBATCH_SIZEjClosedPath000jComparablejConnectingjConnection00jController0jEquivalent0jFrameStats0jIntoFuturejInvalidCid0jIoSliceMutjMaybeFrame0jNamedGroupjNonZeroU32jOtherErrorjPartialOrdjPathAckEcn0jPathStatus0jQlogConfig0jRecvStream00jResetErrorjRetryError0jSendStream00jServerNamejSocketAddrjStreamInfojSubcommandjSystemTimejTimeSource0jTlsSession0jTokenStore0jUdpSockRefjWriteError0jaccept_unijallow_spin0jborrow_mut00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jclone_into000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jclose_pathjcongestion000jconn_statsjconnection0jequivalent00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jerror_code00000jframe_type0jinto_inner0jiroh_quinnjis_drained0jlocal_addr0jlost_bytes0jnew_senderjpad_to_mtu0jpath_stats00jpoll_closejpoll_flush0jpoll_write00jprint_jsonjquinn_perfjread_chunk0jread_exactjset_statusjstart_time0jthroughputjtotal_sizekAcceptErrorkCLIENT_ADDRkCipherSuitekClientStatskCloseReasonkConfigError0kContentTypekCryptoError0kCubicConfig0kDataBlocked0kFinishErrorkIdleTimeout0kIntoRequestkOutOfBounds0kPathAbandon0kQlogFactory0kResetStream0kSERVER_ADDRkSERVER_NAMEkSmolRuntimekStopSending0kStreamEvent0kStreamStats0kUnspecifiedkaccept_withkadd_address0kbind_socketkcertificatekcurrent_mtu00kdescription0kdestination00kinitial_mtu000kinitial_rtt00kinto_future0000000000000kmax_path_id0kmax_streamsknamed_layer0kon_end_acks000kon_intervalkpacing_rate0kpartial_cmp0000000000000000000kpath_eventskpath_statuskread_chunkskread_to_endkrecv_stream0ksample_size0ksend_bufferksend_stream0ksend_window00kserver_name0ktime_source0ktoken_store0kupload_sizekupper_bound0kweak_handlekwith_crypto0kwith_prefix0kwrite_chunklAckFrequency0lAddressAddedlClientConfig00000lClosedStream000000lConnectError00lConnectionId00lDatagramInfolDecryptError0lEcnCodepoint00lEncryptError0lIllegalValuelImmediateAck0lLOCALHOST_V4lLastOpenPathlNatTraversallNonZeroUsizelNoneTokenLog0lObservedAddr0lPathResponse0lPathsBlocked0lReadDatagramlRemoteStatus0lRttEstimator0lSendDatagramlServerConfig00000lSocketAddrV4lSocketAddrV6lStoppedErrorlTokioRuntimelUnstructuredlaugment_args0000lcid_lifetime000lclose_reasonlconnect_withlconservativeldata_blocked0ldrain_streamlearly_crypto000lexpect_types000lgenerate_cid000lgro_segmentslhandle_event0linitial_keys00000000linto_request0llost_packets0lmax_incoming0lmay_fragment00lnetwork_pathlnew_receiverlnoprotection0lorig_dst_cid0lpath_abandon0lpoll_timeoutlqlog_factory0lreset_stream0lsegment_size00lsend_streamslset_lifetime0lset_priority0lstop_sending0lstream_statslupload_statslwith_initial000lwrite_chunks0mAlertReceived0mBloomTokenLog0mCidsExhausted000mCloneToUninitmDatagramEventmEndpointEventmEndpointStatsmFinishedEarlymHandshakeData0mHandshakeDone0mHandshakeTypemINVALID_TOKEN0mInvalidHeadermLocallyClosed000mMaxStreamData0mMaxStreamsUni0mNewConnectionmNewRenoConfig0mParseIntErrormPartialDecodemPathChallenge0mPossibleValuemPrivateKeyDermReadableErrormRemoveAddress0mRootCertStoremStdSystemTime0mUnexpectedEndmaccepted_0rttmack_frequency00mbench_exports0mcid_generator00mcreate_sendermdata_transfermdownload_sizemduration_histmimmediate_ack0minto_incoming0mmax_ack_delay0mno_protectionmobserved_addr0mon_first_bytemon_mtu_update0000000mpath_response0mpaths_blocked0mpeer_identity0000mpoll_read_bufmpoll_shutdownmpoll_transmitmqlog_from_env0mread_datagrammsend_datagrammsend_fairness0msignature_len0mstart_session000000000nAddIpv4Address0nAddIpv6Address0nAddressRemovednAsyncUdpSocketnCertificateDernClosePathError0nCommandFactorynConnectionLost000000nEndpointConfig0nFromArgMatchesnINTERNAL_ERROR0nIncomingFuturenInvalidFrameIdnInvalidMessage00nMaxStreamsBidi0nNO_VIABLE_PATH0nNoneTokenStore0nPeerMisbehaved00nReachOutAtIpv40nReachOutAtIpv60nReadExactErrornReadToEndErrornShouldTransmitnTransferResultnTransportError00nUdpSocketStatenaead_from_hkdf0nconnect_clientncrypto_sessionndownload_statsnfor_connection000nhandle_timeoutnhandshake_data00000nhandshake_done0nhas_subcommandninitial_window0000000000000ninto_unorderedniroh_quinn_udpnis_handshaking0000nis_valid_retry000nminimum_change0nnext_1rtt_keys000npath_challenge0nqlog_from_path0nread_handshake000nread_unorderednreceive_window00nreceived_reset00nremote_address0000nremove_address0nsaturating_add000nsaturating_sub0nthroughput_bpsntime_threshold0ntotal_durationnvalue_variantsoConnectionClose000oConnectionError0oConnectionEvent0oConnectionStats0oGetRandomFailedoNewConnectionId0oOpenStreamStatsoPathCidsBlocked0oProtectedHeader0oQlogFileFactory0oSignatureSchemeoSystemTimeErroroTokenReuseError0oTransportConfig00oTryFromIntErroroVersionMismatch0oZeroRttAcceptedoZeroRttRejected000obloom_token_logoclone_to_uninit000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000odefault_runtimeogrease_quic_bit0ointegrity_limit0ointerface_indexomax_stream_data0omax_streams_uni0oparse_byte_sizeorebind_abstractosend_unreliableoserver_endpointoset_path_statusoset_send_window0oshould_transmitostream_finishedothroughput_histowrap_udp_socket00owrite_handshake000A`AddressDiscoveryA`ApplicationClose000A`CertificateErrorA`ConnectionClosed0A`ConnectionHandleA`DatagramReceivedA`EndpointStopping0A`FINAL_SIZE_ERROR0A`InconsistentKeys00A`KEY_UPDATE_ERROR0A`MaxPathIdReachedA`ObservedIpv4Addr0A`ObservedIpv6Addr0A`PathStatusBackup0A`PeerIncompatible00A`QuicClientConfig0A`QuicServerConfig0A`TokenMemoryCache0A`TooManyAddressesA`ValidationFailedA`VersionNegotiate0A`check_and_insert00000A`configure_clientA`congestion_state0A`connection_close0A`force_key_update0A`from_arg_matches00000A`iroh_quinn_protoA`max_gso_segmentsA`max_idle_timeout0A`max_streams_bidi0A`open_connections0A`open_path_ensure0A`packet_threshold0A`receive_datagramA`recv_buffer_size0A`send_buffer_size0A`transport_config0000A`validation_token0A`with_single_cert0A`write_all_chunksAaAPPLICATION_ERROR0AaApplicationClosed0AaControllerFactory0AaControllerMetrics0AaHandshakeTokenKey0AaInvalidServerName0AaPacketDecodeError0AaSendDatagramError0AaStreamDataBlocked0AaStreamsBlockedUni0AaUnsupportedByPeer0Aacongestion_events0Aacongestion_window0Aamax_datagram_sizeAanew_connection_id0Aapath_cids_blocked0Aasend_buffer_spaceAaset_server_config0Aaset_up_connectionAato_possible_valueAbAEAD_LIMIT_REACHED0AbAckFrequencyConfig0AbBadMaxFragmentSize0AbCONNECTION_REFUSED0AbConnectionIdParserAbDatagramsUnblockedAbFLOW_CONTROL_ERROR0AbHandshakeConfirmedAbHandshakeDataReadyAbIllegalOrderedReadAbInvalidCertificate0AbMtuDiscoveryConfig0AbNotEnoughAddressesAbPERF_CIPHER_SUITESAbPROTOCOL_VIOLATION0AbPrivatePkcs8KeyDerAbRetireConnectionId0AbSTREAM_LIMIT_ERROR0AbSTREAM_STATE_ERROR0AbServerCertVerifiedAbServerCertVerifierAbSetPathStatusError0AbStreamsBlockedBidi0AbTransportErrorCode0AbUnsupportedVersion00000Abcommand_for_update0000Abcrypto_buffer_size0Abfrom_u64_unchecked0Abignored_handshakesAbmin_reset_interval0Abnew_expected_items0Abpath_status_backup0Abrefused_handshakesAbremote_path_statusAbsend_datagram_waitAbset_receive_window0Absupported_versions0Abtoken_memory_cacheAbverify_server_certAbwith_start_instant0AcCongestionAlgorithmAcPathNewConnectionId0AcPathStatusAvailable0AcRemoteCidsExhaustedAcTransportParametersAcUnorderedRecvStreamAcUnsupportedNameType0AcWrongConnectionSideAcaccepted_handshakesAcarbitrary_take_rest000Acaugment_subcommandsAcblack_hole_cooldown0Acearly_data_accepted000Achandshake_confirmedAckeep_alive_interval0Aclost_plpmtud_probes0Acon_congestion_event0000000Acoutgoing_handshakesAcremote_open_streamsAcsend_buffer_benches00Acsend_data_on_streamAcsent_plpmtud_probes0Acstream_data_blocked0Acstreams_blocked_uni0AdFRAME_ENCODING_ERROR0AdHandshakeNotComplete0AdInappropriateMessage000AdInvalidRemoteAddress00AdNoInitialCipherSuite0AdServerSideNotAllowedAdSupportedCipherSuiteAdVarIntBoundsExceeded0AdVerifierBuilderErrorAdWeakConnectionHandleAdack_frequency_config0Adblack_holes_detected0Adfrom_arg_matches_mut00000Adhandle_client_streamAdincoming_buffer_size0Adis_multipath_enabledAdmax_receive_segments0Admax_udp_payload_size00Admtu_discovery_config0Adpoll_endpoint_eventsAdpreferred_address_v40Adpreferred_address_v60Adread_certs_from_fileAdreordering_threshold0Adretire_connection_id0Adretry_token_lifetime0Adset_max_idle_timeoutAdset_recv_buffer_sizeAdset_send_buffer_sizeAdstreams_blocked_bidi0Adtransport_parameters00000AeConnectionIdGenerator0AeDigitallySignedStructAeNoApplicationProtocol0AeNoDefaultClientConfig0AePATH_UNSTABLE_OR_POOR0AeValidationTokenConfig0Aeconfidentiality_limit0Aeincoming_buffer_bytesAeis_probably_same_pathAelocal_address_changedAeloss_reduction_factor0Aemax_transmit_segments0Aenat_traversal_updatesAepath_observed_addressAepath_status_available0Aestream_receive_window00AfCRYPTO_BUFFER_EXCEEDED0AfExtensionNotNegotiatedAfFailedToGetCurrentTime0AfFailedToGetRandomBytes0AfMultipathNotNegotiated00AfPathRetireConnectionId0AfProtectedInitialHeaderAfSkipServerVerificationAfbuild_transport_configAfexport_keying_material0000Afget_into_many_segmentsAfget_loop_many_segmentsAfmax_concurrent_streamsAfobserved_external_addr0Afpath_new_connection_id0Afverify_tls12_signatureAfverify_tls13_signatureAfwith_root_certificates0AgCertRevocationListErrorAgHandshakeSignatureValidAgNoCertificatesPresented0AgPeerSentOversizedRecord0Agack_eliciting_threshold0Agaugment_args_for_update0000Aghas_pending_retransmitsAgis_multipath_negotiatedAgset_keep_alive_intervalAgupdate_from_arg_matches00000Agvalidation_token_config0AhAPPLICATION_ABANDON_PATH0AhNoProtectionClientConfigAhNoProtectionServerConfigAhget_max_udp_payload_size0Ahinitial_dst_cid_provider0Ahnew_with_abstract_socketAhremote_address_validated0Ahset_max_concurrent_pathsAhsupported_verify_schemesAiCONNECTION_ID_LIMIT_ERROR0AiEncryptedClientHelloErrorAiExportKeyingMaterialError0AiInvalidCertRevocationList0AiNO_CID_AVAILABLE_FOR_PATH0AiTRANSPORT_PARAMETER_ERROR0AiUnsupportedOperationErrorAiadd_nat_traversal_address0Aidatagram_send_buffer_size0Aigenerate_self_signed_certAiopen_bidirectional_streamAipath_retire_connection_id0Aiset_default_client_configAiset_path_max_idle_timeoutAjDEFAULT_SUPPORTED_VERSIONSAjdatagram_send_buffer_spaceAjincoming_buffer_size_total0Ajmax_concurrent_uni_streams0Ajopen_unidirectional_streamAjset_max_concurrent_streamsAjtry_with_platform_verifier0AkHashedConnectionIdGeneratorAkInvalidEncryptedClientHello0AkPATH_RESOURCE_LIMIT_REACHED0AkRandomConnectionIdGeneratorAkenable_segmentation_offload0Akmax_concurrent_bidi_streams0Akupdate_from_arg_matches_mut00000Akupdate_local_if_same_remoteAlconfigure_tracing_subscriberAldatagram_receive_buffer_size0Alinitiate_nat_traversal_round0Alon_spurious_congestion_event000Alreceive_bidirectional_streamAlremove_nat_traversal_address0Alset_path_keep_alive_intervalAmFixedLengthConnectionIdParserAmInappropriateHandshakeMessage000Amcongestion_controller_factory0Amdefault_path_max_idle_timeout0Amnegotiated_key_exchange_group0Amreceive_unidirectional_streamAmsend_observed_address_reports0Amset_max_concurrent_bi_streamsAnaugment_subcommands_for_updateAnmax_concurrent_multipath_paths0Anset_max_concurrent_uni_streamsAopersistent_congestion_threshold0B`default_path_keep_alive_interval0B`receive_observed_address_reports0Baget_local_nat_traversal_addresses0Bbget_remote_nat_traversal_addresses0Bbmax_remote_nat_traversal_addressesBfset_max_remote_nat_traversal_addresses0BoDEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED") \ No newline at end of file diff --git a/pr/395/docs/search.index/normalizedName/7cb9cf25523a.js b/pr/395/docs/search.index/normalizedName/7cb9cf25523a.js new file mode 100644 index 000000000..f38c90d63 --- /dev/null +++ b/pr/395/docs/search.index/normalizedName/7cb9cf25523a.js @@ -0,0 +1 @@ +rd_("b()b->bbi0bce00bfnbidbokb[]beq000000000000000000000000000000000000000000000000000000000000000000000000000000033333333333333brtbtybu8cack0canycarccbbr00cboxcbufcclicdir0cerrcmax000cnotcopt00cordcpincuni0cvecA`[5,\"iroh_quinn\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::transport\"]Ae[10,\"tonic::request\"]Aa[10,\"iroh_quinn\"]Ch[10,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::qlog\"]Bl[5,\"iroh_quinn\",\"iroh_quinn::runtime::smol\"]Af[2,\"iroh_quinn_proto\"]Cn[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Ao[5,\"perf::stats\",\"perf::stats\"]Ci[5,\"ring::error::unspecified\",\"ring::error::unspecified\"]f[3,\"\"]A`[2,\"iroh_quinn\"]Bb[2,\"iroh_quinn_proto::connection\"]1Co[5,\"rustls::client::client_conn\",\"rustls::client::client_conn\"]Ci[5,\"iroh_quinn_proto::config\",\"iroh_quinn_proto::config\"]?Ai[10,\"iroh_quinn::crypto\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]A`[5,\"iroh_quinn\"]Cn[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Cm[6,\"iroh_quinn_proto::endpoint\",\"iroh_quinn_proto::endpoint\"]A`[6,\"iroh_quinn\"]Cc[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]Ci[5,\"iroh_quinn_proto::shared\",\"iroh_quinn_proto::shared\"]5Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]4Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]Be[6,\"iroh_quinn_udp\",\"iroh_quinn_udp\"]Bm[8,\"core::num::nonzero\",\"core::num::nonzero\"]:C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]Bb[15,\"iroh_quinn_proto::PathEvent\"]Bi[5,\"iroh_quinn\",\"iroh_quinn::connection\"]1Do[5,\"iroh_quinn_proto::connection::paths\",\"iroh_quinn_proto::connection::paths\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::paths\"]2Co[5,\"rustls::server::server_conn\",\"rustls::server::server_conn\"]Ci[5,\"iroh_quinn_proto::config\",\"iroh_quinn_proto::config\"]A`[5,\"iroh_quinn\"]Ai[10,\"iroh_quinn::crypto\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]Ce[5,\"core::net::socket_addr\",\"core::net::socket_addr\"]0Bj[6,\"iroh_quinn\",\"iroh_quinn::send_stream\"]Bm[5,\"iroh_quinn\",\"iroh_quinn::runtime::tokio\"]Cg[5,\"arbitrary::unstructured\",\"arbitrary::unstructured\"]j[2,\"perf\"]8Cj[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::bloom_token_log\"]Ab[10,\"core::clone\"]Cc[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]Bg[5,\"iroh_quinn\",\"iroh_quinn::endpoint\"]B`[5,\"iroh_quinn::crypto::rustls\"]Di[5,\"iroh_quinn_proto::crypto::rustls\",\"iroh_quinn_proto::crypto::rustls\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Bb[15,\"iroh_quinn_proto::PathEvent\"]Al[5,\"iroh_quinn::congestion\"]Dk[5,\"iroh_quinn_proto::congestion\",\"iroh_quinn_proto::congestion::new_reno\"]Bi[5,\"core::num::error\",\"core::num::error\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]Ec[5,\"clap_builder::builder::possible_value\",\"clap_builder::builder::possible_value\"]Bi[6,\"rustls_pki_types\",\"rustls_pki_types\"]Dd[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams::recv\"]Cg[5,\"rustls::webpki::anchors\",\"rustls::webpki::anchors\"]A`[5,\"iroh_quinn\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Ci[5,\"iroh_quinn_proto::coding\",\"iroh_quinn_proto::coding\"]Af[2,\"iroh_quinn_proto\"]0f[3,\"\"]Bg[10,\"iroh_quinn\",\"iroh_quinn::runtime\"]Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]Da[6,\"iroh_quinn_proto::connection\",\"iroh_quinn_proto::connection\"]Ce[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection\"]Ak[10,\"clap_builder::derive\"]An[15,\"iroh_quinn_proto::Event\"]:91Bg[5,\"iroh_quinn\",\"iroh_quinn::incoming\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]Bc[6,\"rustls::error\",\"rustls::error\"]=C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]1Bj[6,\"iroh_quinn\",\"iroh_quinn::recv_stream\"]0Cn[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Af[5,\"iroh_quinn_proto\"]Bj[5,\"iroh_quinn_udp\",\"iroh_quinn_udp::imp\"]?A`[5,\"iroh_quinn\"]8A`[6,\"iroh_quinn\"]=Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]2Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::stats\"]Ba[5,\"rustls::rand\",\"rustls::rand\"]Ao[5,\"perf::stats\",\"perf::stats\"]Af[2,\"iroh_quinn_proto\"]Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]7Cg[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::qlog\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Ak[5,\"std::time\",\"std::time\"]:C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]Do[5,\"iroh_quinn_proto::config::transport\",\"iroh_quinn_proto::config::transport\"]List of all items in this crate

All

\ No newline at end of file diff --git a/pr/395/docs/set_up_connection/constant.CLIENT_ADDR.html b/pr/395/docs/set_up_connection/constant.CLIENT_ADDR.html new file mode 100644 index 000000000..480b4b634 --- /dev/null +++ b/pr/395/docs/set_up_connection/constant.CLIENT_ADDR.html @@ -0,0 +1 @@ +CLIENT_ADDR in set_up_connection - Rust

CLIENT_ADDR

Constant CLIENT_ADDR 

Source
pub(crate) const CLIENT_ADDR: SocketAddr;
\ No newline at end of file diff --git a/pr/395/docs/set_up_connection/constant.LOCALHOST_V4.html b/pr/395/docs/set_up_connection/constant.LOCALHOST_V4.html new file mode 100644 index 000000000..49f7c0f99 --- /dev/null +++ b/pr/395/docs/set_up_connection/constant.LOCALHOST_V4.html @@ -0,0 +1 @@ +LOCALHOST_V4 in set_up_connection - Rust

LOCALHOST_V4

Constant LOCALHOST_V4 

Source
pub(crate) const LOCALHOST_V4: IpAddr;
\ No newline at end of file diff --git a/pr/395/docs/set_up_connection/constant.SERVER_ADDR.html b/pr/395/docs/set_up_connection/constant.SERVER_ADDR.html new file mode 100644 index 000000000..046828f6b --- /dev/null +++ b/pr/395/docs/set_up_connection/constant.SERVER_ADDR.html @@ -0,0 +1 @@ +SERVER_ADDR in set_up_connection - Rust

SERVER_ADDR

Constant SERVER_ADDR 

Source
pub(crate) const SERVER_ADDR: SocketAddr;
\ No newline at end of file diff --git a/pr/395/docs/set_up_connection/constant.SERVER_NAME.html b/pr/395/docs/set_up_connection/constant.SERVER_NAME.html new file mode 100644 index 000000000..84fc92733 --- /dev/null +++ b/pr/395/docs/set_up_connection/constant.SERVER_NAME.html @@ -0,0 +1 @@ +SERVER_NAME in set_up_connection - Rust

SERVER_NAME

Constant SERVER_NAME 

Source
pub(crate) const SERVER_NAME: &str = "localhost";
\ No newline at end of file diff --git a/pr/395/docs/set_up_connection/fn.client.html b/pr/395/docs/set_up_connection/fn.client.html new file mode 100644 index 000000000..0b2708bd9 --- /dev/null +++ b/pr/395/docs/set_up_connection/fn.client.html @@ -0,0 +1 @@ +client in set_up_connection - Rust

client

Function client 

Source
pub(crate) async fn client() -> Result<(), Box<dyn Error>>
\ No newline at end of file diff --git a/pr/395/docs/set_up_connection/fn.main.html b/pr/395/docs/set_up_connection/fn.main.html new file mode 100644 index 000000000..23244837f --- /dev/null +++ b/pr/395/docs/set_up_connection/fn.main.html @@ -0,0 +1 @@ +main in set_up_connection - Rust

main

Function main 

Source
pub(crate) fn main()
\ No newline at end of file diff --git a/pr/395/docs/set_up_connection/fn.server.html b/pr/395/docs/set_up_connection/fn.server.html new file mode 100644 index 000000000..2cfe5a51d --- /dev/null +++ b/pr/395/docs/set_up_connection/fn.server.html @@ -0,0 +1 @@ +server in set_up_connection - Rust

server

Function server 

Source
pub(crate) async fn server(config: ServerConfig) -> Result<(), Box<dyn Error>>
\ No newline at end of file diff --git a/pr/395/docs/set_up_connection/index.html b/pr/395/docs/set_up_connection/index.html new file mode 100644 index 000000000..9d78e0b7f --- /dev/null +++ b/pr/395/docs/set_up_connection/index.html @@ -0,0 +1 @@ +set_up_connection - Rust

Crate set_up_connection

Crate set_up_connection 

Source

Constants§

CLIENT_ADDR 🔒
LOCALHOST_V4 🔒
SERVER_ADDR 🔒
SERVER_NAME 🔒

Functions§

client 🔒
main 🔒
server 🔒
\ No newline at end of file diff --git a/pr/395/docs/set_up_connection/sidebar-items.js b/pr/395/docs/set_up_connection/sidebar-items.js new file mode 100644 index 000000000..3f14772bc --- /dev/null +++ b/pr/395/docs/set_up_connection/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"constant":["CLIENT_ADDR","LOCALHOST_V4","SERVER_ADDR","SERVER_NAME"],"fn":["client","main","server"]}; \ No newline at end of file diff --git a/pr/395/docs/settings.html b/pr/395/docs/settings.html new file mode 100644 index 000000000..0e08333cc --- /dev/null +++ b/pr/395/docs/settings.html @@ -0,0 +1 @@ +Settings

All

Rustdoc settings

Back
\ No newline at end of file diff --git a/pr/395/docs/src-files.js b/pr/395/docs/src-files.js new file mode 100644 index 000000000..bdda437df --- /dev/null +++ b/pr/395/docs/src-files.js @@ -0,0 +1,2 @@ +createSrcSidebar('[["bench",["",[],["lib.rs","stats.rs"]]],["bulk",["",[],["bulk.rs"]]],["certificate",["",[],["certificate.rs"]]],["data_transfer",["",[],["data-transfer.rs"]]],["iroh_quinn",["",[["runtime",[],["mod.rs","smol.rs","tokio.rs"]]],["connection.rs","endpoint.rs","incoming.rs","lib.rs","mutex.rs","path.rs","recv_stream.rs","send_stream.rs","work_limiter.rs"]]],["iroh_quinn_proto",["",[["config",[],["mod.rs","qlog.rs","transport.rs"]],["congestion",[["bbr",[],["bw_estimation.rs","min_max.rs","mod.rs"]]],["cubic.rs","new_reno.rs"]],["connection",[["streams",[],["mod.rs","recv.rs","send.rs","state.rs"]]],["ack_frequency.rs","assembler.rs","cid_state.rs","datagrams.rs","mod.rs","mtud.rs","pacing.rs","packet_builder.rs","packet_crypto.rs","paths.rs","qlog.rs","send_buffer.rs","spaces.rs","state.rs","stats.rs","timer.rs","transmit_buf.rs"]],["crypto",[],["ring_like.rs","rustls.rs"]],["range_set",[],["array_range_set.rs","mod.rs"]]],["address_discovery.rs","bloom_token_log.rs","cid_generator.rs","cid_queue.rs","coding.rs","congestion.rs","constant_time.rs","crypto.rs","endpoint.rs","frame.rs","iroh_hp.rs","lib.rs","packet.rs","shared.rs","token.rs","token_memory_cache.rs","transport_error.rs","transport_parameters.rs","varint.rs"]]],["iroh_quinn_udp",["",[["cmsg",[],["mod.rs","unix.rs"]]],["lib.rs","unix.rs"]]],["perf",["",[],["client.rs","lib.rs","noprotection.rs","server.rs","stats.rs"]]],["quinn_perf",["",[],["perf.rs"]]],["set_up_connection",["",[],["set-up-connection.rs"]]]]'); +//{"start":19,"fragment_lengths":[39,29,43,47,197,883,80,81,35,55]} \ No newline at end of file diff --git a/pr/395/docs/src/bench/lib.rs.html b/pr/395/docs/src/bench/lib.rs.html new file mode 100644 index 000000000..57f932cf2 --- /dev/null +++ b/pr/395/docs/src/bench/lib.rs.html @@ -0,0 +1,256 @@ +lib.rs - source

bench/
lib.rs

1use core::str;
+2use std::{
+3    convert::TryInto,
+4    net::{IpAddr, Ipv6Addr, SocketAddr},
+5    num::ParseIntError,
+6    str::FromStr,
+7    sync::Arc,
+8};
+9
+10use anyhow::{Context, Result};
+11use bytes::Bytes;
+12use clap::Parser;
+13use quinn::crypto::rustls::QuicClientConfig;
+14use rustls::{
+15    RootCertStore,
+16    pki_types::{CertificateDer, PrivateKeyDer},
+17};
+18use tokio::runtime::{Builder, Runtime};
+19use tracing::trace;
+20
+21pub mod stats;
+22
+23pub fn configure_tracing_subscriber() {
+24    tracing::subscriber::set_global_default(
+25        tracing_subscriber::FmtSubscriber::builder()
+26            .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
+27            .finish(),
+28    )
+29    .unwrap();
+30}
+31
+32/// Creates a server endpoint which runs on the given runtime
+33pub fn server_endpoint(
+34    rt: &tokio::runtime::Runtime,
+35    cert: CertificateDer<'static>,
+36    key: PrivateKeyDer<'static>,
+37    opt: &Opt,
+38) -> (SocketAddr, quinn::Endpoint) {
+39    let cert_chain = vec![cert];
+40    let mut server_config = quinn::ServerConfig::with_single_cert(cert_chain, key).unwrap();
+41    server_config.transport = Arc::new(transport_config(opt));
+42
+43    let endpoint = {
+44        let _guard = rt.enter();
+45        quinn::Endpoint::server(
+46            server_config,
+47            SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 0),
+48        )
+49        .unwrap()
+50    };
+51    let server_addr = endpoint.local_addr().unwrap();
+52    (server_addr, endpoint)
+53}
+54
+55/// Create a client endpoint and client connection
+56pub async fn connect_client(
+57    server_addr: SocketAddr,
+58    server_cert: CertificateDer<'_>,
+59    opt: Opt,
+60) -> Result<(quinn::Endpoint, quinn::Connection)> {
+61    let endpoint =
+62        quinn::Endpoint::client(SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 0)).unwrap();
+63
+64    let mut roots = RootCertStore::empty();
+65    roots.add(server_cert)?;
+66
+67    let default_provider = rustls::crypto::ring::default_provider();
+68    let provider = rustls::crypto::CryptoProvider {
+69        cipher_suites: vec![opt.cipher.as_rustls()],
+70        ..default_provider
+71    };
+72
+73    let crypto = rustls::ClientConfig::builder_with_provider(provider.into())
+74        .with_protocol_versions(&[&rustls::version::TLS13])
+75        .unwrap()
+76        .with_root_certificates(roots)
+77        .with_no_client_auth();
+78
+79    let mut client_config = quinn::ClientConfig::new(Arc::new(QuicClientConfig::try_from(crypto)?));
+80    client_config.transport_config(Arc::new(transport_config(&opt)));
+81
+82    let connection = endpoint
+83        .connect_with(client_config, server_addr, "localhost")
+84        .unwrap()
+85        .await
+86        .context("unable to connect")?;
+87    trace!("connected");
+88
+89    Ok((endpoint, connection))
+90}
+91
+92pub async fn drain_stream(mut stream: quinn::RecvStream, read_unordered: bool) -> Result<usize> {
+93    let mut read = 0;
+94
+95    if read_unordered {
+96        let mut stream = stream.into_unordered();
+97        while let Some(chunk) = stream.read_chunk(usize::MAX).await? {
+98            read += chunk.bytes.len();
+99        }
+100    } else {
+101        // These are 32 buffers, for reading approximately 32kB at once
+102        #[rustfmt::skip]
+103        let mut bufs = [
+104            Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+105            Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+106            Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+107            Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+108            Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+109            Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+110            Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+111            Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+112        ];
+113
+114        while let Some(n) = stream.read_chunks(&mut bufs[..]).await? {
+115            read += bufs.iter().take(n).map(|buf| buf.len()).sum::<usize>();
+116        }
+117    }
+118
+119    Ok(read)
+120}
+121
+122pub async fn send_data_on_stream(stream: &mut quinn::SendStream, stream_size: u64) -> Result<()> {
+123    const DATA: &[u8] = &[0xAB; 1024 * 1024];
+124    let bytes_data = Bytes::from_static(DATA);
+125
+126    let full_chunks = stream_size / (DATA.len() as u64);
+127    let remaining = (stream_size % (DATA.len() as u64)) as usize;
+128
+129    for _ in 0..full_chunks {
+130        stream
+131            .write_chunk(bytes_data.clone())
+132            .await
+133            .context("failed sending data")?;
+134    }
+135
+136    if remaining != 0 {
+137        stream
+138            .write_chunk(bytes_data.slice(0..remaining))
+139            .await
+140            .context("failed sending data")?;
+141    }
+142
+143    stream.finish().unwrap();
+144    // Wait for stream to close
+145    _ = stream.stopped().await;
+146
+147    Ok(())
+148}
+149
+150pub fn rt() -> Runtime {
+151    Builder::new_current_thread().enable_all().build().unwrap()
+152}
+153
+154pub fn transport_config(opt: &Opt) -> quinn::TransportConfig {
+155    // High stream windows are chosen because the amount of concurrent streams
+156    // is configurable as a parameter.
+157    let mut config = quinn::TransportConfig::default();
+158    config.max_concurrent_uni_streams(opt.max_streams.try_into().unwrap());
+159    config.initial_mtu(opt.initial_mtu);
+160
+161    let mut acks = quinn::AckFrequencyConfig::default();
+162    acks.ack_eliciting_threshold(10u32.into());
+163    config.ack_frequency_config(Some(acks));
+164
+165    config
+166}
+167
+168#[derive(Parser, Debug, Clone, Copy)]
+169#[clap(name = "bulk")]
+170pub struct Opt {
+171    /// The total number of clients which should be created
+172    #[clap(long = "clients", short = 'c', default_value = "1")]
+173    pub clients: usize,
+174    /// The total number of streams which should be created
+175    #[clap(long = "streams", short = 'n', default_value = "1")]
+176    pub streams: usize,
+177    /// The amount of concurrent streams which should be used
+178    #[clap(long = "max_streams", short = 'm', default_value = "1")]
+179    pub max_streams: usize,
+180    /// Number of bytes to transmit from server to client
+181    ///
+182    /// This can use SI suffixes for sizes. For example, 1M will transfer
+183    /// 1MiB, 10G will transfer 10GiB.
+184    #[clap(long, default_value = "1G", value_parser = parse_byte_size)]
+185    pub download_size: u64,
+186    /// Number of bytes to transmit from client to server
+187    ///
+188    /// This can use SI suffixes for sizes. For example, 1M will transfer
+189    /// 1MiB, 10G will transfer 10GiB.
+190    #[clap(long, default_value = "0", value_parser = parse_byte_size)]
+191    pub upload_size: u64,
+192    /// Show connection stats the at the end of the benchmark
+193    #[clap(long = "stats")]
+194    pub stats: bool,
+195    /// Whether to use the unordered read API
+196    #[clap(long = "unordered")]
+197    pub read_unordered: bool,
+198    /// Allows to configure the desired cipher suite
+199    ///
+200    /// Valid options are: aes128, aes256, chacha20
+201    #[clap(long = "cipher", default_value = "aes128")]
+202    pub cipher: CipherSuite,
+203    /// Starting guess for maximum UDP payload size
+204    #[clap(long, default_value = "1200")]
+205    pub initial_mtu: u16,
+206}
+207
+208fn parse_byte_size(s: &str) -> Result<u64, ParseIntError> {
+209    let s = s.trim();
+210
+211    let multiplier = match s.chars().last() {
+212        Some('T') => 1024 * 1024 * 1024 * 1024,
+213        Some('G') => 1024 * 1024 * 1024,
+214        Some('M') => 1024 * 1024,
+215        Some('k') => 1024,
+216        _ => 1,
+217    };
+218
+219    let s = match multiplier {
+220        1 => s,
+221        _ => &s[..s.len() - 1],
+222    };
+223
+224    Ok(u64::from_str(s)? * multiplier)
+225}
+226
+227#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+228pub enum CipherSuite {
+229    Aes128,
+230    Aes256,
+231    Chacha20,
+232}
+233
+234impl CipherSuite {
+235    pub fn as_rustls(self) -> rustls::SupportedCipherSuite {
+236        use rustls::crypto::ring::cipher_suite;
+237        match self {
+238            Self::Aes128 => cipher_suite::TLS13_AES_128_GCM_SHA256,
+239            Self::Aes256 => cipher_suite::TLS13_AES_256_GCM_SHA384,
+240            Self::Chacha20 => cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
+241        }
+242    }
+243}
+244
+245impl FromStr for CipherSuite {
+246    type Err = anyhow::Error;
+247
+248    fn from_str(s: &str) -> Result<Self, Self::Err> {
+249        match s.to_lowercase().as_str() {
+250            "aes128" => Ok(Self::Aes128),
+251            "aes256" => Ok(Self::Aes256),
+252            "chacha20" => Ok(Self::Chacha20),
+253            _ => Err(anyhow::anyhow!("Unknown cipher suite {}", s)),
+254        }
+255    }
+256}
\ No newline at end of file diff --git a/pr/395/docs/src/bench/stats.rs.html b/pr/395/docs/src/bench/stats.rs.html new file mode 100644 index 000000000..b8c881d8d --- /dev/null +++ b/pr/395/docs/src/bench/stats.rs.html @@ -0,0 +1,95 @@ +stats.rs - source

bench/
stats.rs

1use std::time::Duration;
+2
+3use hdrhistogram::Histogram;
+4
+5#[derive(Default)]
+6pub struct Stats {
+7    pub total_size: u64,
+8    pub total_duration: Duration,
+9    pub streams: usize,
+10    pub stream_stats: StreamStats,
+11}
+12
+13impl Stats {
+14    pub fn stream_finished(&mut self, stream_result: TransferResult) {
+15        self.total_size += stream_result.size;
+16        self.streams += 1;
+17
+18        self.stream_stats
+19            .duration_hist
+20            .record(stream_result.duration.as_millis() as u64)
+21            .unwrap();
+22        self.stream_stats
+23            .throughput_hist
+24            .record(stream_result.throughput as u64)
+25            .unwrap();
+26    }
+27
+28    pub fn print(&self, stat_name: &str) {
+29        println!("Overall {stat_name} stats:\n");
+30        println!(
+31            "Transferred {} bytes on {} streams in {:4.2?} ({:.2} MiB/s)\n",
+32            self.total_size,
+33            self.streams,
+34            self.total_duration,
+35            throughput_bps(self.total_duration, self.total_size) / 1024.0 / 1024.0
+36        );
+37
+38        println!("Stream {stat_name} metrics:\n");
+39
+40        println!("      │  Throughput   │ Duration ");
+41        println!("──────┼───────────────┼──────────");
+42
+43        let print_metric = |label: &'static str, get_metric: fn(&Histogram<u64>) -> u64| {
+44            println!(
+45                " {} │ {:7.2} MiB/s │ {:>9.2?}",
+46                label,
+47                get_metric(&self.stream_stats.throughput_hist) as f64 / 1024.0 / 1024.0,
+48                Duration::from_millis(get_metric(&self.stream_stats.duration_hist))
+49            );
+50        };
+51
+52        print_metric("AVG ", |hist| hist.mean() as u64);
+53        print_metric("P0  ", |hist| hist.value_at_quantile(0.00));
+54        print_metric("P10 ", |hist| hist.value_at_quantile(0.10));
+55        print_metric("P50 ", |hist| hist.value_at_quantile(0.50));
+56        print_metric("P90 ", |hist| hist.value_at_quantile(0.90));
+57        print_metric("P100", |hist| hist.value_at_quantile(1.00));
+58    }
+59}
+60
+61pub struct StreamStats {
+62    pub duration_hist: Histogram<u64>,
+63    pub throughput_hist: Histogram<u64>,
+64}
+65
+66impl Default for StreamStats {
+67    fn default() -> Self {
+68        Self {
+69            duration_hist: Histogram::<u64>::new(3).unwrap(),
+70            throughput_hist: Histogram::<u64>::new(3).unwrap(),
+71        }
+72    }
+73}
+74
+75#[derive(Debug)]
+76pub struct TransferResult {
+77    pub duration: Duration,
+78    pub size: u64,
+79    pub throughput: f64,
+80}
+81
+82impl TransferResult {
+83    pub fn new(duration: Duration, size: u64) -> Self {
+84        let throughput = throughput_bps(duration, size);
+85        Self {
+86            duration,
+87            size,
+88            throughput,
+89        }
+90    }
+91}
+92
+93pub fn throughput_bps(duration: Duration, size: u64) -> f64 {
+94    (size as f64) / (duration.as_secs_f64())
+95}
\ No newline at end of file diff --git a/pr/395/docs/src/bulk/bulk.rs.html b/pr/395/docs/src/bulk/bulk.rs.html new file mode 100644 index 000000000..c6990807c --- /dev/null +++ b/pr/395/docs/src/bulk/bulk.rs.html @@ -0,0 +1,219 @@ +bulk.rs - source

bulk/
bulk.rs

1use std::{
+2    net::SocketAddr,
+3    sync::{Arc, Mutex},
+4    time::Instant,
+5};
+6
+7use anyhow::{Context, Result};
+8use clap::Parser;
+9use rustls::pki_types::{CertificateDer, PrivatePkcs8KeyDer};
+10use tokio::sync::Semaphore;
+11use tracing::{info, trace};
+12
+13use bench::{
+14    Opt, configure_tracing_subscriber, connect_client, drain_stream, rt, send_data_on_stream,
+15    server_endpoint,
+16    stats::{Stats, TransferResult},
+17};
+18
+19fn main() {
+20    let opt = Opt::parse();
+21    configure_tracing_subscriber();
+22
+23    let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()]).unwrap();
+24    let key = PrivatePkcs8KeyDer::from(cert.signing_key.serialize_der());
+25    let cert = CertificateDer::from(cert.cert);
+26
+27    let server_span = tracing::error_span!("server");
+28    let runtime = rt();
+29    let (server_addr, endpoint) = {
+30        let _guard = server_span.enter();
+31        server_endpoint(&runtime, cert.clone(), key.into(), &opt)
+32    };
+33
+34    let server_thread = std::thread::spawn(move || {
+35        let _guard = server_span.entered();
+36        if let Err(e) = runtime.block_on(server(endpoint, opt)) {
+37            eprintln!("server failed: {e:#}");
+38        }
+39    });
+40
+41    let mut handles = Vec::new();
+42    for id in 0..opt.clients {
+43        let cert = cert.clone();
+44        handles.push(std::thread::spawn(move || {
+45            let _guard = tracing::error_span!("client", id).entered();
+46            let runtime = rt();
+47            match runtime.block_on(client(server_addr, cert, opt)) {
+48                Ok(stats) => Ok(stats),
+49                Err(e) => {
+50                    eprintln!("client failed: {e:#}");
+51                    Err(e)
+52                }
+53            }
+54        }));
+55    }
+56
+57    for (id, handle) in handles.into_iter().enumerate() {
+58        // We print all stats at the end of the test sequentially to avoid
+59        // them being garbled due to being printed concurrently
+60        if let Ok(stats) = handle.join().expect("client thread") {
+61            stats.print(id);
+62        }
+63    }
+64
+65    server_thread.join().expect("server thread");
+66}
+67
+68async fn server(endpoint: quinn::Endpoint, opt: Opt) -> Result<()> {
+69    let mut server_tasks = Vec::new();
+70
+71    // Handle only the expected amount of clients
+72    for _ in 0..opt.clients {
+73        let handshake = endpoint.accept().await.unwrap();
+74        let connection = handshake.await.context("handshake failed")?;
+75
+76        server_tasks.push(tokio::spawn(async move {
+77            loop {
+78                let (mut send_stream, recv_stream) = match connection.accept_bi().await {
+79                    Err(quinn::ConnectionError::ApplicationClosed(_)) => break,
+80                    Err(e) => {
+81                        eprintln!("accepting stream failed: {e:?}");
+82                        break;
+83                    }
+84                    Ok(stream) => stream,
+85                };
+86                trace!("stream established");
+87
+88                tokio::spawn(async move {
+89                    drain_stream(recv_stream, opt.read_unordered).await?;
+90                    send_data_on_stream(&mut send_stream, opt.download_size).await?;
+91                    Ok::<_, anyhow::Error>(())
+92                });
+93            }
+94
+95            if opt.stats {
+96                println!("\nServer connection stats:\n{:#?}", connection.stats());
+97            }
+98        }));
+99    }
+100
+101    // Await all the tasks. We have to do this to prevent the runtime getting dropped
+102    // and all server tasks to be cancelled
+103    for handle in server_tasks {
+104        if let Err(e) = handle.await {
+105            eprintln!("Server task error: {e:?}");
+106        };
+107    }
+108
+109    Ok(())
+110}
+111
+112async fn client(
+113    server_addr: SocketAddr,
+114    server_cert: CertificateDer<'static>,
+115    opt: Opt,
+116) -> Result<ClientStats> {
+117    let (endpoint, connection) = connect_client(server_addr, server_cert, opt).await?;
+118
+119    let start = Instant::now();
+120
+121    let connection = Arc::new(connection);
+122
+123    let mut stats = ClientStats::default();
+124    let mut first_error = None;
+125
+126    let sem = Arc::new(Semaphore::new(opt.max_streams));
+127    let results = Arc::new(Mutex::new(Vec::new()));
+128    for _ in 0..opt.streams {
+129        let permit = sem.clone().acquire_owned().await.unwrap();
+130        let results = results.clone();
+131        let connection = connection.clone();
+132        tokio::spawn(async move {
+133            let result =
+134                handle_client_stream(connection, opt.upload_size, opt.read_unordered).await;
+135            info!("stream finished: {:?}", result);
+136            results.lock().unwrap().push(result);
+137            drop(permit);
+138        });
+139    }
+140
+141    // Wait for remaining streams to finish
+142    let _ = sem.acquire_many(opt.max_streams as u32).await.unwrap();
+143
+144    for result in results.lock().unwrap().drain(..) {
+145        match result {
+146            Ok((upload_result, download_result)) => {
+147                stats.upload_stats.stream_finished(upload_result);
+148                stats.download_stats.stream_finished(download_result);
+149            }
+150            Err(e) => {
+151                if first_error.is_none() {
+152                    first_error = Some(e);
+153                }
+154            }
+155        }
+156    }
+157
+158    stats.upload_stats.total_duration = start.elapsed();
+159    stats.download_stats.total_duration = start.elapsed();
+160
+161    // Explicit close of the connection, since handles can still be around due
+162    // to `Arc`ing them
+163    connection.close(0u32.into(), b"Benchmark done");
+164
+165    endpoint.wait_idle().await;
+166
+167    if opt.stats {
+168        println!("\nClient connection stats:\n{:#?}", connection.stats());
+169    }
+170
+171    match first_error {
+172        None => Ok(stats),
+173        Some(e) => Err(e),
+174    }
+175}
+176
+177async fn handle_client_stream(
+178    connection: Arc<quinn::Connection>,
+179    upload_size: u64,
+180    read_unordered: bool,
+181) -> Result<(TransferResult, TransferResult)> {
+182    let start = Instant::now();
+183
+184    let (mut send_stream, recv_stream) = connection
+185        .open_bi()
+186        .await
+187        .context("failed to open stream")?;
+188
+189    send_data_on_stream(&mut send_stream, upload_size).await?;
+190
+191    let upload_result = TransferResult::new(start.elapsed(), upload_size);
+192
+193    let start = Instant::now();
+194    let size = drain_stream(recv_stream, read_unordered).await?;
+195    let download_result = TransferResult::new(start.elapsed(), size as u64);
+196
+197    Ok((upload_result, download_result))
+198}
+199
+200#[derive(Default)]
+201struct ClientStats {
+202    upload_stats: Stats,
+203    download_stats: Stats,
+204}
+205
+206impl ClientStats {
+207    fn print(&self, client_id: usize) {
+208        println!();
+209        println!("Client {client_id} stats:");
+210
+211        if self.upload_stats.total_size != 0 {
+212            self.upload_stats.print("upload");
+213        }
+214
+215        if self.download_stats.total_size != 0 {
+216            self.download_stats.print("download");
+217        }
+218    }
+219}
\ No newline at end of file diff --git a/pr/395/docs/src/certificate/certificate.rs.html b/pr/395/docs/src/certificate/certificate.rs.html new file mode 100644 index 000000000..e8d65d513 --- /dev/null +++ b/pr/395/docs/src/certificate/certificate.rs.html @@ -0,0 +1,106 @@ +certificate.rs - source

certificate/
certificate.rs

1use std::{error::Error, sync::Arc};
+2
+3use quinn::{
+4    ClientConfig,
+5    crypto::rustls::{NoInitialCipherSuite, QuicClientConfig},
+6};
+7use rustls::{
+8    DigitallySignedStruct, SignatureScheme,
+9    client::danger,
+10    crypto::{CryptoProvider, verify_tls12_signature, verify_tls13_signature},
+11    pki_types::{
+12        CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer, ServerName, UnixTime, pem::PemObject,
+13    },
+14};
+15
+16#[allow(unused_variables)]
+17fn main() {
+18    let (self_signed_certs, self_signed_key) = generate_self_signed_cert().unwrap();
+19    let (certs, key) = read_certs_from_file().unwrap();
+20    let server_config = quinn::ServerConfig::with_single_cert(certs, key);
+21    let client_config = quinn::ClientConfig::try_with_platform_verifier().unwrap();
+22}
+23
+24#[allow(dead_code)] // Included in `certificate.md`
+25fn configure_client() -> Result<ClientConfig, NoInitialCipherSuite> {
+26    let crypto = rustls::ClientConfig::builder()
+27        .dangerous()
+28        .with_custom_certificate_verifier(SkipServerVerification::new())
+29        .with_no_client_auth();
+30
+31    Ok(ClientConfig::new(Arc::new(QuicClientConfig::try_from(
+32        crypto,
+33    )?)))
+34}
+35
+36// Implementation of `ServerCertVerifier` that verifies everything as trustworthy.
+37#[derive(Debug)]
+38struct SkipServerVerification(Arc<CryptoProvider>);
+39
+40impl SkipServerVerification {
+41    fn new() -> Arc<Self> {
+42        Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider())))
+43    }
+44}
+45
+46impl danger::ServerCertVerifier for SkipServerVerification {
+47    fn verify_server_cert(
+48        &self,
+49        _end_entity: &CertificateDer<'_>,
+50        _intermediates: &[CertificateDer<'_>],
+51        _server_name: &ServerName<'_>,
+52        _ocsp: &[u8],
+53        _now: UnixTime,
+54    ) -> Result<danger::ServerCertVerified, rustls::Error> {
+55        Ok(danger::ServerCertVerified::assertion())
+56    }
+57    fn verify_tls12_signature(
+58        &self,
+59        message: &[u8],
+60        cert: &CertificateDer<'_>,
+61        dss: &DigitallySignedStruct,
+62    ) -> Result<danger::HandshakeSignatureValid, rustls::Error> {
+63        verify_tls12_signature(
+64            message,
+65            cert,
+66            dss,
+67            &self.0.signature_verification_algorithms,
+68        )
+69    }
+70
+71    fn verify_tls13_signature(
+72        &self,
+73        message: &[u8],
+74        cert: &CertificateDer<'_>,
+75        dss: &DigitallySignedStruct,
+76    ) -> Result<danger::HandshakeSignatureValid, rustls::Error> {
+77        verify_tls13_signature(
+78            message,
+79            cert,
+80            dss,
+81            &self.0.signature_verification_algorithms,
+82        )
+83    }
+84
+85    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
+86        self.0.signature_verification_algorithms.supported_schemes()
+87    }
+88}
+89
+90fn generate_self_signed_cert()
+91-> Result<(CertificateDer<'static>, PrivatePkcs8KeyDer<'static>), Box<dyn Error>> {
+92    let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_string()])?;
+93    let cert_der = CertificateDer::from(cert.cert);
+94    let key = PrivatePkcs8KeyDer::from(cert.signing_key.serialize_der());
+95    Ok((cert_der, key))
+96}
+97
+98fn read_certs_from_file()
+99-> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>), Box<dyn Error>> {
+100    let certs = CertificateDer::pem_file_iter("./fullchain.pem")
+101        .unwrap()
+102        .map(|cert| cert.unwrap())
+103        .collect();
+104    let key = PrivateKeyDer::from_pem_file("./privkey.pem").unwrap();
+105    Ok((certs, key))
+106}
\ No newline at end of file diff --git a/pr/395/docs/src/data_transfer/data-transfer.rs.html b/pr/395/docs/src/data_transfer/data-transfer.rs.html new file mode 100644 index 000000000..fb76e6aea --- /dev/null +++ b/pr/395/docs/src/data_transfer/data-transfer.rs.html @@ -0,0 +1,56 @@ +data-transfer.rs - source

data_transfer/
data-transfer.rs

1use bytes::Bytes;
+2use quinn::Connection;
+3
+4fn main() {}
+5
+6#[allow(dead_code, unused_variables)] // Included in `data-transfer.md`
+7async fn open_bidirectional_stream(connection: Connection) -> anyhow::Result<()> {
+8    let (mut send, mut recv) = connection.open_bi().await?;
+9    send.write_all(b"test").await?;
+10    send.finish()?;
+11    let received = recv.read_to_end(10).await?;
+12    Ok(())
+13}
+14
+15#[allow(dead_code)] // Included in `data-transfer.md`
+16async fn receive_bidirectional_stream(connection: Connection) -> anyhow::Result<()> {
+17    while let Ok((mut send, mut recv)) = connection.accept_bi().await {
+18        // Because it is a bidirectional stream, we can both send and receive.
+19        println!("request: {:?}", recv.read_to_end(50).await?);
+20        send.write_all(b"response").await?;
+21        send.finish()?;
+22    }
+23    Ok(())
+24}
+25
+26#[allow(dead_code)] // Included in `data-transfer.md`
+27async fn open_unidirectional_stream(connection: Connection) -> anyhow::Result<()> {
+28    let mut send = connection.open_uni().await?;
+29    send.write_all(b"test").await?;
+30    send.finish()?;
+31    Ok(())
+32}
+33
+34#[allow(dead_code)] // Included in `data-transfer.md`
+35async fn receive_unidirectional_stream(connection: Connection) -> anyhow::Result<()> {
+36    while let Ok(mut recv) = connection.accept_uni().await {
+37        // Because it is a unidirectional stream, we can only receive not send back.
+38        println!("{:?}", recv.read_to_end(50).await?);
+39    }
+40    Ok(())
+41}
+42
+43#[allow(dead_code)] // Included in `data-transfer.md`
+44async fn send_unreliable(connection: Connection) -> anyhow::Result<()> {
+45    connection.send_datagram(Bytes::from(&b"test"[..]))?;
+46    Ok(())
+47}
+48
+49#[allow(dead_code)] // Included in `data-transfer.md`
+50async fn receive_datagram(connection: Connection) -> anyhow::Result<()> {
+51    while let Ok(received_bytes) = connection.read_datagram().await {
+52        // Because it is a unidirectional stream, we can only receive not send back.
+53        println!("request: {:?}", received_bytes);
+54    }
+55    Ok(())
+56}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn/connection.rs.html b/pr/395/docs/src/iroh_quinn/connection.rs.html new file mode 100644 index 000000000..42856ce52 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn/connection.rs.html @@ -0,0 +1,1768 @@ +connection.rs - source

iroh_quinn/
connection.rs

1use std::{
+2    any::Any,
+3    fmt,
+4    future::Future,
+5    io,
+6    net::{IpAddr, SocketAddr},
+7    num::NonZeroUsize,
+8    pin::Pin,
+9    sync::{Arc, Weak},
+10    task::{Context, Poll, Waker, ready},
+11};
+12
+13use bytes::Bytes;
+14use pin_project_lite::pin_project;
+15use rustc_hash::FxHashMap;
+16use thiserror::Error;
+17use tokio::sync::{Notify, futures::Notified, mpsc, oneshot, watch};
+18use tracing::{Instrument, Span, debug_span};
+19
+20use crate::{
+21    ConnectionEvent, Duration, Instant, Path, VarInt,
+22    endpoint::ensure_ipv6,
+23    mutex::Mutex,
+24    path::OpenPath,
+25    recv_stream::RecvStream,
+26    runtime::{AsyncTimer, Runtime, UdpSender},
+27    send_stream::SendStream,
+28    udp_transmit,
+29};
+30use proto::{
+31    ConnectionError, ConnectionHandle, ConnectionStats, Dir, EndpointEvent, FourTuple, PathError,
+32    PathEvent, PathId, PathStats, PathStatus, Side, StreamEvent, StreamId, TransportError,
+33    TransportErrorCode, congestion::Controller, iroh_hp,
+34};
+35
+36/// In-progress connection attempt future
+37#[derive(Debug)]
+38pub struct Connecting {
+39    conn: Option<ConnectionRef>,
+40    connected: oneshot::Receiver<bool>,
+41    handshake_data_ready: Option<oneshot::Receiver<()>>,
+42}
+43
+44impl Connecting {
+45    pub(crate) fn new(
+46        handle: ConnectionHandle,
+47        conn: proto::Connection,
+48        endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+49        conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+50        sender: Pin<Box<dyn UdpSender>>,
+51        runtime: Arc<dyn Runtime>,
+52    ) -> Self {
+53        let (on_handshake_data_send, on_handshake_data_recv) = oneshot::channel();
+54        let (on_connected_send, on_connected_recv) = oneshot::channel();
+55
+56        let conn = ConnectionRef(Arc::new(Arc::new(ConnectionInner {
+57            state: Mutex::new(State::new(
+58                conn,
+59                handle,
+60                endpoint_events,
+61                conn_events,
+62                on_handshake_data_send,
+63                on_connected_send,
+64                sender,
+65                runtime.clone(),
+66            )),
+67            shared: Shared::default(),
+68        })));
+69
+70        let driver = ConnectionDriver(conn.clone());
+71        runtime.spawn(Box::pin(
+72            async {
+73                if let Err(e) = driver.await {
+74                    tracing::error!("I/O error: {e}");
+75                }
+76            }
+77            .instrument(Span::current()),
+78        ));
+79
+80        Self {
+81            conn: Some(conn),
+82            connected: on_connected_recv,
+83            handshake_data_ready: Some(on_handshake_data_recv),
+84        }
+85    }
+86
+87    /// Convert into a 0-RTT or 0.5-RTT connection at the cost of weakened security
+88    ///
+89    /// Returns `Ok` immediately if the local endpoint is able to attempt sending 0/0.5-RTT data.
+90    /// If so, the returned [`Connection`] can be used to send application data without waiting for
+91    /// the rest of the handshake to complete, at the cost of weakened cryptographic security
+92    /// guarantees. The returned [`ZeroRttAccepted`] future resolves when the handshake does
+93    /// complete, at which point subsequently opened streams and written data will have full
+94    /// cryptographic protection.
+95    ///
+96    /// ## Outgoing
+97    ///
+98    /// For outgoing connections, the initial attempt to convert to a [`Connection`] which sends
+99    /// 0-RTT data will proceed if the [`crypto::ClientConfig`][crate::crypto::ClientConfig]
+100    /// attempts to resume a previous TLS session. However, **the remote endpoint may not actually
+101    /// _accept_ the 0-RTT data**--yet still accept the connection attempt in general. This
+102    /// possibility is conveyed through the [`ZeroRttAccepted`] future--when the handshake
+103    /// completes, it resolves to true if the 0-RTT data was accepted and false if it was rejected.
+104    /// If it was rejected, the existence of streams opened and other application data sent prior
+105    /// to the handshake completing will not be conveyed to the remote application, and local
+106    /// operations on them will return `ZeroRttRejected` errors.
+107    ///
+108    /// A server may reject 0-RTT data at its discretion, but accepting 0-RTT data requires the
+109    /// relevant resumption state to be stored in the server, which servers may limit or lose for
+110    /// various reasons including not persisting resumption state across server restarts.
+111    ///
+112    /// If manually providing a [`crypto::ClientConfig`][crate::crypto::ClientConfig], check your
+113    /// implementation's docs for 0-RTT pitfalls.
+114    ///
+115    /// ## Incoming
+116    ///
+117    /// For incoming connections, conversion to 0.5-RTT will always fully succeed. `into_0rtt` will
+118    /// always return `Ok` and the [`ZeroRttAccepted`] will always resolve to true.
+119    ///
+120    /// If manually providing a [`crypto::ServerConfig`][crate::crypto::ServerConfig], check your
+121    /// implementation's docs for 0-RTT pitfalls.
+122    ///
+123    /// ## Security
+124    ///
+125    /// On outgoing connections, this enables transmission of 0-RTT data, which is vulnerable to
+126    /// replay attacks, and should therefore never invoke non-idempotent operations.
+127    ///
+128    /// On incoming connections, this enables transmission of 0.5-RTT data, which may be sent
+129    /// before TLS client authentication has occurred, and should therefore not be used to send
+130    /// data for which client authentication is being used.
+131    pub fn into_0rtt(mut self) -> Result<(Connection, ZeroRttAccepted), Self> {
+132        // This lock borrows `self` and would normally be dropped at the end of this scope, so we'll
+133        // have to release it explicitly before returning `self` by value.
+134        let conn = (self.conn.as_mut().unwrap()).state.lock("into_0rtt");
+135
+136        let is_ok = conn.inner.has_0rtt() || conn.inner.side().is_server();
+137        drop(conn);
+138
+139        if is_ok {
+140            let conn = self.conn.take().unwrap();
+141            Ok((Connection(conn), ZeroRttAccepted(self.connected)))
+142        } else {
+143            Err(self)
+144        }
+145    }
+146
+147    /// Parameters negotiated during the handshake
+148    ///
+149    /// The dynamic type returned is determined by the configured
+150    /// [`Session`](proto::crypto::Session). For the default `rustls` session, the return value can
+151    /// be [`downcast`](Box::downcast) to a
+152    /// [`crypto::rustls::HandshakeData`](crate::crypto::rustls::HandshakeData).
+153    pub async fn handshake_data(&mut self) -> Result<Box<dyn Any>, ConnectionError> {
+154        // Taking &mut self allows us to use a single oneshot channel rather than dealing with
+155        // potentially many tasks waiting on the same event. It's a bit of a hack, but keeps things
+156        // simple.
+157        if let Some(x) = self.handshake_data_ready.take() {
+158            let _ = x.await;
+159        }
+160        let conn = self.conn.as_ref().unwrap();
+161        let inner = conn.state.lock("handshake");
+162        inner
+163            .inner
+164            .crypto_session()
+165            .handshake_data()
+166            .ok_or_else(|| {
+167                inner
+168                    .error
+169                    .clone()
+170                    .expect("spurious handshake data ready notification")
+171            })
+172    }
+173
+174    /// The local IP address which was used when the peer established
+175    /// the connection
+176    ///
+177    /// This can be different from the address the endpoint is bound to, in case
+178    /// the endpoint is bound to a wildcard address like `0.0.0.0` or `::`.
+179    ///
+180    /// This will return `None` for clients, or when the platform does not expose this
+181    /// information. See [`quinn_udp::RecvMeta::dst_ip`](udp::RecvMeta::dst_ip) for a list of
+182    /// supported platforms when using [`quinn_udp`](udp) for I/O, which is the default.
+183    ///
+184    /// Will panic if called after `poll` has returned `Ready`.
+185    pub fn local_ip(&self) -> Option<IpAddr> {
+186        let conn = self.conn.as_ref().unwrap();
+187        let inner = conn.state.lock("local_ip");
+188
+189        inner
+190            .inner
+191            .network_path(PathId::ZERO)
+192            .expect("path exists when connecting")
+193            .local_ip
+194    }
+195
+196    /// The peer's UDP addresses
+197    ///
+198    /// Will panic if called after `poll` has returned `Ready`.
+199    pub fn remote_address(&self) -> SocketAddr {
+200        let conn_ref: &ConnectionRef = self.conn.as_ref().expect("used after yielding Ready");
+201        // TODO: another unwrap
+202        conn_ref
+203            .state
+204            .lock("remote_address")
+205            .inner
+206            .network_path(PathId::ZERO)
+207            .expect("path exists when connecting")
+208            .remote
+209    }
+210}
+211
+212impl Future for Connecting {
+213    type Output = Result<Connection, ConnectionError>;
+214    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+215        Pin::new(&mut self.connected).poll(cx).map(|_| {
+216            let conn = self.conn.take().unwrap();
+217            let inner = conn.state.lock("connecting");
+218            if inner.connected {
+219                drop(inner);
+220                Ok(Connection(conn))
+221            } else {
+222                Err(inner
+223                    .error
+224                    .clone()
+225                    .expect("connected signaled without connection success or error"))
+226            }
+227        })
+228    }
+229}
+230
+231/// Future that completes when a connection is fully established
+232///
+233/// For clients, the resulting value indicates if 0-RTT was accepted. For servers, the resulting
+234/// value is meaningless.
+235pub struct ZeroRttAccepted(oneshot::Receiver<bool>);
+236
+237impl Future for ZeroRttAccepted {
+238    type Output = bool;
+239    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+240        Pin::new(&mut self.0).poll(cx).map(|x| x.unwrap_or(false))
+241    }
+242}
+243
+244/// A future that drives protocol logic for a connection
+245///
+246/// This future handles the protocol logic for a single connection, routing events from the
+247/// `Connection` API object to the `Endpoint` task and the related stream-related interfaces.
+248/// It also keeps track of outstanding timeouts for the `Connection`.
+249///
+250/// If the connection encounters an error condition, this future will yield an error. It will
+251/// terminate (yielding `Ok(())`) if the connection was closed without error. Unlike other
+252/// connection-related futures, this waits for the draining period to complete to ensure that
+253/// packets still in flight from the peer are handled gracefully.
+254#[must_use = "connection drivers must be spawned for their connections to function"]
+255#[derive(Debug)]
+256struct ConnectionDriver(ConnectionRef);
+257
+258impl Future for ConnectionDriver {
+259    type Output = Result<(), io::Error>;
+260
+261    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+262        let conn = &mut *self.0.state.lock("poll");
+263
+264        let span = debug_span!("drive", id = conn.handle.0);
+265        let _guard = span.enter();
+266
+267        if let Err(e) = conn.process_conn_events(&self.0.shared, cx) {
+268            conn.terminate(e, &self.0.shared);
+269            return Poll::Ready(Ok(()));
+270        }
+271        let mut keep_going = conn.drive_transmit(cx)?;
+272        // If a timer expires, there might be more to transmit. When we transmit something, we
+273        // might need to reset a timer. Hence, we must loop until neither happens.
+274        keep_going |= conn.drive_timer(cx);
+275        conn.forward_endpoint_events();
+276        conn.forward_app_events(&self.0.shared);
+277
+278        if !conn.inner.is_drained() {
+279            if keep_going {
+280                // If the connection hasn't processed all tasks, schedule it again
+281                cx.waker().wake_by_ref();
+282            } else {
+283                conn.driver = Some(cx.waker().clone());
+284            }
+285            return Poll::Pending;
+286        }
+287        if conn.error.is_none() {
+288            unreachable!("drained connections always have an error");
+289        }
+290        Poll::Ready(Ok(()))
+291    }
+292}
+293
+294/// A QUIC connection.
+295///
+296/// If all references to a connection (including every clone of the `Connection` handle, streams of
+297/// incoming streams, and the various stream types) have been dropped, then the connection will be
+298/// automatically closed with an `error_code` of 0 and an empty `reason`. You can also close the
+299/// connection explicitly by calling [`Connection::close()`].
+300///
+301/// Closing the connection immediately abandons efforts to deliver data to the peer.  Upon
+302/// receiving CONNECTION_CLOSE the peer *may* drop any stream data not yet delivered to the
+303/// application. [`Connection::close()`] describes in more detail how to gracefully close a
+304/// connection without losing application data.
+305///
+306/// May be cloned to obtain another handle to the same connection.
+307///
+308/// [`Connection::close()`]: Connection::close
+309#[derive(Debug, Clone)]
+310pub struct Connection(ConnectionRef);
+311
+312impl Connection {
+313    /// Returns a weak reference to the inner connection struct.
+314    pub fn weak_handle(&self) -> WeakConnectionHandle {
+315        WeakConnectionHandle(Arc::downgrade(&self.0.0))
+316    }
+317
+318    /// Initiate a new outgoing unidirectional stream.
+319    ///
+320    /// Streams are cheap and instantaneous to open unless blocked by flow control. As a
+321    /// consequence, the peer won't be notified that a stream has been opened until the stream is
+322    /// actually used.
+323    pub fn open_uni(&self) -> OpenUni<'_> {
+324        OpenUni {
+325            conn: &self.0,
+326            notify: self.0.shared.stream_budget_available[Dir::Uni as usize].notified(),
+327        }
+328    }
+329
+330    /// Initiate a new outgoing bidirectional stream.
+331    ///
+332    /// Streams are cheap and instantaneous to open unless blocked by flow control. As a
+333    /// consequence, the peer won't be notified that a stream has been opened until the stream is
+334    /// actually used. Calling [`open_bi()`] then waiting on the [`RecvStream`] without writing
+335    /// anything to [`SendStream`] will never succeed.
+336    ///
+337    /// [`open_bi()`]: crate::Connection::open_bi
+338    /// [`SendStream`]: crate::SendStream
+339    /// [`RecvStream`]: crate::RecvStream
+340    pub fn open_bi(&self) -> OpenBi<'_> {
+341        OpenBi {
+342            conn: &self.0,
+343            notify: self.0.shared.stream_budget_available[Dir::Bi as usize].notified(),
+344        }
+345    }
+346
+347    /// Accept the next incoming uni-directional stream
+348    pub fn accept_uni(&self) -> AcceptUni<'_> {
+349        AcceptUni {
+350            conn: &self.0,
+351            notify: self.0.shared.stream_incoming[Dir::Uni as usize].notified(),
+352        }
+353    }
+354
+355    /// Accept the next incoming bidirectional stream
+356    ///
+357    /// **Important Note**: The `Connection` that calls [`open_bi()`] must write to its [`SendStream`]
+358    /// before the other `Connection` is able to `accept_bi()`. Calling [`open_bi()`] then
+359    /// waiting on the [`RecvStream`] without writing anything to [`SendStream`] will never succeed.
+360    ///
+361    /// [`accept_bi()`]: crate::Connection::accept_bi
+362    /// [`open_bi()`]: crate::Connection::open_bi
+363    /// [`SendStream`]: crate::SendStream
+364    /// [`RecvStream`]: crate::RecvStream
+365    pub fn accept_bi(&self) -> AcceptBi<'_> {
+366        AcceptBi {
+367            conn: &self.0,
+368            notify: self.0.shared.stream_incoming[Dir::Bi as usize].notified(),
+369        }
+370    }
+371
+372    /// Receive an application datagram
+373    pub fn read_datagram(&self) -> ReadDatagram<'_> {
+374        ReadDatagram {
+375            conn: &self.0,
+376            notify: self.0.shared.datagram_received.notified(),
+377        }
+378    }
+379
+380    /// Opens a new path if no path exists yet for the remote address.
+381    ///
+382    /// Otherwise behaves exactly as [`open_path`].
+383    ///
+384    /// [`open_path`]: Self::open_path
+385    pub fn open_path_ensure(&self, addr: SocketAddr, initial_status: PathStatus) -> OpenPath {
+386        let mut state = self.0.state.lock("open_path");
+387
+388        // If endpoint::State::ipv6 is true we want to keep all our IP addresses as IPv6.
+389        // If not, we do not support IPv6.  We can not access endpoint::State from here
+390        // however, but either all our paths use an IPv6 address, or all our paths use an
+391        // IPv4 address.  So we can use that information.
+392        let ipv6 = state
+393            .inner
+394            .paths()
+395            .iter()
+396            .filter_map(|id| {
+397                state
+398                    .inner
+399                    .network_path(*id)
+400                    .map(|addrs| addrs.remote.is_ipv6())
+401                    .ok()
+402            })
+403            .next()
+404            .unwrap_or_default();
+405        if addr.is_ipv6() && !ipv6 {
+406            return OpenPath::rejected(PathError::InvalidRemoteAddress(addr));
+407        }
+408        let addr = if ipv6 {
+409            SocketAddr::V6(ensure_ipv6(addr))
+410        } else {
+411            addr
+412        };
+413
+414        let now = state.runtime.now();
+415        // TODO(matheus23): For now this means it's impossible to make use of short-circuiting path validation currently.
+416        // However, changing that would mean changing the API.
+417        let addrs = FourTuple {
+418            remote: addr,
+419            local_ip: None,
+420        };
+421        let open_res = state.inner.open_path_ensure(addrs, initial_status, now);
+422        state.wake();
+423        match open_res {
+424            Ok((path_id, existed)) if existed => {
+425                match state.open_path.get(&path_id).map(|tx| tx.subscribe()) {
+426                    Some(recv) => OpenPath::new(path_id, recv, self.0.clone()),
+427                    None => OpenPath::ready(path_id, self.0.clone()),
+428                }
+429            }
+430            Ok((path_id, _)) => {
+431                let (tx, rx) = watch::channel(Ok(()));
+432                state.open_path.insert(path_id, tx);
+433                drop(state);
+434                OpenPath::new(path_id, rx, self.0.clone())
+435            }
+436            Err(err) => OpenPath::rejected(err),
+437        }
+438    }
+439
+440    /// Opens an additional path if the multipath extension is negotiated.
+441    ///
+442    /// The returned future completes once the path is either fully opened and ready to
+443    /// carry application data, or if there was an error.
+444    ///
+445    /// Dropping the returned future does not cancel the opening of the path, the
+446    /// [`PathEvent::Opened`] event will still be emitted from [`Self::path_events`] if the
+447    /// path opens.  The [`PathId`] for the events can be extracted from
+448    /// [`OpenPath::path_id`].
+449    ///
+450    /// Failure to open a path can either occur immediately, before polling the returned
+451    /// future, or at a later time.  If the failure is immediate [`OpenPath::path_id`] will
+452    /// return `None` and the future will be ready immediately.  If the failure happens
+453    /// later, a [`PathEvent`] will be emitted.
+454    pub fn open_path(&self, addr: SocketAddr, initial_status: PathStatus) -> OpenPath {
+455        let mut state = self.0.state.lock("open_path");
+456
+457        // If endpoint::State::ipv6 is true we want to keep all our IP addresses as IPv6.
+458        // If not, we do not support IPv6.  We can not access endpoint::State from here
+459        // however, but either all our paths use an IPv6 address, or all our paths use an
+460        // IPv4 address.  So we can use that information.
+461        let ipv6 = state
+462            .inner
+463            .paths()
+464            .iter()
+465            .filter_map(|id| {
+466                state
+467                    .inner
+468                    .network_path(*id)
+469                    .map(|addrs| addrs.remote.is_ipv6())
+470                    .ok()
+471            })
+472            .next()
+473            .unwrap_or_default();
+474        if addr.is_ipv6() && !ipv6 {
+475            return OpenPath::rejected(PathError::InvalidRemoteAddress(addr));
+476        }
+477        let addr = if ipv6 {
+478            SocketAddr::V6(ensure_ipv6(addr))
+479        } else {
+480            addr
+481        };
+482
+483        let (on_open_path_send, on_open_path_recv) = watch::channel(Ok(()));
+484        let now = state.runtime.now();
+485        // TODO(matheus23): For now this means it's impossible to make use of short-circuiting path validation currently.
+486        // However, changing that would mean changing the API.
+487        let addrs = FourTuple {
+488            remote: addr,
+489            local_ip: None,
+490        };
+491        let open_res = state.inner.open_path(addrs, initial_status, now);
+492        state.wake();
+493        match open_res {
+494            Ok(path_id) => {
+495                state.open_path.insert(path_id, on_open_path_send);
+496                drop(state);
+497                OpenPath::new(path_id, on_open_path_recv, self.0.clone())
+498            }
+499            Err(err) => OpenPath::rejected(err),
+500        }
+501    }
+502
+503    /// Returns the [`Path`] structure of an open path
+504    pub fn path(&self, id: PathId) -> Option<Path> {
+505        // TODO(flub): Using this to know if the path still exists is... hacky.
+506        self.0.state.lock("path").inner.path_status(id).ok()?;
+507        Some(Path {
+508            id,
+509            conn: self.0.clone(),
+510        })
+511    }
+512
+513    /// A broadcast receiver of [`PathEvent`]s for all paths in this connection
+514    pub fn path_events(&self) -> tokio::sync::broadcast::Receiver<PathEvent> {
+515        self.0.state.lock("path_events").path_events.subscribe()
+516    }
+517
+518    /// A broadcast receiver of [`iroh_hp::Event`]s for updates about server addresses
+519    pub fn nat_traversal_updates(&self) -> tokio::sync::broadcast::Receiver<iroh_hp::Event> {
+520        self.0
+521            .state
+522            .lock("nat_traversal_updates")
+523            .nat_traversal_updates
+524            .subscribe()
+525    }
+526
+527    /// Wait for the connection to be closed for any reason
+528    ///
+529    /// Despite the return type's name, closed connections are often not an error condition at the
+530    /// application layer. Cases that might be routine include [`ConnectionError::LocallyClosed`]
+531    /// and [`ConnectionError::ApplicationClosed`].
+532    pub async fn closed(&self) -> ConnectionError {
+533        {
+534            let conn = self.0.state.lock("closed");
+535            if let Some(error) = conn.error.as_ref() {
+536                return error.clone();
+537            }
+538            // Construct the future while the lock is held to ensure we can't miss a wakeup if
+539            // the `Notify` is signaled immediately after we release the lock. `await` it after
+540            // the lock guard is out of scope.
+541            self.0.shared.closed.notified()
+542        }
+543        .await;
+544        self.0
+545            .state
+546            .lock("closed")
+547            .error
+548            .as_ref()
+549            .expect("closed without an error")
+550            .clone()
+551    }
+552
+553    /// Wait for the connection to be closed without keeping a strong reference to the connection
+554    ///
+555    /// Returns a future that resolves, once the connection is closed, to a tuple of
+556    /// ([`ConnectionError`], [`ConnectionStats`]).
+557    ///
+558    /// Calling [`Self::closed`] keeps the connection alive until it is either closed locally via [`Connection::close`]
+559    /// or closed by the remote peer. This function instead does not keep the connection itself alive,
+560    /// so if all *other* clones of the connection are dropped, the connection will be closed implicitly even
+561    /// if there are futures returned from this function still being awaited.
+562    pub fn on_closed(&self) -> OnClosed {
+563        let (tx, rx) = oneshot::channel();
+564        let mut state = self.0.state.lock("on_closed");
+565        if let Some(error) = &state.error {
+566            // Connection already closed, send immediately
+567            let _ = tx.send((error.clone(), state.inner.stats()));
+568        } else {
+569            state.on_closed.push(tx);
+570        }
+571        drop(state);
+572        OnClosed {
+573            conn: self.weak_handle(),
+574            rx,
+575        }
+576    }
+577
+578    /// If the connection is closed, the reason why.
+579    ///
+580    /// Returns `None` if the connection is still open.
+581    pub fn close_reason(&self) -> Option<ConnectionError> {
+582        self.0.state.lock("close_reason").error.clone()
+583    }
+584
+585    /// Close the connection immediately.
+586    ///
+587    /// Pending operations will fail immediately with [`ConnectionError::LocallyClosed`]. No
+588    /// more data is sent to the peer and the peer may drop buffered data upon receiving
+589    /// the CONNECTION_CLOSE frame.
+590    ///
+591    /// `error_code` and `reason` are not interpreted, and are provided directly to the peer.
+592    ///
+593    /// `reason` will be truncated to fit in a single packet with overhead; to improve odds that it
+594    /// is preserved in full, it should be kept under 1KiB.
+595    ///
+596    /// # Gracefully closing a connection
+597    ///
+598    /// Only the peer last receiving application data can be certain that all data is
+599    /// delivered. The only reliable action it can then take is to close the connection,
+600    /// potentially with a custom error code. The delivery of the final CONNECTION_CLOSE
+601    /// frame is very likely if both endpoints stay online long enough, and
+602    /// [`Endpoint::wait_idle()`] can be used to provide sufficient time. Otherwise, the
+603    /// remote peer will time out the connection, provided that the idle timeout is not
+604    /// disabled.
+605    ///
+606    /// The sending side can not guarantee all stream data is delivered to the remote
+607    /// application. It only knows the data is delivered to the QUIC stack of the remote
+608    /// endpoint. Once the local side sends a CONNECTION_CLOSE frame in response to calling
+609    /// [`close()`] the remote endpoint may drop any data it received but is as yet
+610    /// undelivered to the application, including data that was acknowledged as received to
+611    /// the local endpoint.
+612    ///
+613    /// [`ConnectionError::LocallyClosed`]: crate::ConnectionError::LocallyClosed
+614    /// [`Endpoint::wait_idle()`]: crate::Endpoint::wait_idle
+615    /// [`close()`]: Connection::close
+616    pub fn close(&self, error_code: VarInt, reason: &[u8]) {
+617        let conn = &mut *self.0.state.lock("close");
+618        conn.close(error_code, Bytes::copy_from_slice(reason), &self.0.shared);
+619    }
+620
+621    /// Wait for the handshake to be confirmed.
+622    ///
+623    /// As a server, who must be authenticated by clients,
+624    /// this happens when the handshake completes
+625    /// upon receiving a TLS Finished message from the client.
+626    /// In return, the server send a HANDSHAKE_DONE frame.
+627    ///
+628    /// As a client, this happens when receiving a HANDSHAKE_DONE frame.
+629    /// At this point, the server has either accepted our authentication,
+630    /// or, if client authentication is not required, accepted our lack of authentication.
+631    pub async fn handshake_confirmed(&self) -> Result<(), ConnectionError> {
+632        {
+633            let conn = self.0.state.lock("handshake_confirmed");
+634            if let Some(error) = conn.error.as_ref() {
+635                return Err(error.clone());
+636            }
+637            if conn.handshake_confirmed {
+638                return Ok(());
+639            }
+640            // Construct the future while the lock is held to ensure we can't miss a wakeup if
+641            // the `Notify` is signaled immediately after we release the lock. `await` it after
+642            // the lock guard is out of scope.
+643            self.0.shared.handshake_confirmed.notified()
+644        }
+645        .await;
+646        if let Some(error) = self.0.state.lock("handshake_confirmed").error.as_ref() {
+647            Err(error.clone())
+648        } else {
+649            Ok(())
+650        }
+651    }
+652
+653    /// Transmit `data` as an unreliable, unordered application datagram
+654    ///
+655    /// Application datagrams are a low-level primitive. They may be lost or delivered out of order,
+656    /// and `data` must both fit inside a single QUIC packet and be smaller than the maximum
+657    /// dictated by the peer.
+658    ///
+659    /// Previously queued datagrams which are still unsent may be discarded to make space for this
+660    /// datagram, in order of oldest to newest.
+661    pub fn send_datagram(&self, data: Bytes) -> Result<(), SendDatagramError> {
+662        let conn = &mut *self.0.state.lock("send_datagram");
+663        if let Some(ref x) = conn.error {
+664            return Err(SendDatagramError::ConnectionLost(x.clone()));
+665        }
+666        use proto::SendDatagramError::*;
+667        match conn.inner.datagrams().send(data, true) {
+668            Ok(()) => {
+669                conn.wake();
+670                Ok(())
+671            }
+672            Err(e) => Err(match e {
+673                Blocked(..) => unreachable!(),
+674                UnsupportedByPeer => SendDatagramError::UnsupportedByPeer,
+675                Disabled => SendDatagramError::Disabled,
+676                TooLarge => SendDatagramError::TooLarge,
+677            }),
+678        }
+679    }
+680
+681    /// Transmit `data` as an unreliable, unordered application datagram
+682    ///
+683    /// Unlike [`send_datagram()`], this method will wait for buffer space during congestion
+684    /// conditions, which effectively prioritizes old datagrams over new datagrams.
+685    ///
+686    /// See [`send_datagram()`] for details.
+687    ///
+688    /// [`send_datagram()`]: Connection::send_datagram
+689    pub fn send_datagram_wait(&self, data: Bytes) -> SendDatagram<'_> {
+690        SendDatagram {
+691            conn: &self.0,
+692            data: Some(data),
+693            notify: self.0.shared.datagrams_unblocked.notified(),
+694        }
+695    }
+696
+697    /// Compute the maximum size of datagrams that may be passed to [`send_datagram()`].
+698    ///
+699    /// Returns `None` if datagrams are unsupported by the peer or disabled locally.
+700    ///
+701    /// This may change over the lifetime of a connection according to variation in the path MTU
+702    /// estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer's
+703    /// limit is large this is guaranteed to be a little over a kilobyte at minimum.
+704    ///
+705    /// Not necessarily the maximum size of received datagrams.
+706    ///
+707    /// [`send_datagram()`]: Connection::send_datagram
+708    pub fn max_datagram_size(&self) -> Option<usize> {
+709        self.0
+710            .state
+711            .lock("max_datagram_size")
+712            .inner
+713            .datagrams()
+714            .max_size()
+715    }
+716
+717    /// Bytes available in the outgoing datagram buffer
+718    ///
+719    /// When greater than zero, calling [`send_datagram()`](Self::send_datagram) with a datagram of
+720    /// at most this size is guaranteed not to cause older datagrams to be dropped.
+721    pub fn datagram_send_buffer_space(&self) -> usize {
+722        self.0
+723            .state
+724            .lock("datagram_send_buffer_space")
+725            .inner
+726            .datagrams()
+727            .send_buffer_space()
+728    }
+729
+730    /// The side of the connection (client or server)
+731    pub fn side(&self) -> Side {
+732        self.0.state.lock("side").inner.side()
+733    }
+734
+735    /// The peer's UDP address
+736    ///
+737    /// If [`ServerConfig::migration`] is `true`, clients may change addresses at will,
+738    /// e.g. when switching to a cellular internet connection.
+739    ///
+740    /// If [`multipath`] is enabled this will return the address of *any*
+741    /// path, and may not be consistent. Prefer [`Path::remote_address`] instead.
+742    ///
+743    /// [`ServerConfig::migration`]: crate::ServerConfig::migration
+744    /// [`multipath`]: crate::TransportConfig::max_concurrent_multipath_paths
+745    pub fn remote_address(&self) -> SocketAddr {
+746        // TODO: an unwrap again
+747        let state = self.0.state.lock("remote_address");
+748        state
+749            .inner
+750            .paths()
+751            .iter()
+752            .filter_map(|id| state.inner.network_path(*id).ok())
+753            .next()
+754            .unwrap()
+755            .remote
+756    }
+757
+758    /// The local IP address which was used when the peer established
+759    /// the connection
+760    ///
+761    /// This can be different from the address the endpoint is bound to, in case
+762    /// the endpoint is bound to a wildcard address like `0.0.0.0` or `::`.
+763    ///
+764    /// This will return `None` for clients, or when the platform does not expose this
+765    /// information. See [`quinn_udp::RecvMeta::dst_ip`](udp::RecvMeta::dst_ip) for a list of
+766    /// supported platforms when using [`quinn_udp`](udp) for I/O, which is the default.
+767    pub fn local_ip(&self) -> Option<IpAddr> {
+768        // TODO: an unwrap again
+769        let state = self.0.state.lock("remote_address");
+770        state
+771            .inner
+772            .paths()
+773            .iter()
+774            .filter_map(|id| state.inner.network_path(*id).ok())
+775            .next()
+776            .unwrap()
+777            .local_ip
+778    }
+779
+780    /// Current best estimate of this connection's latency (round-trip-time)
+781    pub fn rtt(&self, path_id: PathId) -> Option<Duration> {
+782        self.0.state.lock("rtt").inner.rtt(path_id)
+783    }
+784
+785    /// Returns connection statistics
+786    pub fn stats(&self) -> ConnectionStats {
+787        self.0.state.lock("stats").inner.stats()
+788    }
+789
+790    /// Returns path statistics
+791    pub fn path_stats(&self, path_id: PathId) -> Option<PathStats> {
+792        self.0.state.lock("path_stats").inner.path_stats(path_id)
+793    }
+794
+795    /// Current state of the congestion control algorithm, for debugging purposes
+796    pub fn congestion_state(&self, path_id: PathId) -> Option<Box<dyn Controller>> {
+797        self.0
+798            .state
+799            .lock("congestion_state")
+800            .inner
+801            .congestion_state(path_id)
+802            .map(|c| c.clone_box())
+803    }
+804
+805    /// Parameters negotiated during the handshake
+806    ///
+807    /// Guaranteed to return `Some` on fully established connections or after
+808    /// [`Connecting::handshake_data()`] succeeds. See that method's documentations for details on
+809    /// the returned value.
+810    ///
+811    /// [`Connection::handshake_data()`]: crate::Connecting::handshake_data
+812    pub fn handshake_data(&self) -> Option<Box<dyn Any>> {
+813        self.0
+814            .state
+815            .lock("handshake_data")
+816            .inner
+817            .crypto_session()
+818            .handshake_data()
+819    }
+820
+821    /// Cryptographic identity of the peer
+822    ///
+823    /// The dynamic type returned is determined by the configured
+824    /// [`Session`](proto::crypto::Session). For the default `rustls` session, the return value can
+825    /// be [`downcast`](Box::downcast) to a <code>Vec<[rustls::pki_types::CertificateDer]></code>
+826    pub fn peer_identity(&self) -> Option<Box<dyn Any>> {
+827        self.0
+828            .state
+829            .lock("peer_identity")
+830            .inner
+831            .crypto_session()
+832            .peer_identity()
+833    }
+834
+835    /// A stable identifier for this connection
+836    ///
+837    /// Peer addresses and connection IDs can change, but this value will remain
+838    /// fixed for the lifetime of the connection.
+839    pub fn stable_id(&self) -> usize {
+840        self.0.stable_id()
+841    }
+842
+843    /// Update traffic keys spontaneously
+844    ///
+845    /// This primarily exists for testing purposes.
+846    pub fn force_key_update(&self) {
+847        self.0
+848            .state
+849            .lock("force_key_update")
+850            .inner
+851            .force_key_update()
+852    }
+853
+854    /// Derive keying material from this connection's TLS session secrets.
+855    ///
+856    /// When both peers call this method with the same `label` and `context`
+857    /// arguments and `output` buffers of equal length, they will get the
+858    /// same sequence of bytes in `output`. These bytes are cryptographically
+859    /// strong and pseudorandom, and are suitable for use as keying material.
+860    ///
+861    /// See [RFC5705](https://tools.ietf.org/html/rfc5705) for more information.
+862    pub fn export_keying_material(
+863        &self,
+864        output: &mut [u8],
+865        label: &[u8],
+866        context: &[u8],
+867    ) -> Result<(), proto::crypto::ExportKeyingMaterialError> {
+868        self.0
+869            .state
+870            .lock("export_keying_material")
+871            .inner
+872            .crypto_session()
+873            .export_keying_material(output, label, context)
+874    }
+875
+876    /// Modify the number of remotely initiated unidirectional streams that may be concurrently open
+877    ///
+878    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+879    /// `count`s increase both minimum and worst-case memory consumption.
+880    pub fn set_max_concurrent_uni_streams(&self, count: VarInt) {
+881        let mut conn = self.0.state.lock("set_max_concurrent_uni_streams");
+882        conn.inner.set_max_concurrent_streams(Dir::Uni, count);
+883        // May need to send MAX_STREAMS to make progress
+884        conn.wake();
+885    }
+886
+887    /// See [`proto::TransportConfig::send_window()`]
+888    pub fn set_send_window(&self, send_window: u64) {
+889        let mut conn = self.0.state.lock("set_send_window");
+890        conn.inner.set_send_window(send_window);
+891        conn.wake();
+892    }
+893
+894    /// See [`proto::TransportConfig::receive_window()`]
+895    pub fn set_receive_window(&self, receive_window: VarInt) {
+896        let mut conn = self.0.state.lock("set_receive_window");
+897        conn.inner.set_receive_window(receive_window);
+898        conn.wake();
+899    }
+900
+901    /// Modify the number of remotely initiated bidirectional streams that may be concurrently open
+902    ///
+903    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+904    /// `count`s increase both minimum and worst-case memory consumption.
+905    pub fn set_max_concurrent_bi_streams(&self, count: VarInt) {
+906        let mut conn = self.0.state.lock("set_max_concurrent_bi_streams");
+907        conn.inner.set_max_concurrent_streams(Dir::Bi, count);
+908        // May need to send MAX_STREAMS to make progress
+909        conn.wake();
+910    }
+911
+912    /// Track changed on our external address as reported by the peer.
+913    pub fn observed_external_addr(&self) -> watch::Receiver<Option<SocketAddr>> {
+914        let conn = self.0.state.lock("external_addr");
+915        conn.observed_external_addr.subscribe()
+916    }
+917
+918    /// Is multipath enabled?
+919    // TODO(flub): not a useful API, once we do real things with multipath we can remove
+920    // this again.
+921    pub fn is_multipath_enabled(&self) -> bool {
+922        let conn = self.0.state.lock("is_multipath_enabled");
+923        conn.inner.is_multipath_negotiated()
+924    }
+925
+926    /// Registers one address at which this endpoint might be reachable
+927    ///
+928    /// When the NAT traversal extension is negotiated, servers send these addresses to clients in
+929    /// `ADD_ADDRESS` frames. This allows clients to obtain server address candidates to initiate
+930    /// NAT traversal attempts. Clients provide their own reachable addresses in `REACH_OUT` frames
+931    /// when [`Self::initiate_nat_traversal_round`] is called.
+932    pub fn add_nat_traversal_address(&self, address: SocketAddr) -> Result<(), iroh_hp::Error> {
+933        let mut conn = self.0.state.lock("add_nat_traversal_addresses");
+934        conn.inner.add_nat_traversal_address(address)
+935    }
+936
+937    /// Removes one or more addresses from the set of addresses at which this endpoint is reachable
+938    ///
+939    /// When the NAT traversal extension is negotiated, servers send address removals to
+940    /// clients in `REMOVE_ADDRESS` frames. This allows clients to stop using outdated
+941    /// server address candidates that are no longer valid for NAT traversal.
+942    ///
+943    /// For clients, removed addresses will no longer be advertised in `REACH_OUT` frames.
+944    ///
+945    /// Addresses not present in the set will be silently ignored.
+946    pub fn remove_nat_traversal_address(&self, address: SocketAddr) -> Result<(), iroh_hp::Error> {
+947        let mut conn = self.0.state.lock("remove_nat_traversal_addresses");
+948        conn.inner.remove_nat_traversal_address(address)
+949    }
+950
+951    /// Get the current local nat traversal addresses
+952    pub fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+953        let conn = self.0.state.lock("get_local_nat_traversal_addresses");
+954        conn.inner.get_local_nat_traversal_addresses()
+955    }
+956
+957    /// Get the currently advertised nat traversal addresses by the server
+958    pub fn get_remote_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+959        let conn = self.0.state.lock("get_remote_nat_traversal_addresses");
+960        conn.inner.get_remote_nat_traversal_addresses()
+961    }
+962
+963    /// Initiates a new nat traversal round
+964    ///
+965    /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT`
+966    /// frames, and initiating probing of the known remote addresses. When a new round is
+967    /// initiated, the previous one is cancelled, and paths that have not been opened are closed.
+968    ///
+969    /// Returns the server addresses that are now being probed.
+970    pub fn initiate_nat_traversal_round(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+971        let mut conn = self.0.state.lock("initiate_nat_traversal_round");
+972        let now = conn.runtime.now();
+973        conn.inner.initiate_nat_traversal_round(now)
+974    }
+975}
+976
+977pin_project! {
+978    /// Future produced by [`Connection::open_uni`]
+979    pub struct OpenUni<'a> {
+980        conn: &'a ConnectionRef,
+981        #[pin]
+982        notify: Notified<'a>,
+983    }
+984}
+985
+986impl Future for OpenUni<'_> {
+987    type Output = Result<SendStream, ConnectionError>;
+988    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+989        let this = self.project();
+990        let (conn, id, is_0rtt) = ready!(poll_open(ctx, this.conn, this.notify, Dir::Uni))?;
+991        Poll::Ready(Ok(SendStream::new(conn, id, is_0rtt)))
+992    }
+993}
+994
+995pin_project! {
+996    /// Future produced by [`Connection::open_bi`]
+997    pub struct OpenBi<'a> {
+998        conn: &'a ConnectionRef,
+999        #[pin]
+1000        notify: Notified<'a>,
+1001    }
+1002}
+1003
+1004impl Future for OpenBi<'_> {
+1005    type Output = Result<(SendStream, RecvStream), ConnectionError>;
+1006    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1007        let this = self.project();
+1008        let (conn, id, is_0rtt) = ready!(poll_open(ctx, this.conn, this.notify, Dir::Bi))?;
+1009
+1010        Poll::Ready(Ok((
+1011            SendStream::new(conn.clone(), id, is_0rtt),
+1012            RecvStream::new(conn, id, is_0rtt),
+1013        )))
+1014    }
+1015}
+1016
+1017fn poll_open<'a>(
+1018    ctx: &mut Context<'_>,
+1019    conn: &'a ConnectionRef,
+1020    mut notify: Pin<&mut Notified<'a>>,
+1021    dir: Dir,
+1022) -> Poll<Result<(ConnectionRef, StreamId, bool), ConnectionError>> {
+1023    let mut state = conn.state.lock("poll_open");
+1024    if let Some(ref e) = state.error {
+1025        return Poll::Ready(Err(e.clone()));
+1026    } else if let Some(id) = state.inner.streams().open(dir) {
+1027        let is_0rtt = state.inner.side().is_client() && state.inner.is_handshaking();
+1028        drop(state); // Release the lock so clone can take it
+1029        return Poll::Ready(Ok((conn.clone(), id, is_0rtt)));
+1030    }
+1031    loop {
+1032        match notify.as_mut().poll(ctx) {
+1033            // `state` lock ensures we didn't race with readiness
+1034            Poll::Pending => return Poll::Pending,
+1035            // Spurious wakeup, get a new future
+1036            Poll::Ready(()) => {
+1037                notify.set(conn.shared.stream_budget_available[dir as usize].notified())
+1038            }
+1039        }
+1040    }
+1041}
+1042
+1043pin_project! {
+1044    /// Future produced by [`Connection::accept_uni`]
+1045    pub struct AcceptUni<'a> {
+1046        conn: &'a ConnectionRef,
+1047        #[pin]
+1048        notify: Notified<'a>,
+1049    }
+1050}
+1051
+1052impl Future for AcceptUni<'_> {
+1053    type Output = Result<RecvStream, ConnectionError>;
+1054
+1055    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1056        let this = self.project();
+1057        let (conn, id, is_0rtt) = ready!(poll_accept(ctx, this.conn, this.notify, Dir::Uni))?;
+1058        Poll::Ready(Ok(RecvStream::new(conn, id, is_0rtt)))
+1059    }
+1060}
+1061
+1062pin_project! {
+1063    /// Future produced by [`Connection::accept_bi`]
+1064    pub struct AcceptBi<'a> {
+1065        conn: &'a ConnectionRef,
+1066        #[pin]
+1067        notify: Notified<'a>,
+1068    }
+1069}
+1070
+1071impl Future for AcceptBi<'_> {
+1072    type Output = Result<(SendStream, RecvStream), ConnectionError>;
+1073
+1074    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1075        let this = self.project();
+1076        let (conn, id, is_0rtt) = ready!(poll_accept(ctx, this.conn, this.notify, Dir::Bi))?;
+1077        Poll::Ready(Ok((
+1078            SendStream::new(conn.clone(), id, is_0rtt),
+1079            RecvStream::new(conn, id, is_0rtt),
+1080        )))
+1081    }
+1082}
+1083
+1084fn poll_accept<'a>(
+1085    ctx: &mut Context<'_>,
+1086    conn: &'a ConnectionRef,
+1087    mut notify: Pin<&mut Notified<'a>>,
+1088    dir: Dir,
+1089) -> Poll<Result<(ConnectionRef, StreamId, bool), ConnectionError>> {
+1090    let mut state = conn.state.lock("poll_accept");
+1091    // Check for incoming streams before checking `state.error` so that already-received streams,
+1092    // which are necessarily finite, can be drained from a closed connection.
+1093    if let Some(id) = state.inner.streams().accept(dir) {
+1094        let is_0rtt = state.inner.is_handshaking();
+1095        state.wake(); // To send additional stream ID credit
+1096        drop(state); // Release the lock so clone can take it
+1097        return Poll::Ready(Ok((conn.clone(), id, is_0rtt)));
+1098    } else if let Some(ref e) = state.error {
+1099        return Poll::Ready(Err(e.clone()));
+1100    }
+1101    loop {
+1102        match notify.as_mut().poll(ctx) {
+1103            // `state` lock ensures we didn't race with readiness
+1104            Poll::Pending => return Poll::Pending,
+1105            // Spurious wakeup, get a new future
+1106            Poll::Ready(()) => notify.set(conn.shared.stream_incoming[dir as usize].notified()),
+1107        }
+1108    }
+1109}
+1110
+1111pin_project! {
+1112    /// Future produced by [`Connection::read_datagram`]
+1113    pub struct ReadDatagram<'a> {
+1114        conn: &'a ConnectionRef,
+1115        #[pin]
+1116        notify: Notified<'a>,
+1117    }
+1118}
+1119
+1120impl Future for ReadDatagram<'_> {
+1121    type Output = Result<Bytes, ConnectionError>;
+1122    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1123        let mut this = self.project();
+1124        let mut state = this.conn.state.lock("ReadDatagram::poll");
+1125        // Check for buffered datagrams before checking `state.error` so that already-received
+1126        // datagrams, which are necessarily finite, can be drained from a closed connection.
+1127        if let Some(x) = state.inner.datagrams().recv() {
+1128            return Poll::Ready(Ok(x));
+1129        } else if let Some(ref e) = state.error {
+1130            return Poll::Ready(Err(e.clone()));
+1131        }
+1132        loop {
+1133            match this.notify.as_mut().poll(ctx) {
+1134                // `state` lock ensures we didn't race with readiness
+1135                Poll::Pending => return Poll::Pending,
+1136                // Spurious wakeup, get a new future
+1137                Poll::Ready(()) => this
+1138                    .notify
+1139                    .set(this.conn.shared.datagram_received.notified()),
+1140            }
+1141        }
+1142    }
+1143}
+1144
+1145pin_project! {
+1146    /// Future produced by [`Connection::send_datagram_wait`]
+1147    pub struct SendDatagram<'a> {
+1148        conn: &'a ConnectionRef,
+1149        data: Option<Bytes>,
+1150        #[pin]
+1151        notify: Notified<'a>,
+1152    }
+1153}
+1154
+1155impl Future for SendDatagram<'_> {
+1156    type Output = Result<(), SendDatagramError>;
+1157    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1158        let mut this = self.project();
+1159        let mut state = this.conn.state.lock("SendDatagram::poll");
+1160        if let Some(ref e) = state.error {
+1161            return Poll::Ready(Err(SendDatagramError::ConnectionLost(e.clone())));
+1162        }
+1163        use proto::SendDatagramError::*;
+1164        match state
+1165            .inner
+1166            .datagrams()
+1167            .send(this.data.take().unwrap(), false)
+1168        {
+1169            Ok(()) => {
+1170                state.wake();
+1171                Poll::Ready(Ok(()))
+1172            }
+1173            Err(e) => Poll::Ready(Err(match e {
+1174                Blocked(data) => {
+1175                    this.data.replace(data);
+1176                    loop {
+1177                        match this.notify.as_mut().poll(ctx) {
+1178                            Poll::Pending => return Poll::Pending,
+1179                            // Spurious wakeup, get a new future
+1180                            Poll::Ready(()) => this
+1181                                .notify
+1182                                .set(this.conn.shared.datagrams_unblocked.notified()),
+1183                        }
+1184                    }
+1185                }
+1186                UnsupportedByPeer => SendDatagramError::UnsupportedByPeer,
+1187                Disabled => SendDatagramError::Disabled,
+1188                TooLarge => SendDatagramError::TooLarge,
+1189            })),
+1190        }
+1191    }
+1192}
+1193
+1194/// Future returned by [`Connection::on_closed`]
+1195///
+1196/// Resolves to a tuple of ([`ConnectionError`], [`ConnectionStats`]).
+1197pub struct OnClosed {
+1198    rx: oneshot::Receiver<(ConnectionError, ConnectionStats)>,
+1199    conn: WeakConnectionHandle,
+1200}
+1201
+1202impl Drop for OnClosed {
+1203    fn drop(&mut self) {
+1204        if self.rx.is_terminated() {
+1205            return;
+1206        };
+1207        if let Some(conn) = self.conn.upgrade() {
+1208            self.rx.close();
+1209            conn.0
+1210                .state
+1211                .lock("OnClosed::drop")
+1212                .on_closed
+1213                .retain(|tx| !tx.is_closed());
+1214        }
+1215    }
+1216}
+1217
+1218impl Future for OnClosed {
+1219    type Output = (ConnectionError, ConnectionStats);
+1220
+1221    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+1222        let this = self.get_mut();
+1223        // The `expect` is safe because `State::drop` ensures that all senders are triggered
+1224        // before being dropped.
+1225        Pin::new(&mut this.rx)
+1226            .poll(cx)
+1227            .map(|x| x.expect("on_close sender is never dropped before sending"))
+1228    }
+1229}
+1230
+1231#[derive(Debug)]
+1232#[allow(clippy::redundant_allocation)]
+1233pub(crate) struct ConnectionRef(Arc<Arc<ConnectionInner>>);
+1234
+1235impl ConnectionRef {
+1236    #[allow(clippy::redundant_allocation)]
+1237    fn from_arc(inner: Arc<Arc<ConnectionInner>>) -> Self {
+1238        inner.state.lock("from_arc").ref_count += 1;
+1239        Self(inner)
+1240    }
+1241
+1242    fn stable_id(&self) -> usize {
+1243        &*self.0 as *const _ as usize
+1244    }
+1245}
+1246
+1247impl Clone for ConnectionRef {
+1248    fn clone(&self) -> Self {
+1249        Self::from_arc(Arc::clone(&self.0))
+1250    }
+1251}
+1252
+1253impl Drop for ConnectionRef {
+1254    fn drop(&mut self) {
+1255        let conn = &mut *self.state.lock("drop");
+1256        if let Some(x) = conn.ref_count.checked_sub(1) {
+1257            conn.ref_count = x;
+1258            if x == 0 && !conn.inner.is_closed() {
+1259                // If the driver is alive, it's just it and us, so we'd better shut it down. If it's
+1260                // not, we can't do any harm. If there were any streams being opened, then either
+1261                // the connection will be closed for an unrelated reason or a fresh reference will
+1262                // be constructed for the newly opened stream.
+1263                conn.implicit_close(&self.shared);
+1264            }
+1265        }
+1266    }
+1267}
+1268
+1269impl std::ops::Deref for ConnectionRef {
+1270    type Target = ConnectionInner;
+1271    fn deref(&self) -> &Self::Target {
+1272        &self.0
+1273    }
+1274}
+1275
+1276#[derive(Debug)]
+1277pub(crate) struct ConnectionInner {
+1278    pub(crate) state: Mutex<State>,
+1279    pub(crate) shared: Shared,
+1280}
+1281
+1282/// A handle to some connection internals, use with care.
+1283///
+1284/// This contains a weak reference to the connection so will not itself keep the connection
+1285/// alive.
+1286#[derive(Debug, Clone)]
+1287pub struct WeakConnectionHandle(Weak<Arc<ConnectionInner>>);
+1288
+1289impl WeakConnectionHandle {
+1290    /// Returns `true` if the [`Connection`] associated with this handle is still alive.
+1291    pub fn is_alive(&self) -> bool {
+1292        self.0.upgrade().is_some()
+1293    }
+1294
+1295    /// Upgrade the handle to a full `Connection`
+1296    pub fn upgrade(&self) -> Option<Connection> {
+1297        self.0
+1298            .upgrade()
+1299            .map(|inner| Connection(ConnectionRef::from_arc(inner)))
+1300    }
+1301}
+1302
+1303#[derive(Debug, Default)]
+1304pub(crate) struct Shared {
+1305    handshake_confirmed: Notify,
+1306    /// Notified when new streams may be locally initiated due to an increase in stream ID flow
+1307    /// control budget
+1308    stream_budget_available: [Notify; 2],
+1309    /// Notified when the peer has initiated a new stream
+1310    stream_incoming: [Notify; 2],
+1311    datagram_received: Notify,
+1312    datagrams_unblocked: Notify,
+1313    closed: Notify,
+1314}
+1315
+1316pub(crate) struct State {
+1317    pub(crate) inner: proto::Connection,
+1318    driver: Option<Waker>,
+1319    handle: ConnectionHandle,
+1320    on_handshake_data: Option<oneshot::Sender<()>>,
+1321    on_connected: Option<oneshot::Sender<bool>>,
+1322    connected: bool,
+1323    handshake_confirmed: bool,
+1324    timer: Option<Pin<Box<dyn AsyncTimer>>>,
+1325    timer_deadline: Option<Instant>,
+1326    conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+1327    endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+1328    pub(crate) blocked_writers: FxHashMap<StreamId, Waker>,
+1329    pub(crate) blocked_readers: FxHashMap<StreamId, Waker>,
+1330    pub(crate) stopped: FxHashMap<StreamId, Arc<Notify>>,
+1331    /// Always set to Some before the connection becomes drained
+1332    pub(crate) error: Option<ConnectionError>,
+1333    /// Tracks paths being opened
+1334    open_path: FxHashMap<PathId, watch::Sender<Result<(), PathError>>>,
+1335    /// Tracks paths being closed
+1336    pub(crate) close_path: FxHashMap<PathId, oneshot::Sender<VarInt>>,
+1337    pub(crate) path_events: tokio::sync::broadcast::Sender<PathEvent>,
+1338    /// Number of live handles that can be used to initiate or handle I/O; excludes the driver
+1339    ref_count: usize,
+1340    sender: Pin<Box<dyn UdpSender>>,
+1341    pub(crate) runtime: Arc<dyn Runtime>,
+1342    send_buffer: Vec<u8>,
+1343    /// We buffer a transmit when the underlying I/O would block
+1344    buffered_transmit: Option<proto::Transmit>,
+1345    /// Our last external address reported by the peer. When multipath is enabled, this will be the
+1346    /// last report across all paths.
+1347    pub(crate) observed_external_addr: watch::Sender<Option<SocketAddr>>,
+1348    pub(crate) nat_traversal_updates: tokio::sync::broadcast::Sender<iroh_hp::Event>,
+1349    on_closed: Vec<oneshot::Sender<(ConnectionError, ConnectionStats)>>,
+1350}
+1351
+1352impl State {
+1353    #[allow(clippy::too_many_arguments)]
+1354    fn new(
+1355        inner: proto::Connection,
+1356        handle: ConnectionHandle,
+1357        endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+1358        conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+1359        on_handshake_data: oneshot::Sender<()>,
+1360        on_connected: oneshot::Sender<bool>,
+1361        sender: Pin<Box<dyn UdpSender>>,
+1362        runtime: Arc<dyn Runtime>,
+1363    ) -> Self {
+1364        Self {
+1365            inner,
+1366            driver: None,
+1367            handle,
+1368            on_handshake_data: Some(on_handshake_data),
+1369            on_connected: Some(on_connected),
+1370            connected: false,
+1371            handshake_confirmed: false,
+1372            timer: None,
+1373            timer_deadline: None,
+1374            conn_events,
+1375            endpoint_events,
+1376            blocked_writers: FxHashMap::default(),
+1377            blocked_readers: FxHashMap::default(),
+1378            stopped: FxHashMap::default(),
+1379            open_path: FxHashMap::default(),
+1380            close_path: FxHashMap::default(),
+1381            error: None,
+1382            ref_count: 0,
+1383            sender,
+1384            runtime,
+1385            send_buffer: Vec::new(),
+1386            buffered_transmit: None,
+1387            path_events: tokio::sync::broadcast::channel(32).0,
+1388            observed_external_addr: watch::Sender::new(None),
+1389            nat_traversal_updates: tokio::sync::broadcast::channel(32).0,
+1390            on_closed: Vec::new(),
+1391        }
+1392    }
+1393
+1394    fn drive_transmit(&mut self, cx: &mut Context<'_>) -> io::Result<bool> {
+1395        let now = self.runtime.now();
+1396        let mut transmits = 0;
+1397
+1398        let max_datagrams = self
+1399            .sender
+1400            .max_transmit_segments()
+1401            .min(MAX_TRANSMIT_SEGMENTS);
+1402
+1403        loop {
+1404            // Retry the last transmit, or get a new one.
+1405            let t = match self.buffered_transmit.take() {
+1406                Some(t) => t,
+1407                None => {
+1408                    self.send_buffer.clear();
+1409                    match self
+1410                        .inner
+1411                        .poll_transmit(now, max_datagrams, &mut self.send_buffer)
+1412                    {
+1413                        Some(t) => {
+1414                            transmits += match t.segment_size {
+1415                                None => 1,
+1416                                Some(s) => t.size.div_ceil(s), // round up
+1417                            };
+1418                            t
+1419                        }
+1420                        None => break,
+1421                    }
+1422                }
+1423            };
+1424
+1425            let len = t.size;
+1426            match self
+1427                .sender
+1428                .as_mut()
+1429                .poll_send(&udp_transmit(&t, &self.send_buffer[..len]), cx)
+1430            {
+1431                Poll::Pending => {
+1432                    self.buffered_transmit = Some(t);
+1433                    return Ok(false);
+1434                }
+1435                Poll::Ready(Err(e)) => return Err(e),
+1436                Poll::Ready(Ok(())) => {}
+1437            }
+1438
+1439            if transmits >= MAX_TRANSMIT_DATAGRAMS {
+1440                // TODO: What isn't ideal here yet is that if we don't poll all
+1441                // datagrams that could be sent we don't go into the `app_limited`
+1442                // state and CWND continues to grow until we get here the next time.
+1443                // See https://github.com/quinn-rs/quinn/issues/1126
+1444                return Ok(true);
+1445            }
+1446        }
+1447
+1448        Ok(false)
+1449    }
+1450
+1451    fn forward_endpoint_events(&mut self) {
+1452        while let Some(event) = self.inner.poll_endpoint_events() {
+1453            // If the endpoint driver is gone, noop.
+1454            let _ = self.endpoint_events.send((self.handle, event));
+1455        }
+1456    }
+1457
+1458    /// If this returns `Err`, the endpoint is dead, so the driver should exit immediately.
+1459    fn process_conn_events(
+1460        &mut self,
+1461        shared: &Shared,
+1462        cx: &mut Context<'_>,
+1463    ) -> Result<(), ConnectionError> {
+1464        loop {
+1465            match self.conn_events.poll_recv(cx) {
+1466                Poll::Ready(Some(ConnectionEvent::Rebind(sender))) => {
+1467                    self.sender = sender;
+1468                    self.inner.local_address_changed();
+1469                }
+1470                Poll::Ready(Some(ConnectionEvent::Proto(event))) => {
+1471                    self.inner.handle_event(event);
+1472                }
+1473                Poll::Ready(Some(ConnectionEvent::Close { reason, error_code })) => {
+1474                    self.close(error_code, reason, shared);
+1475                }
+1476                Poll::Ready(None) => {
+1477                    return Err(ConnectionError::TransportError(TransportError::new(
+1478                        TransportErrorCode::INTERNAL_ERROR,
+1479                        "endpoint driver future was dropped".to_string(),
+1480                    )));
+1481                }
+1482                Poll::Pending => {
+1483                    return Ok(());
+1484                }
+1485            }
+1486        }
+1487    }
+1488
+1489    fn forward_app_events(&mut self, shared: &Shared) {
+1490        while let Some(event) = self.inner.poll() {
+1491            use proto::Event::*;
+1492            match event {
+1493                HandshakeDataReady => {
+1494                    if let Some(x) = self.on_handshake_data.take() {
+1495                        let _ = x.send(());
+1496                    }
+1497                }
+1498                Connected => {
+1499                    self.connected = true;
+1500                    if let Some(x) = self.on_connected.take() {
+1501                        // We don't care if the on-connected future was dropped
+1502                        let _ = x.send(self.inner.accepted_0rtt());
+1503                    }
+1504                    if self.inner.side().is_client() && !self.inner.accepted_0rtt() {
+1505                        // Wake up rejected 0-RTT streams so they can fail immediately with
+1506                        // `ZeroRttRejected` errors.
+1507                        wake_all(&mut self.blocked_writers);
+1508                        wake_all(&mut self.blocked_readers);
+1509                        wake_all_notify(&mut self.stopped);
+1510                    }
+1511                }
+1512                HandshakeConfirmed => {
+1513                    self.handshake_confirmed = true;
+1514                    shared.handshake_confirmed.notify_waiters();
+1515                }
+1516                ConnectionLost { reason } => {
+1517                    self.terminate(reason, shared);
+1518                }
+1519                Stream(StreamEvent::Writable { id }) => wake_stream(id, &mut self.blocked_writers),
+1520                Stream(StreamEvent::Opened { dir: Dir::Uni }) => {
+1521                    shared.stream_incoming[Dir::Uni as usize].notify_waiters();
+1522                }
+1523                Stream(StreamEvent::Opened { dir: Dir::Bi }) => {
+1524                    shared.stream_incoming[Dir::Bi as usize].notify_waiters();
+1525                }
+1526                DatagramReceived => {
+1527                    shared.datagram_received.notify_waiters();
+1528                }
+1529                DatagramsUnblocked => {
+1530                    shared.datagrams_unblocked.notify_waiters();
+1531                }
+1532                Stream(StreamEvent::Readable { id }) => wake_stream(id, &mut self.blocked_readers),
+1533                Stream(StreamEvent::Available { dir }) => {
+1534                    // Might mean any number of streams are ready, so we wake up everyone
+1535                    shared.stream_budget_available[dir as usize].notify_waiters();
+1536                }
+1537                Stream(StreamEvent::Finished { id }) => wake_stream_notify(id, &mut self.stopped),
+1538                Stream(StreamEvent::Stopped { id, .. }) => {
+1539                    wake_stream_notify(id, &mut self.stopped);
+1540                    wake_stream(id, &mut self.blocked_writers);
+1541                }
+1542                Path(ref evt @ PathEvent::ObservedAddr { addr: observed, .. }) => {
+1543                    self.path_events.send(evt.clone()).ok();
+1544                    self.observed_external_addr.send_if_modified(|addr| {
+1545                        let old = addr.replace(observed);
+1546                        old != *addr
+1547                    });
+1548                }
+1549                Path(ref evt @ PathEvent::Opened { id }) => {
+1550                    self.path_events.send(evt.clone()).ok();
+1551                    if let Some(sender) = self.open_path.remove(&id) {
+1552                        sender.send_modify(|value| *value = Ok(()));
+1553                    }
+1554                }
+1555                Path(ref evt @ PathEvent::Closed { id, error_code }) => {
+1556                    self.path_events.send(evt.clone()).ok();
+1557                    if let Some(sender) = self.close_path.remove(&id) {
+1558                        let _ = sender.send(error_code);
+1559                    }
+1560                }
+1561                Path(evt @ PathEvent::Abandoned { .. }) => {
+1562                    self.path_events.send(evt).ok();
+1563                }
+1564                Path(ref evt @ PathEvent::LocallyClosed { id, error }) => {
+1565                    self.path_events.send(evt.clone()).ok();
+1566                    if let Some(sender) = self.open_path.remove(&id) {
+1567                        sender.send_modify(|value| *value = Err(error));
+1568                    }
+1569                    // this will happen also for already opened paths
+1570                }
+1571                Path(evt @ PathEvent::RemoteStatus { .. }) => {
+1572                    self.path_events.send(evt).ok();
+1573                }
+1574                NatTraversal(update) => {
+1575                    self.nat_traversal_updates.send(update).ok();
+1576                }
+1577            }
+1578        }
+1579    }
+1580
+1581    fn drive_timer(&mut self, cx: &mut Context<'_>) -> bool {
+1582        // Check whether we need to (re)set the timer. If so, we must poll again to ensure the
+1583        // timer is registered with the runtime (and check whether it's already
+1584        // expired).
+1585        match self.inner.poll_timeout() {
+1586            Some(deadline) => {
+1587                if let Some(delay) = &mut self.timer {
+1588                    // There is no need to reset the tokio timer if the deadline
+1589                    // did not change
+1590                    if self
+1591                        .timer_deadline
+1592                        .map(|current_deadline| current_deadline != deadline)
+1593                        .unwrap_or(true)
+1594                    {
+1595                        delay.as_mut().reset(deadline);
+1596                    }
+1597                } else {
+1598                    self.timer = Some(self.runtime.new_timer(deadline));
+1599                }
+1600                // Store the actual expiration time of the timer
+1601                self.timer_deadline = Some(deadline);
+1602            }
+1603            None => {
+1604                self.timer_deadline = None;
+1605                return false;
+1606            }
+1607        }
+1608
+1609        if self.timer_deadline.is_none() {
+1610            return false;
+1611        }
+1612
+1613        let delay = self
+1614            .timer
+1615            .as_mut()
+1616            .expect("timer must exist in this state")
+1617            .as_mut();
+1618        if delay.poll(cx).is_pending() {
+1619            // Since there wasn't a timeout event, there is nothing new
+1620            // for the connection to do
+1621            return false;
+1622        }
+1623
+1624        // A timer expired, so the caller needs to check for
+1625        // new transmits, which might cause new timers to be set.
+1626        self.inner.handle_timeout(self.runtime.now());
+1627        self.timer_deadline = None;
+1628        true
+1629    }
+1630
+1631    /// Wake up a blocked `Driver` task to process I/O
+1632    pub(crate) fn wake(&mut self) {
+1633        if let Some(x) = self.driver.take() {
+1634            x.wake();
+1635        }
+1636    }
+1637
+1638    /// Used to wake up all blocked futures when the connection becomes closed for any reason
+1639    fn terminate(&mut self, reason: ConnectionError, shared: &Shared) {
+1640        self.error = Some(reason.clone());
+1641        if let Some(x) = self.on_handshake_data.take() {
+1642            let _ = x.send(());
+1643        }
+1644        wake_all(&mut self.blocked_writers);
+1645        wake_all(&mut self.blocked_readers);
+1646        shared.stream_budget_available[Dir::Uni as usize].notify_waiters();
+1647        shared.stream_budget_available[Dir::Bi as usize].notify_waiters();
+1648        shared.stream_incoming[Dir::Uni as usize].notify_waiters();
+1649        shared.stream_incoming[Dir::Bi as usize].notify_waiters();
+1650        shared.datagram_received.notify_waiters();
+1651        shared.datagrams_unblocked.notify_waiters();
+1652        if let Some(x) = self.on_connected.take() {
+1653            let _ = x.send(false);
+1654        }
+1655        shared.handshake_confirmed.notify_waiters();
+1656        wake_all_notify(&mut self.stopped);
+1657        shared.closed.notify_waiters();
+1658
+1659        // Send to the registered on_closed futures.
+1660        let stats = self.inner.stats();
+1661        for tx in self.on_closed.drain(..) {
+1662            tx.send((reason.clone(), stats.clone())).ok();
+1663        }
+1664    }
+1665
+1666    fn close(&mut self, error_code: VarInt, reason: Bytes, shared: &Shared) {
+1667        self.inner.close(self.runtime.now(), error_code, reason);
+1668        self.terminate(ConnectionError::LocallyClosed, shared);
+1669        self.wake();
+1670    }
+1671
+1672    /// Close for a reason other than the application's explicit request
+1673    pub(crate) fn implicit_close(&mut self, shared: &Shared) {
+1674        self.close(0u32.into(), Bytes::new(), shared);
+1675    }
+1676
+1677    pub(crate) fn check_0rtt(&self) -> Result<(), ()> {
+1678        if self.inner.is_handshaking()
+1679            || self.inner.accepted_0rtt()
+1680            || self.inner.side().is_server()
+1681        {
+1682            Ok(())
+1683        } else {
+1684            Err(())
+1685        }
+1686    }
+1687}
+1688
+1689impl Drop for State {
+1690    fn drop(&mut self) {
+1691        if !self.inner.is_drained() {
+1692            // Ensure the endpoint can tidy up
+1693            let _ = self
+1694                .endpoint_events
+1695                .send((self.handle, proto::EndpointEvent::drained()));
+1696        }
+1697
+1698        if !self.on_closed.is_empty() {
+1699            // Ensure that all on_closed oneshot senders are triggered before dropping.
+1700            let reason = self.error.as_ref().expect("closed without error reason");
+1701            let stats = self.inner.stats();
+1702            for tx in self.on_closed.drain(..) {
+1703                tx.send((reason.clone(), stats.clone())).ok();
+1704            }
+1705        }
+1706    }
+1707}
+1708
+1709impl fmt::Debug for State {
+1710    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+1711        f.debug_struct("State").field("inner", &self.inner).finish()
+1712    }
+1713}
+1714
+1715fn wake_stream(stream_id: StreamId, wakers: &mut FxHashMap<StreamId, Waker>) {
+1716    if let Some(waker) = wakers.remove(&stream_id) {
+1717        waker.wake();
+1718    }
+1719}
+1720
+1721fn wake_all(wakers: &mut FxHashMap<StreamId, Waker>) {
+1722    wakers.drain().for_each(|(_, waker)| waker.wake())
+1723}
+1724
+1725fn wake_stream_notify(stream_id: StreamId, wakers: &mut FxHashMap<StreamId, Arc<Notify>>) {
+1726    if let Some(notify) = wakers.remove(&stream_id) {
+1727        notify.notify_waiters()
+1728    }
+1729}
+1730
+1731fn wake_all_notify(wakers: &mut FxHashMap<StreamId, Arc<Notify>>) {
+1732    wakers
+1733        .drain()
+1734        .for_each(|(_, notify)| notify.notify_waiters())
+1735}
+1736
+1737/// Errors that can arise when sending a datagram
+1738#[derive(Debug, Error, Clone, Eq, PartialEq)]
+1739pub enum SendDatagramError {
+1740    /// The peer does not support receiving datagram frames
+1741    #[error("datagrams not supported by peer")]
+1742    UnsupportedByPeer,
+1743    /// Datagram support is disabled locally
+1744    #[error("datagram support disabled")]
+1745    Disabled,
+1746    /// The datagram is larger than the connection can currently accommodate
+1747    ///
+1748    /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been
+1749    /// exceeded.
+1750    #[error("datagram too large")]
+1751    TooLarge,
+1752    /// The connection was lost
+1753    #[error("connection lost")]
+1754    ConnectionLost(#[from] ConnectionError),
+1755}
+1756
+1757/// The maximum amount of datagrams which will be produced in a single `drive_transmit` call
+1758///
+1759/// This limits the amount of CPU resources consumed by datagram generation,
+1760/// and allows other tasks (like receiving ACKs) to run in between.
+1761const MAX_TRANSMIT_DATAGRAMS: usize = 20;
+1762
+1763/// The maximum amount of datagrams that are sent in a single transmit
+1764///
+1765/// This can be lower than the maximum platform capabilities, to avoid excessive
+1766/// memory allocations when calling `poll_transmit()`. Benchmarks have shown
+1767/// that numbers around 10 are a good compromise.
+1768const MAX_TRANSMIT_SEGMENTS: NonZeroUsize = NonZeroUsize::new(10).expect("known");
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn/endpoint.rs.html b/pr/395/docs/src/iroh_quinn/endpoint.rs.html new file mode 100644 index 000000000..389033714 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn/endpoint.rs.html @@ -0,0 +1,933 @@ +endpoint.rs - source

iroh_quinn/
endpoint.rs

1use std::{
+2    collections::VecDeque,
+3    fmt,
+4    future::Future,
+5    io::{self, IoSliceMut},
+6    mem,
+7    net::{SocketAddr, SocketAddrV6},
+8    num::NonZeroUsize,
+9    pin::Pin,
+10    str,
+11    sync::{Arc, Mutex},
+12    task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
+13};
+14
+15#[cfg(all(
+16    not(wasm_browser),
+17    any(feature = "runtime-tokio", feature = "runtime-smol"),
+18    any(feature = "aws-lc-rs", feature = "ring"),
+19))]
+20use crate::runtime::default_runtime;
+21use crate::{
+22    Instant,
+23    runtime::{AsyncUdpSocket, Runtime, UdpSender},
+24    udp_transmit,
+25};
+26use bytes::{Bytes, BytesMut};
+27use pin_project_lite::pin_project;
+28use proto::{
+29    self as proto, ClientConfig, ConnectError, ConnectionError, ConnectionHandle, DatagramEvent,
+30    EndpointEvent, FourTuple, ServerConfig,
+31};
+32use rustc_hash::FxHashMap;
+33#[cfg(all(
+34    not(wasm_browser),
+35    any(feature = "runtime-tokio", feature = "runtime-smol"),
+36    any(feature = "aws-lc-rs", feature = "ring"),
+37))]
+38use socket2::{Domain, Protocol, Socket, Type};
+39use tokio::sync::{Notify, futures::Notified, mpsc};
+40use tracing::{Instrument, Span};
+41use udp::{BATCH_SIZE, RecvMeta};
+42
+43use crate::{
+44    ConnectionEvent, EndpointConfig, IO_LOOP_BOUND, RECV_TIME_BOUND, VarInt,
+45    connection::Connecting, incoming::Incoming, work_limiter::WorkLimiter,
+46};
+47
+48/// A QUIC endpoint.
+49///
+50/// An endpoint corresponds to a single UDP socket, may host many connections, and may act as both
+51/// client and server for different connections.
+52///
+53/// May be cloned to obtain another handle to the same endpoint.
+54#[derive(Debug, Clone)]
+55pub struct Endpoint {
+56    pub(crate) inner: EndpointRef,
+57    runtime: Arc<dyn Runtime>,
+58}
+59
+60impl Endpoint {
+61    /// Helper to construct an endpoint for use with outgoing connections only
+62    ///
+63    /// Note that `addr` is the *local* address to bind to, which should usually be a wildcard
+64    /// address like `0.0.0.0:0` or `[::]:0`, which allow communication with any reachable IPv4 or
+65    /// IPv6 address respectively from an OS-assigned port.
+66    ///
+67    /// If an IPv6 address is provided, attempts to make the socket dual-stack so as to allow
+68    /// communication with both IPv4 and IPv6 addresses. As such, calling `Endpoint::client` with
+69    /// the address `[::]:0` is a reasonable default to maximize the ability to connect to other
+70    /// address. For example:
+71    ///
+72    /// ```
+73    /// iroh_quinn::Endpoint::client((std::net::Ipv6Addr::UNSPECIFIED, 0).into());
+74    /// ```
+75    ///
+76    /// Some environments may not allow creation of dual-stack sockets, in which case an IPv6
+77    /// client will only be able to connect to IPv6 servers. An IPv4 client is never dual-stack.
+78    #[cfg(all(
+79        not(wasm_browser),
+80        any(feature = "runtime-tokio", feature = "runtime-smol"),
+81        any(feature = "aws-lc-rs", feature = "ring"), // `EndpointConfig::default()` is only available with these
+82    ))]
+83    pub fn client(addr: SocketAddr) -> io::Result<Self> {
+84        let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))?;
+85        if addr.is_ipv6()
+86            && let Err(e) = socket.set_only_v6(false)
+87        {
+88            tracing::debug!(%e, "unable to make socket dual-stack");
+89        }
+90        socket.bind(&addr.into())?;
+91        let runtime =
+92            default_runtime().ok_or_else(|| io::Error::other("no async runtime found"))?;
+93        Self::new_with_abstract_socket(
+94            EndpointConfig::default(),
+95            None,
+96            runtime.wrap_udp_socket(socket.into())?,
+97            runtime,
+98        )
+99    }
+100
+101    /// Returns relevant stats from this Endpoint
+102    pub fn stats(&self) -> EndpointStats {
+103        self.inner.state.lock().unwrap().stats
+104    }
+105
+106    /// Helper to construct an endpoint for use with both incoming and outgoing connections
+107    ///
+108    /// Platform defaults for dual-stack sockets vary. For example, any socket bound to a wildcard
+109    /// IPv6 address on Windows will not by default be able to communicate with IPv4
+110    /// addresses. Portable applications should bind an address that matches the family they wish to
+111    /// communicate within.
+112    #[cfg(all(
+113        not(wasm_browser),
+114        any(feature = "runtime-tokio", feature = "runtime-smol"),
+115        any(feature = "aws-lc-rs", feature = "ring"), // `EndpointConfig::default()` is only available with these
+116    ))]
+117    pub fn server(config: ServerConfig, addr: SocketAddr) -> io::Result<Self> {
+118        let socket = std::net::UdpSocket::bind(addr)?;
+119        let runtime =
+120            default_runtime().ok_or_else(|| io::Error::other("no async runtime found"))?;
+121        Self::new_with_abstract_socket(
+122            EndpointConfig::default(),
+123            Some(config),
+124            runtime.wrap_udp_socket(socket)?,
+125            runtime,
+126        )
+127    }
+128
+129    /// Construct an endpoint with arbitrary configuration and socket
+130    #[cfg(not(wasm_browser))]
+131    pub fn new(
+132        config: EndpointConfig,
+133        server_config: Option<ServerConfig>,
+134        socket: std::net::UdpSocket,
+135        runtime: Arc<dyn Runtime>,
+136    ) -> io::Result<Self> {
+137        let socket = runtime.wrap_udp_socket(socket)?;
+138        Self::new_with_abstract_socket(config, server_config, socket, runtime)
+139    }
+140
+141    /// Construct an endpoint with arbitrary configuration and pre-constructed abstract socket
+142    ///
+143    /// Useful when `socket` has additional state (e.g. sidechannels) attached for which shared
+144    /// ownership is needed.
+145    pub fn new_with_abstract_socket(
+146        config: EndpointConfig,
+147        server_config: Option<ServerConfig>,
+148        socket: Box<dyn AsyncUdpSocket>,
+149        runtime: Arc<dyn Runtime>,
+150    ) -> io::Result<Self> {
+151        let addr = socket.local_addr()?;
+152        let allow_mtud = !socket.may_fragment();
+153        let rc = EndpointRef::new(
+154            socket,
+155            proto::Endpoint::new(Arc::new(config), server_config.map(Arc::new), allow_mtud),
+156            addr.is_ipv6(),
+157            runtime.clone(),
+158        );
+159        let driver = EndpointDriver(rc.clone());
+160        runtime.spawn(Box::pin(
+161            async {
+162                if let Err(e) = driver.await {
+163                    tracing::error!("I/O error: {}", e);
+164                }
+165            }
+166            .instrument(Span::current()),
+167        ));
+168        Ok(Self { inner: rc, runtime })
+169    }
+170
+171    /// Get the next incoming connection attempt from a client
+172    ///
+173    /// Yields [`Incoming`]s, or `None` if the endpoint is [`close`](Self::close)d. [`Incoming`]
+174    /// can be `await`ed to obtain the final [`Connection`](crate::Connection), or used to e.g.
+175    /// filter connection attempts or force address validation, or converted into an intermediate
+176    /// `Connecting` future which can be used to e.g. send 0.5-RTT data.
+177    pub fn accept(&self) -> Accept<'_> {
+178        Accept {
+179            endpoint: self,
+180            notify: self.inner.shared.incoming.notified(),
+181        }
+182    }
+183
+184    /// Set the client configuration used by `connect`
+185    pub fn set_default_client_config(&self, config: ClientConfig) {
+186        self.inner.0.state.lock().unwrap().default_client_config = Some(config);
+187    }
+188
+189    /// Connect to a remote endpoint
+190    ///
+191    /// `server_name` must be covered by the certificate presented by the server. This prevents a
+192    /// connection from being intercepted by an attacker with a valid certificate for some other
+193    /// server.
+194    ///
+195    /// May fail immediately due to configuration errors, or in the future if the connection could
+196    /// not be established.
+197    pub fn connect(&self, addr: SocketAddr, server_name: &str) -> Result<Connecting, ConnectError> {
+198        let Some(config) = self
+199            .inner
+200            .0
+201            .state
+202            .lock()
+203            .unwrap()
+204            .default_client_config
+205            .clone()
+206        else {
+207            return Err(ConnectError::NoDefaultClientConfig);
+208        };
+209
+210        self.connect_with(config, addr, server_name)
+211    }
+212
+213    /// Connect to a remote endpoint using a custom configuration.
+214    ///
+215    /// See [`connect()`] for details.
+216    ///
+217    /// [`connect()`]: Endpoint::connect
+218    pub fn connect_with(
+219        &self,
+220        config: ClientConfig,
+221        addr: SocketAddr,
+222        server_name: &str,
+223    ) -> Result<Connecting, ConnectError> {
+224        let mut endpoint = self.inner.state.lock().unwrap();
+225        if endpoint.driver_lost || endpoint.recv_state.connections.close.is_some() {
+226            return Err(ConnectError::EndpointStopping);
+227        }
+228        if addr.is_ipv6() && !endpoint.ipv6 {
+229            return Err(ConnectError::InvalidRemoteAddress(addr));
+230        }
+231        let addr = if endpoint.ipv6 {
+232            SocketAddr::V6(ensure_ipv6(addr))
+233        } else {
+234            addr
+235        };
+236
+237        let (ch, conn) = endpoint
+238            .inner
+239            .connect(self.runtime.now(), config, addr, server_name)?;
+240
+241        let sender = endpoint.socket.create_sender();
+242        endpoint.stats.outgoing_handshakes += 1;
+243        Ok(endpoint
+244            .recv_state
+245            .connections
+246            .insert(ch, conn, sender, self.runtime.clone()))
+247    }
+248
+249    /// Switch to a new UDP socket
+250    ///
+251    /// See [`Endpoint::rebind_abstract()`] for details.
+252    #[cfg(not(wasm_browser))]
+253    pub fn rebind(&self, socket: std::net::UdpSocket) -> io::Result<()> {
+254        self.rebind_abstract(self.runtime.wrap_udp_socket(socket)?)
+255    }
+256
+257    /// Switch to a new UDP socket
+258    ///
+259    /// Allows the endpoint's address to be updated live, affecting all active connections. Incoming
+260    /// connections and connections to servers unreachable from the new address will be lost.
+261    ///
+262    /// On error, the old UDP socket is retained.
+263    pub fn rebind_abstract(&self, socket: Box<dyn AsyncUdpSocket>) -> io::Result<()> {
+264        let addr = socket.local_addr()?;
+265        let mut inner = self.inner.state.lock().unwrap();
+266        inner.prev_socket = Some(mem::replace(&mut inner.socket, socket));
+267        inner.ipv6 = addr.is_ipv6();
+268
+269        // Update connection socket references
+270        for sender in inner.recv_state.connections.senders.values() {
+271            // Ignoring errors from dropped connections
+272            let _ = sender.send(ConnectionEvent::Rebind(inner.socket.create_sender()));
+273        }
+274        if let Some(driver) = inner.driver.take() {
+275            // Ensure the driver can register for wake-ups from the new socket
+276            driver.wake();
+277        }
+278
+279        Ok(())
+280    }
+281
+282    /// Replace the server configuration, affecting new incoming connections only
+283    ///
+284    /// Useful for e.g. refreshing TLS certificates without disrupting existing connections.
+285    pub fn set_server_config(&self, server_config: Option<ServerConfig>) {
+286        self.inner
+287            .state
+288            .lock()
+289            .unwrap()
+290            .inner
+291            .set_server_config(server_config.map(Arc::new))
+292    }
+293
+294    /// Get the local `SocketAddr` the underlying socket is bound to
+295    pub fn local_addr(&self) -> io::Result<SocketAddr> {
+296        self.inner.state.lock().unwrap().socket.local_addr()
+297    }
+298
+299    /// Get the number of connections that are currently open
+300    pub fn open_connections(&self) -> usize {
+301        self.inner.state.lock().unwrap().inner.open_connections()
+302    }
+303
+304    /// Close all of this endpoint's connections immediately and cease accepting new connections.
+305    ///
+306    /// See [`Connection::close()`] for details.
+307    ///
+308    /// [`Connection::close()`]: crate::Connection::close
+309    pub fn close(&self, error_code: VarInt, reason: &[u8]) {
+310        let reason = Bytes::copy_from_slice(reason);
+311        let mut endpoint = self.inner.state.lock().unwrap();
+312        endpoint.recv_state.connections.close = Some((error_code, reason.clone()));
+313        for sender in endpoint.recv_state.connections.senders.values() {
+314            // Ignoring errors from dropped connections
+315            let _ = sender.send(ConnectionEvent::Close {
+316                error_code,
+317                reason: reason.clone(),
+318            });
+319        }
+320        self.inner.shared.incoming.notify_waiters();
+321    }
+322
+323    /// Wait for all connections on the endpoint to be cleanly shut down
+324    ///
+325    /// Waiting for this condition before exiting ensures that a good-faith effort is made to notify
+326    /// peers of recent connection closes, whereas exiting immediately could force them to wait out
+327    /// the idle timeout period.
+328    ///
+329    /// Does not proactively close existing connections or cause incoming connections to be
+330    /// rejected. Consider calling [`close()`] if that is desired.
+331    ///
+332    /// [`close()`]: Endpoint::close
+333    pub async fn wait_idle(&self) {
+334        loop {
+335            {
+336                let endpoint = &mut *self.inner.state.lock().unwrap();
+337                if endpoint.recv_state.connections.is_empty() {
+338                    break;
+339                }
+340                // Construct future while lock is held to avoid race
+341                self.inner.shared.idle.notified()
+342            }
+343            .await;
+344        }
+345    }
+346}
+347
+348/// Statistics on [Endpoint] activity
+349#[non_exhaustive]
+350#[derive(Debug, Default, Copy, Clone)]
+351pub struct EndpointStats {
+352    /// Cumulative number of Quic handshakes accepted by this [Endpoint]
+353    pub accepted_handshakes: u64,
+354    /// Cumulative number of Quic handshakes sent from this [Endpoint]
+355    pub outgoing_handshakes: u64,
+356    /// Cumulative number of Quic handshakes refused on this [Endpoint]
+357    pub refused_handshakes: u64,
+358    /// Cumulative number of Quic handshakes ignored on this [Endpoint]
+359    pub ignored_handshakes: u64,
+360}
+361
+362/// A future that drives IO on an endpoint
+363///
+364/// This task functions as the switch point between the UDP socket object and the
+365/// `Endpoint` responsible for routing datagrams to their owning `Connection`.
+366/// In order to do so, it also facilitates the exchange of different types of events
+367/// flowing between the `Endpoint` and the tasks managing `Connection`s. As such,
+368/// running this task is necessary to keep the endpoint's connections running.
+369///
+370/// `EndpointDriver` futures terminate when all clones of the `Endpoint` have been dropped, or when
+371/// an I/O error occurs.
+372#[must_use = "endpoint drivers must be spawned for I/O to occur"]
+373#[derive(Debug)]
+374pub(crate) struct EndpointDriver(pub(crate) EndpointRef);
+375
+376impl Future for EndpointDriver {
+377    type Output = Result<(), io::Error>;
+378
+379    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+380        let mut endpoint = self.0.state.lock().unwrap();
+381        if endpoint.driver.is_none() {
+382            endpoint.driver = Some(cx.waker().clone());
+383        }
+384
+385        let now = endpoint.runtime.now();
+386        let mut keep_going = false;
+387        keep_going |= endpoint.drive_recv(cx, now)?;
+388        keep_going |= endpoint.handle_events(cx, &self.0.shared);
+389
+390        if !endpoint.recv_state.incoming.is_empty() {
+391            self.0.shared.incoming.notify_waiters();
+392        }
+393
+394        if endpoint.ref_count == 0 && endpoint.recv_state.connections.is_empty() {
+395            Poll::Ready(Ok(()))
+396        } else {
+397            drop(endpoint);
+398            // If there is more work to do schedule the endpoint task again.
+399            // `wake_by_ref()` is called outside the lock to minimize
+400            // lock contention on a multithreaded runtime.
+401            if keep_going {
+402                cx.waker().wake_by_ref();
+403            }
+404            Poll::Pending
+405        }
+406    }
+407}
+408
+409impl Drop for EndpointDriver {
+410    fn drop(&mut self) {
+411        let mut endpoint = self.0.state.lock().unwrap();
+412        endpoint.driver_lost = true;
+413        self.0.shared.incoming.notify_waiters();
+414        // Drop all outgoing channels, signaling the termination of the endpoint to the associated
+415        // connections.
+416        endpoint.recv_state.connections.senders.clear();
+417    }
+418}
+419
+420#[derive(Debug)]
+421pub(crate) struct EndpointInner {
+422    pub(crate) state: Mutex<State>,
+423    pub(crate) shared: Shared,
+424}
+425
+426impl EndpointInner {
+427    pub(crate) fn accept(
+428        &self,
+429        incoming: proto::Incoming,
+430        server_config: Option<Arc<ServerConfig>>,
+431    ) -> Result<Connecting, ConnectionError> {
+432        let mut state = self.state.lock().unwrap();
+433        let mut response_buffer = Vec::new();
+434        let now = state.runtime.now();
+435        match state
+436            .inner
+437            .accept(incoming, now, &mut response_buffer, server_config)
+438        {
+439            Ok((handle, conn)) => {
+440                state.stats.accepted_handshakes += 1;
+441                let sender = state.socket.create_sender();
+442                let runtime = state.runtime.clone();
+443                Ok(state
+444                    .recv_state
+445                    .connections
+446                    .insert(handle, conn, sender, runtime))
+447            }
+448            Err(error) => {
+449                if let Some(transmit) = error.response {
+450                    respond(transmit, &response_buffer, &mut state.sender);
+451                }
+452                Err(error.cause)
+453            }
+454        }
+455    }
+456
+457    pub(crate) fn refuse(&self, incoming: proto::Incoming) {
+458        let mut state = self.state.lock().unwrap();
+459        state.stats.refused_handshakes += 1;
+460        let mut response_buffer = Vec::new();
+461        let transmit = state.inner.refuse(incoming, &mut response_buffer);
+462        respond(transmit, &response_buffer, &mut state.sender);
+463    }
+464
+465    pub(crate) fn retry(&self, incoming: proto::Incoming) -> Result<(), proto::RetryError> {
+466        let mut state = self.state.lock().unwrap();
+467        let mut response_buffer = Vec::new();
+468        let transmit = state.inner.retry(incoming, &mut response_buffer)?;
+469        respond(transmit, &response_buffer, &mut state.sender);
+470        Ok(())
+471    }
+472
+473    pub(crate) fn ignore(&self, incoming: proto::Incoming) {
+474        let mut state = self.state.lock().unwrap();
+475        state.stats.ignored_handshakes += 1;
+476        state.inner.ignore(incoming);
+477    }
+478}
+479
+480#[derive(Debug)]
+481pub(crate) struct State {
+482    socket: Box<dyn AsyncUdpSocket>,
+483    sender: Pin<Box<dyn UdpSender>>,
+484    /// During an active migration, abandoned_socket receives traffic
+485    /// until the first packet arrives on the new socket.
+486    prev_socket: Option<Box<dyn AsyncUdpSocket>>,
+487    inner: proto::Endpoint,
+488    recv_state: RecvState,
+489    driver: Option<Waker>,
+490    ipv6: bool,
+491    events: mpsc::UnboundedReceiver<(ConnectionHandle, EndpointEvent)>,
+492    /// Number of live handles that can be used to initiate or handle I/O; excludes the driver
+493    ref_count: usize,
+494    driver_lost: bool,
+495    runtime: Arc<dyn Runtime>,
+496    stats: EndpointStats,
+497    default_client_config: Option<ClientConfig>,
+498}
+499
+500#[derive(Debug)]
+501pub(crate) struct Shared {
+502    incoming: Notify,
+503    idle: Notify,
+504}
+505
+506impl State {
+507    fn drive_recv(&mut self, cx: &mut Context<'_>, now: Instant) -> Result<bool, io::Error> {
+508        let get_time = || self.runtime.now();
+509        self.recv_state.recv_limiter.start_cycle(get_time);
+510        if let Some(socket) = &mut self.prev_socket {
+511            // We don't care about the `PollProgress` from old sockets.
+512            let poll_res = self.recv_state.poll_socket(
+513                cx,
+514                &mut self.inner,
+515                &mut **socket,
+516                &mut self.sender,
+517                &*self.runtime,
+518                now,
+519            );
+520            if poll_res.is_err() {
+521                self.prev_socket = None;
+522            }
+523        };
+524        let poll_res = self.recv_state.poll_socket(
+525            cx,
+526            &mut self.inner,
+527            &mut *self.socket,
+528            &mut self.sender,
+529            &*self.runtime,
+530            now,
+531        );
+532        self.recv_state.recv_limiter.finish_cycle(get_time);
+533        let poll_res = poll_res?;
+534        if poll_res.received_connection_packet {
+535            // Traffic has arrived on self.socket, therefore there is no need for the abandoned
+536            // one anymore. TODO: Account for multiple outgoing connections.
+537            self.prev_socket = None;
+538        }
+539        Ok(poll_res.keep_going)
+540    }
+541
+542    fn handle_events(&mut self, cx: &mut Context<'_>, shared: &Shared) -> bool {
+543        for _ in 0..IO_LOOP_BOUND {
+544            let (ch, event) = match self.events.poll_recv(cx) {
+545                Poll::Ready(Some(x)) => x,
+546                Poll::Ready(None) => unreachable!("EndpointInner owns one sender"),
+547                Poll::Pending => {
+548                    return false;
+549                }
+550            };
+551
+552            if event.is_drained() {
+553                self.recv_state.connections.senders.remove(&ch);
+554                if self.recv_state.connections.is_empty() {
+555                    shared.idle.notify_waiters();
+556                }
+557            }
+558            let Some(event) = self.inner.handle_event(ch, event) else {
+559                continue;
+560            };
+561            // Ignoring errors from dropped connections that haven't yet been cleaned up
+562            let _ = self
+563                .recv_state
+564                .connections
+565                .senders
+566                .get_mut(&ch)
+567                .unwrap()
+568                .send(ConnectionEvent::Proto(event));
+569        }
+570
+571        true
+572    }
+573}
+574
+575impl Drop for State {
+576    fn drop(&mut self) {
+577        for incoming in self.recv_state.incoming.drain(..) {
+578            self.inner.ignore(incoming);
+579        }
+580    }
+581}
+582
+583fn respond(
+584    transmit: proto::Transmit,
+585    response_buffer: &[u8],
+586    sender: &mut Pin<Box<dyn UdpSender>>,
+587) {
+588    // Send if there's kernel buffer space; otherwise, drop it
+589    //
+590    // As an endpoint-generated packet, we know this is an
+591    // immediate, stateless response to an unconnected peer,
+592    // one of:
+593    //
+594    // - A version negotiation response due to an unknown version
+595    // - A `CLOSE` due to a malformed or unwanted connection attempt
+596    // - A stateless reset due to an unrecognized connection
+597    // - A `Retry` packet due to a connection attempt when
+598    //   `use_retry` is set
+599    //
+600    // In each case, a well-behaved peer can be trusted to retry a
+601    // few times, which is guaranteed to produce the same response
+602    // from us. Repeated failures might at worst cause a peer's new
+603    // connection attempt to time out, which is acceptable if we're
+604    // under such heavy load that there's never room for this code
+605    // to transmit. This is morally equivalent to the packet getting
+606    // lost due to congestion further along the link, which
+607    // similarly relies on peer retries for recovery.
+608
+609    // Copied from rust 1.85's std::task::Waker::noop() implementation for backwards compatibility
+610    const NOOP: RawWaker = {
+611        const VTABLE: RawWakerVTable = RawWakerVTable::new(
+612            // Cloning just returns a new no-op raw waker
+613            |_| NOOP,
+614            // `wake` does nothing
+615            |_| {},
+616            // `wake_by_ref` does nothing
+617            |_| {},
+618            // Dropping does nothing as we don't allocate anything
+619            |_| {},
+620        );
+621        RawWaker::new(std::ptr::null(), &VTABLE)
+622    };
+623    // SAFETY: Copied from rust stdlib, the NOOP waker is thread-safe and doesn't violate the RawWakerVTable contract,
+624    // it doesn't access the data pointer at all.
+625    let waker = unsafe { Waker::from_raw(NOOP) };
+626    let mut cx = Context::from_waker(&waker);
+627    _ = sender.as_mut().poll_send(
+628        &udp_transmit(&transmit, &response_buffer[..transmit.size]),
+629        &mut cx,
+630    );
+631}
+632
+633#[inline]
+634fn proto_ecn(ecn: udp::EcnCodepoint) -> proto::EcnCodepoint {
+635    match ecn {
+636        udp::EcnCodepoint::Ect0 => proto::EcnCodepoint::Ect0,
+637        udp::EcnCodepoint::Ect1 => proto::EcnCodepoint::Ect1,
+638        udp::EcnCodepoint::Ce => proto::EcnCodepoint::Ce,
+639    }
+640}
+641
+642#[derive(Debug)]
+643struct ConnectionSet {
+644    /// Senders for communicating with the endpoint's connections
+645    senders: FxHashMap<ConnectionHandle, mpsc::UnboundedSender<ConnectionEvent>>,
+646    /// Stored to give out clones to new ConnectionInners
+647    sender: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+648    /// Set if the endpoint has been manually closed
+649    close: Option<(VarInt, Bytes)>,
+650}
+651
+652impl ConnectionSet {
+653    fn insert(
+654        &mut self,
+655        handle: ConnectionHandle,
+656        conn: proto::Connection,
+657        sender: Pin<Box<dyn UdpSender>>,
+658        runtime: Arc<dyn Runtime>,
+659    ) -> Connecting {
+660        let (send, recv) = mpsc::unbounded_channel();
+661        if let Some((error_code, ref reason)) = self.close {
+662            send.send(ConnectionEvent::Close {
+663                error_code,
+664                reason: reason.clone(),
+665            })
+666            .unwrap();
+667        }
+668        self.senders.insert(handle, send);
+669        Connecting::new(handle, conn, self.sender.clone(), recv, sender, runtime)
+670    }
+671
+672    fn is_empty(&self) -> bool {
+673        self.senders.is_empty()
+674    }
+675}
+676
+677pub(crate) fn ensure_ipv6(x: SocketAddr) -> SocketAddrV6 {
+678    match x {
+679        SocketAddr::V6(x) => x,
+680        SocketAddr::V4(x) => SocketAddrV6::new(x.ip().to_ipv6_mapped(), x.port(), 0, 0),
+681    }
+682}
+683
+684pin_project! {
+685    /// Future produced by [`Endpoint::accept`]
+686    pub struct Accept<'a> {
+687        endpoint: &'a Endpoint,
+688        #[pin]
+689        notify: Notified<'a>,
+690    }
+691}
+692
+693impl Future for Accept<'_> {
+694    type Output = Option<Incoming>;
+695    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+696        let mut this = self.project();
+697        let mut endpoint = this.endpoint.inner.state.lock().unwrap();
+698        if endpoint.driver_lost {
+699            return Poll::Ready(None);
+700        }
+701        if let Some(incoming) = endpoint.recv_state.incoming.pop_front() {
+702            // Release the mutex lock on endpoint so cloning it doesn't deadlock
+703            drop(endpoint);
+704            let incoming = Incoming::new(incoming, this.endpoint.inner.clone());
+705            return Poll::Ready(Some(incoming));
+706        }
+707        if endpoint.recv_state.connections.close.is_some() {
+708            return Poll::Ready(None);
+709        }
+710        loop {
+711            match this.notify.as_mut().poll(ctx) {
+712                // `state` lock ensures we didn't race with readiness
+713                Poll::Pending => return Poll::Pending,
+714                // Spurious wakeup, get a new future
+715                Poll::Ready(()) => this
+716                    .notify
+717                    .set(this.endpoint.inner.shared.incoming.notified()),
+718            }
+719        }
+720    }
+721}
+722
+723#[derive(Debug)]
+724pub(crate) struct EndpointRef(Arc<EndpointInner>);
+725
+726impl EndpointRef {
+727    pub(crate) fn new(
+728        socket: Box<dyn AsyncUdpSocket>,
+729        inner: proto::Endpoint,
+730        ipv6: bool,
+731        runtime: Arc<dyn Runtime>,
+732    ) -> Self {
+733        let (sender, events) = mpsc::unbounded_channel();
+734        let recv_state = RecvState::new(sender, socket.max_receive_segments(), &inner);
+735        let sender = socket.create_sender();
+736        Self(Arc::new(EndpointInner {
+737            shared: Shared {
+738                incoming: Notify::new(),
+739                idle: Notify::new(),
+740            },
+741            state: Mutex::new(State {
+742                socket,
+743                sender,
+744                prev_socket: None,
+745                inner,
+746                ipv6,
+747                events,
+748                driver: None,
+749                ref_count: 0,
+750                driver_lost: false,
+751                recv_state,
+752                runtime,
+753                stats: EndpointStats::default(),
+754                default_client_config: None,
+755            }),
+756        }))
+757    }
+758}
+759
+760impl Clone for EndpointRef {
+761    fn clone(&self) -> Self {
+762        self.0.state.lock().unwrap().ref_count += 1;
+763        Self(self.0.clone())
+764    }
+765}
+766
+767impl Drop for EndpointRef {
+768    fn drop(&mut self) {
+769        let endpoint = &mut *self.0.state.lock().unwrap();
+770        if let Some(x) = endpoint.ref_count.checked_sub(1) {
+771            endpoint.ref_count = x;
+772            if x == 0 {
+773                // If the driver is about to be on its own, ensure it can shut down if the last
+774                // connection is gone.
+775                if let Some(task) = endpoint.driver.take() {
+776                    task.wake();
+777                }
+778            }
+779        }
+780    }
+781}
+782
+783impl std::ops::Deref for EndpointRef {
+784    type Target = EndpointInner;
+785    fn deref(&self) -> &Self::Target {
+786        &self.0
+787    }
+788}
+789
+790/// State directly involved in handling incoming packets
+791struct RecvState {
+792    incoming: VecDeque<proto::Incoming>,
+793    connections: ConnectionSet,
+794    recv_buf: Box<[u8]>,
+795    recv_limiter: WorkLimiter,
+796}
+797
+798impl RecvState {
+799    fn new(
+800        sender: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+801        max_receive_segments: NonZeroUsize,
+802        endpoint: &proto::Endpoint,
+803    ) -> Self {
+804        let recv_buf = vec![
+805            0;
+806            endpoint.config().get_max_udp_payload_size().min(64 * 1024) as usize
+807                * max_receive_segments.get()
+808                * BATCH_SIZE
+809        ];
+810        Self {
+811            connections: ConnectionSet {
+812                senders: FxHashMap::default(),
+813                sender,
+814                close: None,
+815            },
+816            incoming: VecDeque::new(),
+817            recv_buf: recv_buf.into(),
+818            recv_limiter: WorkLimiter::new(RECV_TIME_BOUND),
+819        }
+820    }
+821
+822    fn poll_socket(
+823        &mut self,
+824        cx: &mut Context<'_>,
+825        endpoint: &mut proto::Endpoint,
+826        socket: &mut dyn AsyncUdpSocket,
+827        sender: &mut Pin<Box<dyn UdpSender>>,
+828        runtime: &dyn Runtime,
+829        now: Instant,
+830    ) -> Result<PollProgress, io::Error> {
+831        let mut received_connection_packet = false;
+832        let mut metas = [RecvMeta::default(); BATCH_SIZE];
+833        let mut iovs: [IoSliceMut<'_>; BATCH_SIZE] = {
+834            let mut bufs = self
+835                .recv_buf
+836                .chunks_mut(self.recv_buf.len() / BATCH_SIZE)
+837                .map(IoSliceMut::new);
+838
+839            // expect() safe as self.recv_buf is chunked into BATCH_SIZE items
+840            // and iovs will be of size BATCH_SIZE, thus from_fn is called
+841            // exactly BATCH_SIZE times.
+842            std::array::from_fn(|_| bufs.next().expect("BATCH_SIZE elements"))
+843        };
+844        loop {
+845            match socket.poll_recv(cx, &mut iovs, &mut metas) {
+846                Poll::Ready(Ok(msgs)) => {
+847                    self.recv_limiter.record_work(msgs);
+848                    for (meta, buf) in metas.iter().zip(iovs.iter()).take(msgs) {
+849                        let mut data: BytesMut = buf[0..meta.len].into();
+850                        while !data.is_empty() {
+851                            let buf = data.split_to(meta.stride.min(data.len()));
+852                            let mut response_buffer = Vec::new();
+853                            let addresses = FourTuple {
+854                                remote: meta.addr,
+855                                local_ip: meta.dst_ip,
+856                            };
+857                            match endpoint.handle(
+858                                now,
+859                                addresses,
+860                                meta.ecn.map(proto_ecn),
+861                                buf,
+862                                &mut response_buffer,
+863                            ) {
+864                                Some(DatagramEvent::NewConnection(incoming)) => {
+865                                    if self.connections.close.is_none() {
+866                                        self.incoming.push_back(incoming);
+867                                    } else {
+868                                        let transmit =
+869                                            endpoint.refuse(incoming, &mut response_buffer);
+870                                        respond(transmit, &response_buffer, sender);
+871                                    }
+872                                }
+873                                Some(DatagramEvent::ConnectionEvent(handle, event)) => {
+874                                    // Ignoring errors from dropped connections that haven't yet been cleaned up
+875                                    received_connection_packet = true;
+876                                    let _ = self
+877                                        .connections
+878                                        .senders
+879                                        .get_mut(&handle)
+880                                        .unwrap()
+881                                        .send(ConnectionEvent::Proto(event));
+882                                }
+883                                Some(DatagramEvent::Response(transmit)) => {
+884                                    respond(transmit, &response_buffer, sender);
+885                                }
+886                                None => {}
+887                            }
+888                        }
+889                    }
+890                }
+891                Poll::Pending => {
+892                    return Ok(PollProgress {
+893                        received_connection_packet,
+894                        keep_going: false,
+895                    });
+896                }
+897                // Ignore ECONNRESET as it's undefined in QUIC and may be injected by an
+898                // attacker
+899                Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::ConnectionReset => {
+900                    continue;
+901                }
+902                Poll::Ready(Err(e)) => {
+903                    return Err(e);
+904                }
+905            }
+906            if !self.recv_limiter.allow_work(|| runtime.now()) {
+907                return Ok(PollProgress {
+908                    received_connection_packet,
+909                    keep_going: true,
+910                });
+911            }
+912        }
+913    }
+914}
+915
+916impl fmt::Debug for RecvState {
+917    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+918        f.debug_struct("RecvState")
+919            .field("incoming", &self.incoming)
+920            .field("connections", &self.connections)
+921            // recv_buf too large
+922            .field("recv_limiter", &self.recv_limiter)
+923            .finish_non_exhaustive()
+924    }
+925}
+926
+927#[derive(Default)]
+928struct PollProgress {
+929    /// Whether a datagram was routed to an existing connection
+930    received_connection_packet: bool,
+931    /// Whether datagram handling was interrupted early by the work limiter for fairness
+932    keep_going: bool,
+933}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn/incoming.rs.html b/pr/395/docs/src/iroh_quinn/incoming.rs.html new file mode 100644 index 000000000..666f6fccd --- /dev/null +++ b/pr/395/docs/src/iroh_quinn/incoming.rs.html @@ -0,0 +1,152 @@ +incoming.rs - source

iroh_quinn/
incoming.rs

1use std::{
+2    future::{Future, IntoFuture},
+3    net::{IpAddr, SocketAddr},
+4    pin::Pin,
+5    sync::Arc,
+6    task::{Context, Poll},
+7};
+8
+9use proto::{ConnectionError, ConnectionId, ServerConfig};
+10use thiserror::Error;
+11
+12use crate::{
+13    connection::{Connecting, Connection},
+14    endpoint::EndpointRef,
+15};
+16
+17/// An incoming connection for which the server has not yet begun its part of the handshake
+18#[derive(Debug)]
+19pub struct Incoming(Option<State>);
+20
+21impl Incoming {
+22    pub(crate) fn new(inner: proto::Incoming, endpoint: EndpointRef) -> Self {
+23        Self(Some(State { inner, endpoint }))
+24    }
+25
+26    /// Attempt to accept this incoming connection (an error may still occur)
+27    pub fn accept(mut self) -> Result<Connecting, ConnectionError> {
+28        let state = self.0.take().unwrap();
+29        state.endpoint.accept(state.inner, None)
+30    }
+31
+32    /// Accept this incoming connection using a custom configuration
+33    ///
+34    /// See [`accept()`][Incoming::accept] for more details.
+35    pub fn accept_with(
+36        mut self,
+37        server_config: Arc<ServerConfig>,
+38    ) -> Result<Connecting, ConnectionError> {
+39        let state = self.0.take().unwrap();
+40        state.endpoint.accept(state.inner, Some(server_config))
+41    }
+42
+43    /// Reject this incoming connection attempt
+44    pub fn refuse(mut self) {
+45        let state = self.0.take().unwrap();
+46        state.endpoint.refuse(state.inner);
+47    }
+48
+49    /// Respond with a retry packet, requiring the client to retry with address validation
+50    ///
+51    /// Errors if `may_retry()` is false.
+52    pub fn retry(mut self) -> Result<(), RetryError> {
+53        let state = self.0.take().unwrap();
+54        state.endpoint.retry(state.inner).map_err(|e| {
+55            RetryError(Box::new(Self(Some(State {
+56                inner: e.into_incoming(),
+57                endpoint: state.endpoint,
+58            }))))
+59        })
+60    }
+61
+62    /// Ignore this incoming connection attempt, not sending any packet in response
+63    pub fn ignore(mut self) {
+64        let state = self.0.take().unwrap();
+65        state.endpoint.ignore(state.inner);
+66    }
+67
+68    /// The local IP address which was used when the peer established the connection
+69    pub fn local_ip(&self) -> Option<IpAddr> {
+70        self.0.as_ref().unwrap().inner.local_ip()
+71    }
+72
+73    /// The peer's UDP address
+74    pub fn remote_address(&self) -> SocketAddr {
+75        self.0.as_ref().unwrap().inner.remote_address()
+76    }
+77
+78    /// Whether the socket address that is initiating this connection has been validated
+79    ///
+80    /// This means that the sender of the initial packet has proved that they can receive traffic
+81    /// sent to `self.remote_address()`.
+82    ///
+83    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+84    /// The inverse is not guaranteed.
+85    pub fn remote_address_validated(&self) -> bool {
+86        self.0.as_ref().unwrap().inner.remote_address_validated()
+87    }
+88
+89    /// Whether it is legal to respond with a retry packet
+90    ///
+91    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+92    /// The inverse is not guaranteed.
+93    pub fn may_retry(&self) -> bool {
+94        self.0.as_ref().unwrap().inner.may_retry()
+95    }
+96
+97    /// The original destination CID when initiating the connection
+98    pub fn orig_dst_cid(&self) -> ConnectionId {
+99        self.0.as_ref().unwrap().inner.orig_dst_cid()
+100    }
+101}
+102
+103impl Drop for Incoming {
+104    fn drop(&mut self) {
+105        // Implicit reject, similar to Connection's implicit close
+106        if let Some(state) = self.0.take() {
+107            state.endpoint.refuse(state.inner);
+108        }
+109    }
+110}
+111
+112#[derive(Debug)]
+113struct State {
+114    inner: proto::Incoming,
+115    endpoint: EndpointRef,
+116}
+117
+118/// Error for attempting to retry an [`Incoming`] which already bears a token from a previous retry
+119#[derive(Debug, Error)]
+120#[error("retry() with validated Incoming")]
+121pub struct RetryError(Box<Incoming>);
+122
+123impl RetryError {
+124    /// Get the [`Incoming`]
+125    pub fn into_incoming(self) -> Incoming {
+126        *self.0
+127    }
+128}
+129
+130/// Basic adapter to let [`Incoming`] be `await`-ed like a [`Connecting`]
+131#[derive(Debug)]
+132pub struct IncomingFuture(Result<Connecting, ConnectionError>);
+133
+134impl Future for IncomingFuture {
+135    type Output = Result<Connection, ConnectionError>;
+136
+137    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+138        match &mut self.0 {
+139            Ok(connecting) => Pin::new(connecting).poll(cx),
+140            Err(e) => Poll::Ready(Err(e.clone())),
+141        }
+142    }
+143}
+144
+145impl IntoFuture for Incoming {
+146    type Output = Result<Connection, ConnectionError>;
+147    type IntoFuture = IncomingFuture;
+148
+149    fn into_future(self) -> Self::IntoFuture {
+150        IncomingFuture(self.accept())
+151    }
+152}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn/lib.rs.html b/pr/395/docs/src/iroh_quinn/lib.rs.html new file mode 100644 index 000000000..bd0410251 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn/lib.rs.html @@ -0,0 +1,138 @@ +lib.rs - source

iroh_quinn/
lib.rs

1//! QUIC transport protocol implementation
+2//!
+3//! [QUIC](https://en.wikipedia.org/wiki/QUIC) is a modern transport protocol addressing
+4//! shortcomings of TCP, such as head-of-line blocking, poor security, slow handshakes, and
+5//! inefficient congestion control. This crate provides a portable userspace implementation. It
+6//! builds on top of quinn-proto, which implements protocol logic independent of any particular
+7//! runtime.
+8//!
+9//! The entry point of this crate is the [`Endpoint`].
+10//!
+11//! # About QUIC
+12//!
+13//! A QUIC connection is an association between two endpoints. The endpoint which initiates the
+14//! connection is termed the client, and the endpoint which accepts it is termed the server. A
+15//! single endpoint may function as both client and server for different connections, for example
+16//! in a peer-to-peer application. To communicate application data, each endpoint may open streams
+17//! up to a limit dictated by its peer. Typically, that limit is increased as old streams are
+18//! finished.
+19//!
+20//! Streams may be unidirectional or bidirectional, and are cheap to create and disposable. For
+21//! example, a traditionally datagram-oriented application could use a new stream for every
+22//! message it wants to send, no longer needing to worry about MTUs. Bidirectional streams behave
+23//! much like a traditional TCP connection, and are useful for sending messages that have an
+24//! immediate response, such as an HTTP request. Stream data is delivered reliably, and there is no
+25//! ordering enforced between data on different streams.
+26//!
+27//! By avoiding head-of-line blocking and providing unified congestion control across all streams
+28//! of a connection, QUIC is able to provide higher throughput and lower latency than one or
+29//! multiple TCP connections between the same two hosts, while providing more useful behavior than
+30//! raw UDP sockets.
+31//!
+32//! Quinn also exposes unreliable datagrams, which are a low-level primitive preferred when
+33//! automatic fragmentation and retransmission of certain data is not desired.
+34//!
+35//! QUIC uses encryption and identity verification built directly on TLS 1.3. Just as with a TLS
+36//! server, it is useful for a QUIC server to be identified by a certificate signed by a trusted
+37//! authority. If this is infeasible--for example, if servers are short-lived or not associated
+38//! with a domain name--then as with TLS, self-signed certificates can be used to provide
+39//! encryption alone.
+40#![warn(missing_docs)]
+41
+42use std::pin::Pin;
+43
+44mod connection;
+45mod endpoint;
+46mod incoming;
+47mod mutex;
+48mod path;
+49mod recv_stream;
+50mod runtime;
+51mod send_stream;
+52mod work_limiter;
+53
+54#[cfg(not(wasm_browser))]
+55pub(crate) use std::time::{Duration, Instant};
+56#[cfg(wasm_browser)]
+57pub(crate) use web_time::{Duration, Instant};
+58
+59#[cfg(feature = "bloom")]
+60pub use proto::BloomTokenLog;
+61pub use proto::{
+62    AckFrequencyConfig, ApplicationClose, Chunk, ClientConfig, ClosedStream, ConfigError,
+63    ConnectError, ConnectionClose, ConnectionError, ConnectionId, ConnectionIdGenerator,
+64    ConnectionStats, Dir, EcnCodepoint, EndpointConfig, FrameStats, FrameType, IdleTimeout,
+65    InvalidCid, MtuDiscoveryConfig, NoneTokenLog, NoneTokenStore, PathId, PathStats, ServerConfig,
+66    Side, StdSystemTime, StreamId, TimeSource, TokenLog, TokenMemoryCache, TokenReuseError,
+67    TokenStore, Transmit, TransportConfig, TransportErrorCode, UdpStats, ValidationTokenConfig,
+68    VarInt, VarIntBoundsExceeded, Written, congestion, crypto,
+69};
+70#[cfg(feature = "qlog")]
+71pub use proto::{QlogConfig, QlogFactory, QlogFileFactory};
+72#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+73pub use rustls;
+74pub use udp;
+75
+76pub use crate::connection::{
+77    AcceptBi, AcceptUni, Connecting, Connection, OnClosed, OpenBi, OpenUni, ReadDatagram,
+78    SendDatagram, SendDatagramError, WeakConnectionHandle, ZeroRttAccepted,
+79};
+80pub use crate::endpoint::{Accept, Endpoint, EndpointStats};
+81pub use crate::incoming::{Incoming, IncomingFuture, RetryError};
+82pub use crate::path::{AddressDiscovery, ClosePath, OpenPath, Path};
+83pub use crate::recv_stream::{
+84    ReadError, ReadExactError, ReadToEndError, RecvStream, ResetError, UnorderedRecvStream,
+85};
+86#[cfg(feature = "runtime-smol")]
+87pub use crate::runtime::SmolRuntime;
+88#[cfg(feature = "runtime-tokio")]
+89pub use crate::runtime::TokioRuntime;
+90#[cfg(any(feature = "runtime-tokio", feature = "runtime-smol"))]
+91pub use crate::runtime::default_runtime;
+92pub use crate::runtime::{AsyncTimer, AsyncUdpSocket, Runtime, UdpSender};
+93pub use crate::send_stream::{SendStream, StoppedError, WriteError};
+94
+95#[cfg(test)]
+96mod tests;
+97
+98#[derive(Debug)]
+99enum ConnectionEvent {
+100    Close {
+101        error_code: VarInt,
+102        reason: bytes::Bytes,
+103    },
+104    Proto(proto::ConnectionEvent),
+105    Rebind(Pin<Box<dyn UdpSender>>),
+106}
+107
+108fn udp_transmit<'a>(t: &proto::Transmit, buffer: &'a [u8]) -> udp::Transmit<'a> {
+109    udp::Transmit {
+110        destination: t.destination,
+111        ecn: t.ecn.map(udp_ecn),
+112        contents: buffer,
+113        segment_size: t.segment_size,
+114        src_ip: t.src_ip,
+115    }
+116}
+117
+118fn udp_ecn(ecn: proto::EcnCodepoint) -> udp::EcnCodepoint {
+119    match ecn {
+120        proto::EcnCodepoint::Ect0 => udp::EcnCodepoint::Ect0,
+121        proto::EcnCodepoint::Ect1 => udp::EcnCodepoint::Ect1,
+122        proto::EcnCodepoint::Ce => udp::EcnCodepoint::Ce,
+123    }
+124}
+125
+126/// Maximum number of datagrams processed in send/recv calls to make before moving on to other processing
+127///
+128/// This helps ensure we don't starve anything when the CPU is slower than the link.
+129/// Value is selected by picking a low number which didn't degrade throughput in benchmarks.
+130const IO_LOOP_BOUND: usize = 160;
+131
+132/// The maximum amount of time that should be spent in `recvmsg()` calls per endpoint iteration
+133///
+134/// 50us are chosen so that an endpoint iteration with a 50us sendmsg limit blocks
+135/// the runtime for a maximum of about 100us.
+136/// Going much lower does not yield any noticeable difference, since a single `recvmmsg`
+137/// batch of size 32 was observed to take 30us on some systems.
+138const RECV_TIME_BOUND: Duration = Duration::from_micros(50);
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn/mutex.rs.html b/pr/395/docs/src/iroh_quinn/mutex.rs.html new file mode 100644 index 000000000..ad0484c77 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn/mutex.rs.html @@ -0,0 +1,163 @@ +mutex.rs - source

iroh_quinn/
mutex.rs

1use std::{
+2    fmt::Debug,
+3    ops::{Deref, DerefMut},
+4};
+5
+6#[cfg(feature = "lock_tracking")]
+7mod tracking {
+8    use super::*;
+9    use crate::{Duration, Instant};
+10    use std::collections::VecDeque;
+11    use tracing::warn;
+12
+13    #[derive(Debug)]
+14    struct Inner<T> {
+15        last_lock_owner: VecDeque<(&'static str, Duration)>,
+16        value: T,
+17    }
+18
+19    /// A Mutex which optionally allows to track the time a lock was held and
+20    /// emit warnings in case of excessive lock times
+21    pub(crate) struct Mutex<T> {
+22        inner: std::sync::Mutex<Inner<T>>,
+23    }
+24
+25    impl<T: Debug> std::fmt::Debug for Mutex<T> {
+26        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+27            std::fmt::Debug::fmt(&self.inner, f)
+28        }
+29    }
+30
+31    impl<T> Mutex<T> {
+32        pub(crate) fn new(value: T) -> Self {
+33            Self {
+34                inner: std::sync::Mutex::new(Inner {
+35                    last_lock_owner: VecDeque::new(),
+36                    value,
+37                }),
+38            }
+39        }
+40
+41        /// Acquires the lock for a certain purpose
+42        ///
+43        /// The purpose will be recorded in the list of last lock owners
+44        pub(crate) fn lock(&self, purpose: &'static str) -> MutexGuard<'_, T> {
+45            // We don't bother dispatching through Runtime::now because they're pure performance
+46            // diagnostics.
+47            let now = Instant::now();
+48            let guard = self.inner.lock().unwrap();
+49
+50            let lock_time = Instant::now();
+51            let elapsed = lock_time.duration_since(now);
+52
+53            if elapsed > Duration::from_millis(1) {
+54                warn!(
+55                    "Locking the connection for {} took {:?}. Last owners: {:?}",
+56                    purpose, elapsed, guard.last_lock_owner
+57                );
+58            }
+59
+60            MutexGuard {
+61                guard,
+62                start_time: lock_time,
+63                purpose,
+64            }
+65        }
+66    }
+67
+68    pub(crate) struct MutexGuard<'a, T> {
+69        guard: std::sync::MutexGuard<'a, Inner<T>>,
+70        start_time: Instant,
+71        purpose: &'static str,
+72    }
+73
+74    impl<T> Drop for MutexGuard<'_, T> {
+75        fn drop(&mut self) {
+76            if self.guard.last_lock_owner.len() == MAX_LOCK_OWNERS {
+77                self.guard.last_lock_owner.pop_back();
+78            }
+79
+80            let duration = self.start_time.elapsed();
+81
+82            if duration > Duration::from_millis(1) {
+83                warn!(
+84                    "Utilizing the connection for {} took {:?}",
+85                    self.purpose, duration
+86                );
+87            }
+88
+89            self.guard
+90                .last_lock_owner
+91                .push_front((self.purpose, duration));
+92        }
+93    }
+94
+95    impl<T> Deref for MutexGuard<'_, T> {
+96        type Target = T;
+97
+98        fn deref(&self) -> &Self::Target {
+99            &self.guard.value
+100        }
+101    }
+102
+103    impl<T> DerefMut for MutexGuard<'_, T> {
+104        fn deref_mut(&mut self) -> &mut Self::Target {
+105            &mut self.guard.value
+106        }
+107    }
+108
+109    const MAX_LOCK_OWNERS: usize = 20;
+110}
+111
+112#[cfg(feature = "lock_tracking")]
+113pub(crate) use tracking::Mutex;
+114
+115#[cfg(not(feature = "lock_tracking"))]
+116mod non_tracking {
+117    use super::*;
+118
+119    /// A Mutex which optionally allows to track the time a lock was held and
+120    /// emit warnings in case of excessive lock times
+121    #[derive(Debug)]
+122    pub(crate) struct Mutex<T> {
+123        inner: std::sync::Mutex<T>,
+124    }
+125
+126    impl<T> Mutex<T> {
+127        pub(crate) fn new(value: T) -> Self {
+128            Self {
+129                inner: std::sync::Mutex::new(value),
+130            }
+131        }
+132
+133        /// Acquires the lock for a certain purpose
+134        ///
+135        /// The purpose will be recorded in the list of last lock owners
+136        pub(crate) fn lock(&self, _purpose: &'static str) -> MutexGuard<'_, T> {
+137            MutexGuard {
+138                guard: self.inner.lock().unwrap(),
+139            }
+140        }
+141    }
+142
+143    pub(crate) struct MutexGuard<'a, T> {
+144        guard: std::sync::MutexGuard<'a, T>,
+145    }
+146
+147    impl<T> Deref for MutexGuard<'_, T> {
+148        type Target = T;
+149
+150        fn deref(&self) -> &Self::Target {
+151            self.guard.deref()
+152        }
+153    }
+154
+155    impl<T> DerefMut for MutexGuard<'_, T> {
+156        fn deref_mut(&mut self) -> &mut Self::Target {
+157            self.guard.deref_mut()
+158        }
+159    }
+160}
+161
+162#[cfg(not(feature = "lock_tracking"))]
+163pub(crate) use non_tracking::Mutex;
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn/path.rs.html b/pr/395/docs/src/iroh_quinn/path.rs.html new file mode 100644 index 000000000..dd44d7f0f --- /dev/null +++ b/pr/395/docs/src/iroh_quinn/path.rs.html @@ -0,0 +1,303 @@ +path.rs - source

iroh_quinn/
path.rs

1use std::future::Future;
+2use std::net::SocketAddr;
+3use std::pin::Pin;
+4use std::sync::Arc;
+5use std::task::{Context, Poll, ready};
+6use std::time::Duration;
+7
+8use proto::{
+9    ClosePathError, ClosedPath, ConnectionError, PathError, PathEvent, PathId, PathStatus,
+10    SetPathStatusError, TransportErrorCode, VarInt,
+11};
+12use tokio::sync::{oneshot, watch};
+13use tokio_stream::{Stream, wrappers::WatchStream};
+14
+15use crate::Runtime;
+16use crate::connection::ConnectionRef;
+17
+18/// Future produced by [`crate::Connection::open_path`]
+19pub struct OpenPath(OpenPathInner);
+20
+21enum OpenPathInner {
+22    /// Opening a path in underway
+23    ///
+24    /// This might fail later on.
+25    Ongoing {
+26        opened: WatchStream<Result<(), PathError>>,
+27        path_id: PathId,
+28        conn: ConnectionRef,
+29    },
+30    /// Opening a path failed immediately
+31    Rejected {
+32        /// The error that occurred
+33        err: PathError,
+34    },
+35    /// The path is already open
+36    Ready {
+37        path_id: PathId,
+38        conn: ConnectionRef,
+39    },
+40}
+41
+42impl OpenPath {
+43    pub(crate) fn new(
+44        path_id: PathId,
+45        opened: watch::Receiver<Result<(), PathError>>,
+46        conn: ConnectionRef,
+47    ) -> Self {
+48        Self(OpenPathInner::Ongoing {
+49            opened: WatchStream::from_changes(opened),
+50            path_id,
+51            conn,
+52        })
+53    }
+54
+55    pub(crate) fn ready(path_id: PathId, conn: ConnectionRef) -> Self {
+56        Self(OpenPathInner::Ready { path_id, conn })
+57    }
+58
+59    pub(crate) fn rejected(err: PathError) -> Self {
+60        Self(OpenPathInner::Rejected { err })
+61    }
+62
+63    /// Returns the path ID of the new path being opened.
+64    ///
+65    /// If an error occurred before a path ID was allocated, `None` is returned.  In this
+66    /// case the future is ready and polling it will immediately yield the error.
+67    ///
+68    /// The returned value remains the same for the entire lifetime of this future.
+69    pub fn path_id(&self) -> Option<PathId> {
+70        match self.0 {
+71            OpenPathInner::Ongoing { path_id, .. } => Some(path_id),
+72            OpenPathInner::Rejected { .. } => None,
+73            OpenPathInner::Ready { path_id, .. } => Some(path_id),
+74        }
+75    }
+76}
+77
+78impl Future for OpenPath {
+79    type Output = Result<Path, PathError>;
+80    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+81        match self.get_mut().0 {
+82            OpenPathInner::Ongoing {
+83                ref mut opened,
+84                path_id,
+85                ref mut conn,
+86            } => match ready!(Pin::new(opened).poll_next(ctx)) {
+87                Some(value) => Poll::Ready(value.map(|_| Path {
+88                    id: path_id,
+89                    conn: conn.clone(),
+90                })),
+91                None => {
+92                    // This only happens if receiving a notification change failed, this means the
+93                    // sender was dropped. This generally should not happen so we use a transient
+94                    // error
+95                    Poll::Ready(Err(PathError::ValidationFailed))
+96                }
+97            },
+98            OpenPathInner::Ready {
+99                path_id,
+100                ref mut conn,
+101            } => Poll::Ready(Ok(Path {
+102                id: path_id,
+103                conn: conn.clone(),
+104            })),
+105            OpenPathInner::Rejected { err } => Poll::Ready(Err(err)),
+106        }
+107    }
+108}
+109
+110/// An open (Multi)Path
+111#[derive(Debug)]
+112pub struct Path {
+113    pub(crate) id: PathId,
+114    pub(crate) conn: ConnectionRef,
+115}
+116
+117impl Path {
+118    /// The [`PathId`] of this path.
+119    pub fn id(&self) -> PathId {
+120        self.id
+121    }
+122
+123    /// The current local [`PathStatus`] of this path.
+124    pub fn status(&self) -> Result<PathStatus, ClosedPath> {
+125        self.conn
+126            .state
+127            .lock("path status")
+128            .inner
+129            .path_status(self.id)
+130    }
+131
+132    /// Sets the [`PathStatus`] of this path.
+133    pub fn set_status(&self, status: PathStatus) -> Result<(), SetPathStatusError> {
+134        self.conn
+135            .state
+136            .lock("set path status")
+137            .inner
+138            .set_path_status(self.id, status)?;
+139        Ok(())
+140    }
+141
+142    /// Closes this path
+143    ///
+144    /// The future will resolve when all the path state is dropped.  This only happens after
+145    /// the remote has confirmed the path as closed **and** after an additional timeout to
+146    /// give any in-flight packets the time to arrive.
+147    pub fn close(&self) -> Result<ClosePath, ClosePathError> {
+148        let (on_path_close_send, on_path_close_recv) = oneshot::channel();
+149        {
+150            let mut state = self.conn.state.lock("close_path");
+151            state.inner.close_path(
+152                crate::Instant::now(),
+153                self.id,
+154                TransportErrorCode::APPLICATION_ABANDON_PATH.into(),
+155            )?;
+156            state.close_path.insert(self.id, on_path_close_send);
+157        }
+158
+159        Ok(ClosePath {
+160            closed: on_path_close_recv,
+161        })
+162    }
+163
+164    /// Sets the keep_alive_interval for a specific path
+165    ///
+166    /// See [`TransportConfig::default_path_keep_alive_interval`] for details.
+167    ///
+168    /// Returns the previous value of the setting.
+169    ///
+170    /// [`TransportConfig::default_path_keep_alive_interval`]: crate::TransportConfig::default_path_keep_alive_interval
+171    pub fn set_max_idle_timeout(
+172        &self,
+173        timeout: Option<Duration>,
+174    ) -> Result<Option<Duration>, ClosedPath> {
+175        let mut state = self.conn.state.lock("path_set_max_idle_timeout");
+176        state.inner.set_path_max_idle_timeout(self.id, timeout)
+177    }
+178
+179    /// Sets the keep_alive_interval for a specific path
+180    ///
+181    /// See [`TransportConfig::default_path_keep_alive_interval`] for details.
+182    ///
+183    /// Returns the previous value of the setting.
+184    ///
+185    /// [`TransportConfig::default_path_keep_alive_interval`]: crate::TransportConfig::default_path_keep_alive_interval
+186    pub fn set_keep_alive_interval(
+187        &self,
+188        interval: Option<Duration>,
+189    ) -> Result<Option<Duration>, ClosedPath> {
+190        let mut state = self.conn.state.lock("path_set_keep_alive_interval");
+191        state.inner.set_path_keep_alive_interval(self.id, interval)
+192    }
+193
+194    /// Track changes on our external address as reported by the peer.
+195    ///
+196    /// If the address-discovery extension is not negotiated, the stream will never return.
+197    pub fn observed_external_addr(&self) -> Result<AddressDiscovery, ClosedPath> {
+198        let state = self.conn.state.lock("per_path_observed_address");
+199        let path_events = state.path_events.subscribe();
+200        let initial_value = state.inner.path_observed_address(self.id)?;
+201        Ok(AddressDiscovery::new(
+202            self.id,
+203            path_events,
+204            initial_value,
+205            state.runtime.clone(),
+206        ))
+207    }
+208
+209    /// The peer's UDP address for this path.
+210    pub fn remote_address(&self) -> Result<SocketAddr, ClosedPath> {
+211        let state = self.conn.state.lock("per_path_remote_address");
+212        Ok(state.inner.network_path(self.id)?.remote)
+213    }
+214
+215    /// Ping the remote endpoint over this path.
+216    pub fn ping(&self) -> Result<(), ClosedPath> {
+217        let mut state = self.conn.state.lock("ping");
+218        state.inner.ping_path(self.id)
+219    }
+220}
+221
+222/// Future produced by [`Path::close`]
+223pub struct ClosePath {
+224    closed: oneshot::Receiver<VarInt>,
+225}
+226
+227impl Future for ClosePath {
+228    type Output = Result<VarInt, ConnectionError>;
+229    fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+230        // TODO: thread through errors
+231        let res = ready!(Pin::new(&mut self.closed).poll(ctx));
+232        match res {
+233            Ok(code) => Poll::Ready(Ok(code)),
+234            Err(_err) => todo!(), // TODO: appropriate error
+235        }
+236    }
+237}
+238
+239/// Stream produced by [`Path::observed_external_addr`]
+240///
+241/// This will always return the external address most recently reported by the remote over this
+242/// path. If the extension is not negotiated, this stream will never return.
+243// TODO(@divma): provide a way to check if the extension is negotiated.
+244pub struct AddressDiscovery {
+245    watcher: WatchStream<SocketAddr>,
+246}
+247
+248impl AddressDiscovery {
+249    pub(super) fn new(
+250        path_id: PathId,
+251        mut path_events: tokio::sync::broadcast::Receiver<PathEvent>,
+252        initial_value: Option<SocketAddr>,
+253        runtime: Arc<dyn Runtime>,
+254    ) -> Self {
+255        let (tx, rx) = watch::channel(initial_value.unwrap_or_else(||
+256                // if the dummy value is used, it will be ignored
+257                SocketAddr::new([0, 0, 0, 0].into(), 0)));
+258        let filter = async move {
+259            loop {
+260                match path_events.recv().await {
+261                    Ok(PathEvent::ObservedAddr { id, addr: observed }) if id == path_id => {
+262                        tx.send_if_modified(|addr| {
+263                            let old = std::mem::replace(addr, observed);
+264                            old != *addr
+265                        });
+266                    }
+267                    Ok(PathEvent::Abandoned { id, .. }) if id == path_id => {
+268                        // If the path is closed, terminate the stream
+269                        break;
+270                    }
+271                    Ok(_) => {
+272                        // ignore any other event
+273                    }
+274                    Err(_) => {
+275                        // A lagged error should never happen since this (detached) task is
+276                        // constantly reading from the channel. Therefore, if an error does happen,
+277                        // the stream can terminate
+278                        break;
+279                    }
+280                }
+281            }
+282        };
+283
+284        let watcher = if initial_value.is_some() {
+285            WatchStream::new(rx)
+286        } else {
+287            WatchStream::from_changes(rx)
+288        };
+289
+290        runtime.spawn(Box::pin(filter));
+291        // TODO(@divma): check if there's a way to ensure the future ends. AbortHandle is not an
+292        // option
+293        Self { watcher }
+294    }
+295}
+296
+297impl Stream for AddressDiscovery {
+298    type Item = SocketAddr;
+299
+300    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+301        Pin::new(&mut self.watcher).poll_next(cx)
+302    }
+303}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn/recv_stream.rs.html b/pr/395/docs/src/iroh_quinn/recv_stream.rs.html new file mode 100644 index 000000000..2aad2b8ca --- /dev/null +++ b/pr/395/docs/src/iroh_quinn/recv_stream.rs.html @@ -0,0 +1,759 @@ +recv_stream.rs - source

iroh_quinn/
recv_stream.rs

1use std::{
+2    future::{Future, poll_fn},
+3    io,
+4    pin::Pin,
+5    task::{Context, Poll, ready},
+6};
+7
+8use bytes::Bytes;
+9use proto::{Chunk, Chunks, ClosedStream, ConnectionError, ReadableError, StreamId};
+10use thiserror::Error;
+11use tokio::io::ReadBuf;
+12
+13use crate::{VarInt, connection::ConnectionRef};
+14
+15/// A stream that can only be used to receive data
+16///
+17/// `stop(0)` is implicitly called on drop unless:
+18/// - A variant of [`ReadError`] has been yielded by a read call
+19/// - [`stop()`] was called explicitly
+20///
+21/// # Cancellation
+22///
+23/// A `read` method is said to be *cancel-safe* when dropping its future before the future becomes
+24/// ready cannot lead to loss of stream data. This is true of methods which succeed immediately when
+25/// any progress is made, and is not true of methods which might need to perform multiple reads
+26/// internally before succeeding. Each `read` method documents whether it is cancel-safe.
+27///
+28/// # Common issues
+29///
+30/// ## Data never received on a locally-opened stream
+31///
+32/// Peers are not notified of streams until they or a later-numbered stream are used to send
+33/// data. If a bidirectional stream is locally opened but never used to send, then the peer may
+34/// never see it. Application protocols should always arrange for the endpoint which will first
+35/// transmit on a stream to be the endpoint responsible for opening it.
+36///
+37/// ## Data never received on a remotely-opened stream
+38///
+39/// Verify that the stream you are receiving is the same one that the server is sending on, e.g. by
+40/// logging the [`id`] of each. Streams are always accepted in the same order as they are created,
+41/// i.e. ascending order by [`StreamId`]. For example, even if a sender first transmits on
+42/// bidirectional stream 1, the first stream yielded by [`Connection::accept_bi`] on the receiver
+43/// will be bidirectional stream 0.
+44///
+45/// [`ReadError`]: crate::ReadError
+46/// [`stop()`]: RecvStream::stop
+47/// [`SendStream::finish`]: crate::SendStream::finish
+48/// [`WriteError::Stopped`]: crate::WriteError::Stopped
+49/// [`id`]: RecvStream::id
+50/// [`Connection::accept_bi`]: crate::Connection::accept_bi
+51#[derive(Debug)]
+52pub struct RecvStream {
+53    conn: ConnectionRef,
+54    stream: StreamId,
+55    is_0rtt: bool,
+56    all_data_read: bool,
+57    reset: Option<VarInt>,
+58}
+59
+60impl RecvStream {
+61    pub(crate) fn new(conn: ConnectionRef, stream: StreamId, is_0rtt: bool) -> Self {
+62        Self {
+63            conn,
+64            stream,
+65            is_0rtt,
+66            all_data_read: false,
+67            reset: None,
+68        }
+69    }
+70
+71    /// Read data contiguously from the stream.
+72    ///
+73    /// Yields the number of bytes read into `buf` on success, or `None` if the stream was finished.
+74    ///
+75    /// This operation is cancel-safe.
+76    pub async fn read(&mut self, buf: &mut [u8]) -> Result<Option<usize>, ReadError> {
+77        Read {
+78            stream: self,
+79            buf: ReadBuf::new(buf),
+80        }
+81        .await
+82    }
+83
+84    /// Read an exact number of bytes contiguously from the stream.
+85    ///
+86    /// See [`read()`] for details. This operation is *not* cancel-safe.
+87    ///
+88    /// [`read()`]: RecvStream::read
+89    pub async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError> {
+90        ReadExact {
+91            stream: self,
+92            buf: ReadBuf::new(buf),
+93        }
+94        .await
+95    }
+96
+97    /// Attempts to read from the stream into the provided buffer
+98    ///
+99    /// On success, returns `Poll::Ready(Ok(num_bytes_read))` and places data into `buf`. If this
+100    /// returns zero bytes read (and `buf` has a non-zero length), that indicates that the remote
+101    /// side has [`finish`]ed the stream and the local side has already read all bytes.
+102    ///
+103    /// If no data is available for reading, this returns `Poll::Pending` and arranges for the
+104    /// current task (via `cx.waker()`) to be notified when the stream becomes readable or is
+105    /// closed.
+106    ///
+107    /// [`finish`]: crate::SendStream::finish
+108    pub fn poll_read(
+109        &mut self,
+110        cx: &mut Context<'_>,
+111        buf: &mut [u8],
+112    ) -> Poll<Result<usize, ReadError>> {
+113        let mut buf = ReadBuf::new(buf);
+114        ready!(self.poll_read_buf(cx, &mut buf))?;
+115        Poll::Ready(Ok(buf.filled().len()))
+116    }
+117
+118    /// Attempts to read from the stream into the provided buffer, which may be uninitialized
+119    ///
+120    /// On success, returns `Poll::Ready(Ok(()))` and places data into the unfilled portion of
+121    /// `buf`. If this does not write any bytes to `buf` (and `buf.remaining()` is non-zero), that
+122    /// indicates that the remote side has [`finish`]ed the stream and the local side has already
+123    /// read all bytes.
+124    ///
+125    /// If no data is available for reading, this returns `Poll::Pending` and arranges for the
+126    /// current task (via `cx.waker()`) to be notified when the stream becomes readable or is
+127    /// closed.
+128    ///
+129    /// [`finish`]: crate::SendStream::finish
+130    pub fn poll_read_buf(
+131        &mut self,
+132        cx: &mut Context<'_>,
+133        buf: &mut ReadBuf<'_>,
+134    ) -> Poll<Result<(), ReadError>> {
+135        if buf.remaining() == 0 {
+136            return Poll::Ready(Ok(()));
+137        }
+138
+139        self.poll_read_generic(cx, true, |chunks| {
+140            let mut read = false;
+141            loop {
+142                if buf.remaining() == 0 {
+143                    // We know `read` is `true` because `buf.remaining()` was not 0 before
+144                    return ReadStatus::Readable(());
+145                }
+146
+147                match chunks.next(buf.remaining()) {
+148                    Ok(Some(chunk)) => {
+149                        buf.put_slice(&chunk.bytes);
+150                        read = true;
+151                    }
+152                    res => return (if read { Some(()) } else { None }, res.err()).into(),
+153                }
+154            }
+155        })
+156        .map(|res| res.map(|_| ()))
+157    }
+158
+159    /// Read the next segment of data
+160    ///
+161    /// Yields `None` if the stream was finished. Otherwise, yields a segment of data and its
+162    /// offset in the stream. The chunk's offset will be immediately after
+163    /// the last data yielded by [`RecvStream::read`] or [`RecvStream::read_chunk`].
+164    ///
+165    /// For unordered reads, convert the stream into an unordered stream using [`Self::into_unordered`].
+166    ///
+167    /// Slightly more efficient than [`RecvStream::read`] due to not copying. Chunk boundaries do not correspond
+168    /// to peer writes, and hence cannot be used as framing.
+169    ///
+170    /// This operation is cancel-safe.
+171    pub async fn read_chunk(&mut self, max_length: usize) -> Result<Option<Chunk>, ReadError> {
+172        ReadChunk {
+173            stream: self,
+174            max_length,
+175            ordered: true,
+176        }
+177        .await
+178    }
+179
+180    /// Attempts to read a chunk from the stream.
+181    ///
+182    /// On success, returns `Poll::Ready(Ok(Some(chunk)))`. If `Poll::Ready(Ok(None))`
+183    /// is returned, it implies that EOF has been reached.
+184    ///
+185    /// If no data is available for reading, the method returns `Poll::Pending`
+186    /// and arranges for the current task (via cx.waker()) to receive a notification
+187    /// when the stream becomes readable or is closed.
+188    fn poll_read_chunk(
+189        &mut self,
+190        cx: &mut Context<'_>,
+191        max_length: usize,
+192        ordered: bool,
+193    ) -> Poll<Result<Option<Chunk>, ReadError>> {
+194        self.poll_read_generic(cx, ordered, |chunks| match chunks.next(max_length) {
+195            Ok(Some(chunk)) => ReadStatus::Readable(chunk),
+196            res => (None, res.err()).into(),
+197        })
+198    }
+199
+200    /// Read the next segments of data
+201    ///
+202    /// Fills `bufs` with the segments of data beginning immediately after the
+203    /// last data yielded by `read` or `read_chunk`, or `None` if the stream was
+204    /// finished.
+205    ///
+206    /// Slightly more efficient than `read` due to not copying. Chunk boundaries
+207    /// do not correspond to peer writes, and hence cannot be used as framing.
+208    ///
+209    /// This operation is cancel-safe.
+210    pub async fn read_chunks(&mut self, bufs: &mut [Bytes]) -> Result<Option<usize>, ReadError> {
+211        ReadChunks { stream: self, bufs }.await
+212    }
+213
+214    /// Foundation of [`Self::read_chunks`]
+215    fn poll_read_chunks(
+216        &mut self,
+217        cx: &mut Context<'_>,
+218        bufs: &mut [Bytes],
+219    ) -> Poll<Result<Option<usize>, ReadError>> {
+220        if bufs.is_empty() {
+221            return Poll::Ready(Ok(Some(0)));
+222        }
+223
+224        self.poll_read_generic(cx, true, |chunks| {
+225            let mut read = 0;
+226            loop {
+227                if read >= bufs.len() {
+228                    // We know `read > 0` because `bufs` cannot be empty here
+229                    return ReadStatus::Readable(read);
+230                }
+231
+232                match chunks.next(usize::MAX) {
+233                    Ok(Some(chunk)) => {
+234                        bufs[read] = chunk.bytes;
+235                        read += 1;
+236                    }
+237                    res => return (if read == 0 { None } else { Some(read) }, res.err()).into(),
+238                }
+239            }
+240        })
+241    }
+242
+243    /// Convenience method to read all remaining data into a buffer
+244    ///
+245    /// Fails with [`ReadToEndError::TooLong`] on reading more than `size_limit` bytes, discarding
+246    /// all data read. Uses unordered reads to be more efficient than using `AsyncRead` would
+247    /// allow. `size_limit` should be set to limit worst-case memory use.
+248    ///
+249    /// If unordered reads have already been made, the resulting buffer may have gaps containing
+250    /// arbitrary data.
+251    ///
+252    /// This operation is *not* cancel-safe.
+253    ///
+254    /// [`ReadToEndError::TooLong`]: crate::ReadToEndError::TooLong
+255    pub async fn read_to_end(&mut self, size_limit: usize) -> Result<Vec<u8>, ReadToEndError> {
+256        ReadToEnd {
+257            stream: self,
+258            size_limit,
+259            read: Vec::new(),
+260            start: u64::MAX,
+261            end: 0,
+262        }
+263        .await
+264    }
+265
+266    /// Stop accepting data
+267    ///
+268    /// Discards unread data and notifies the peer to stop transmitting. Once stopped, further
+269    /// attempts to operate on a stream will yield `ClosedStream` errors.
+270    pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+271        let mut conn = self.conn.state.lock("RecvStream::stop");
+272        if self.is_0rtt && conn.check_0rtt().is_err() {
+273            return Ok(());
+274        }
+275        conn.inner.recv_stream(self.stream).stop(error_code)?;
+276        conn.wake();
+277        self.all_data_read = true;
+278        Ok(())
+279    }
+280
+281    /// Check if this stream has been opened during 0-RTT.
+282    ///
+283    /// In which case any non-idempotent request should be considered dangerous at the application
+284    /// level. Because read data is subject to replay attacks.
+285    pub fn is_0rtt(&self) -> bool {
+286        self.is_0rtt
+287    }
+288
+289    /// Get the identity of this stream
+290    pub fn id(&self) -> StreamId {
+291        self.stream
+292    }
+293
+294    /// Completes when the stream has been reset by the peer or otherwise closed
+295    ///
+296    /// Yields `Some` with the reset error code when the stream is reset by the peer. Yields `None`
+297    /// when the stream was previously [`stop()`](Self::stop)ed, or when the stream was
+298    /// [`finish()`](crate::SendStream::finish)ed by the peer and all data has been received, after
+299    /// which it is no longer meaningful for the stream to be reset.
+300    ///
+301    /// This operation is cancel-safe.
+302    pub async fn received_reset(&mut self) -> Result<Option<VarInt>, ResetError> {
+303        poll_fn(|cx| {
+304            let mut conn = self.conn.state.lock("RecvStream::reset");
+305            if self.is_0rtt && conn.check_0rtt().is_err() {
+306                return Poll::Ready(Err(ResetError::ZeroRttRejected));
+307            }
+308
+309            if let Some(code) = self.reset {
+310                return Poll::Ready(Ok(Some(code)));
+311            }
+312
+313            match conn.inner.recv_stream(self.stream).received_reset() {
+314                Err(_) => Poll::Ready(Ok(None)),
+315                Ok(Some(error_code)) => {
+316                    // Stream state has just now been freed, so the connection may need to issue new
+317                    // stream ID flow control credit
+318                    conn.wake();
+319                    Poll::Ready(Ok(Some(error_code)))
+320                }
+321                Ok(None) => {
+322                    if let Some(e) = &conn.error {
+323                        return Poll::Ready(Err(e.clone().into()));
+324                    }
+325                    // Resets always notify readers, since a reset is an immediate read error. We
+326                    // could introduce a dedicated channel to reduce the risk of spurious wakeups,
+327                    // but that increased complexity is probably not justified, as an application
+328                    // that is expecting a reset is not likely to receive large amounts of data.
+329                    conn.blocked_readers.insert(self.stream, cx.waker().clone());
+330                    Poll::Pending
+331                }
+332            }
+333        })
+334        .await
+335    }
+336
+337    /// Handle common logic related to reading out of a receive stream
+338    ///
+339    /// This takes an `FnMut` closure that takes care of the actual reading process, matching
+340    /// the detailed read semantics for the calling function with a particular return type.
+341    /// The closure can read from the passed `&mut Chunks` and has to return the status after
+342    /// reading: the amount of data read, and the status after the final read call.
+343    fn poll_read_generic<T, U>(
+344        &mut self,
+345        cx: &mut Context<'_>,
+346        ordered: bool,
+347        mut read_fn: T,
+348    ) -> Poll<Result<Option<U>, ReadError>>
+349    where
+350        T: FnMut(&mut Chunks<'_>) -> ReadStatus<U>,
+351    {
+352        use proto::ReadError::*;
+353        if self.all_data_read {
+354            return Poll::Ready(Ok(None));
+355        }
+356
+357        let mut conn = self.conn.state.lock("RecvStream::poll_read");
+358        if self.is_0rtt {
+359            conn.check_0rtt().map_err(|()| ReadError::ZeroRttRejected)?;
+360        }
+361
+362        // If we stored an error during a previous call, return it now. This can happen if a
+363        // `read_fn` both wants to return data and also returns an error in its final stream status.
+364        let status = match self.reset {
+365            Some(code) => ReadStatus::Failed(None, Reset(code)),
+366            None => {
+367                let mut recv = conn.inner.recv_stream(self.stream);
+368                let mut chunks = recv.read(ordered).map_err(|e| match e {
+369                    ReadableError::ClosedStream => ReadError::ClosedStream,
+370                    ReadableError::IllegalOrderedRead => {
+371                        // We should never get here because the only way to do unordered reads is
+372                        // via UnorderedRecvStream, which allows only unordered reads. It is not
+373                        // possible to get a RecvStream from an UnorderedRecvStream.
+374                        unreachable!("ordered read after unordered read")
+375                    }
+376                })?;
+377                let status = read_fn(&mut chunks);
+378                if chunks.finalize().should_transmit() {
+379                    conn.wake();
+380                }
+381                status
+382            }
+383        };
+384
+385        match status {
+386            ReadStatus::Readable(read) => Poll::Ready(Ok(Some(read))),
+387            ReadStatus::Finished(read) => {
+388                self.all_data_read = true;
+389                Poll::Ready(Ok(read))
+390            }
+391            ReadStatus::Failed(read, Blocked) => match read {
+392                Some(val) => Poll::Ready(Ok(Some(val))),
+393                None => {
+394                    if let Some(ref x) = conn.error {
+395                        return Poll::Ready(Err(ReadError::ConnectionLost(x.clone())));
+396                    }
+397                    conn.blocked_readers.insert(self.stream, cx.waker().clone());
+398                    Poll::Pending
+399                }
+400            },
+401            ReadStatus::Failed(read, Reset(error_code)) => match read {
+402                None => {
+403                    self.all_data_read = true;
+404                    self.reset = Some(error_code);
+405                    Poll::Ready(Err(ReadError::Reset(error_code)))
+406                }
+407                done => {
+408                    self.reset = Some(error_code);
+409                    Poll::Ready(Ok(done))
+410                }
+411            },
+412        }
+413    }
+414
+415    /// Converts this stream into an unordered stream.
+416    pub fn into_unordered(self) -> UnorderedRecvStream {
+417        UnorderedRecvStream { inner: self }
+418    }
+419}
+420
+421/// A stream that can be used to receive data out-of-order.
+422///
+423/// Obtained by converting a [`RecvStream`] via [`RecvStream::into_unordered`].
+424///
+425/// This variant of `RecvStream` allows reading chunks of data *exclusively*
+426/// out of order. Once you have done an unordered read, ordered reads are no
+427/// longer possible since data may have been consumed out of order.
+428///
+429/// The stream state related fns like [`Self::id`], [`Self::is_0rtt`], [`Self::stop`], and
+430/// [`Self::received_reset`] behave exactly as on [`RecvStream`].
+431#[derive(Debug)]
+432pub struct UnorderedRecvStream {
+433    inner: RecvStream,
+434}
+435
+436impl UnorderedRecvStream {
+437    /// Reads the next segment of data.
+438    ///
+439    /// Yields `None` if the stream was finished. Otherwise, yields a segment of data and its
+440    /// offset in the stream. Segments may be received in any order, and the `Chunk`'s `offset`
+441    /// field can be used to determine ordering in the caller. Unordered reads are less prone
+442    /// to head-of-line blocking within a stream, but require the application to manage
+443    /// reassembling the original data.
+444    ///
+445    /// This operation is cancel-safe.
+446    pub async fn read_chunk(&mut self, max_length: usize) -> Result<Option<Chunk>, ReadError> {
+447        ReadChunk {
+448            stream: &mut self.inner,
+449            max_length,
+450            ordered: false,
+451        }
+452        .await
+453    }
+454
+455    /// Get the identity of this stream
+456    pub fn id(&self) -> StreamId {
+457        self.inner.id()
+458    }
+459
+460    /// Check if this stream has been opened during 0-RTT.
+461    ///
+462    /// In which case any non-idempotent request should be considered dangerous at the application
+463    /// level. Because read data is subject to replay attacks.
+464    pub fn is_0rtt(&self) -> bool {
+465        self.inner.is_0rtt()
+466    }
+467
+468    /// Stop accepting data
+469    ///
+470    /// Discards unread data and notifies the peer to stop transmitting. Once stopped, further
+471    /// attempts to operate on a stream will yield `ClosedStream` errors.
+472    pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+473        self.inner.stop(error_code)
+474    }
+475
+476    /// Completes when the stream has been reset by the peer or otherwise closed
+477    ///
+478    /// Yields `Some` with the reset error code when the stream is reset by the peer. Yields `None`
+479    /// when the stream was previously [`stop()`](Self::stop)ed, or when the stream was
+480    /// [`finish()`](crate::SendStream::finish)ed by the peer and all data has been received, after
+481    /// which it is no longer meaningful for the stream to be reset.
+482    ///
+483    /// This operation is cancel-safe.
+484    pub async fn received_reset(&mut self) -> Result<Option<VarInt>, ResetError> {
+485        self.inner.received_reset().await
+486    }
+487}
+488
+489enum ReadStatus<T> {
+490    Readable(T),
+491    Finished(Option<T>),
+492    Failed(Option<T>, proto::ReadError),
+493}
+494
+495impl<T> From<(Option<T>, Option<proto::ReadError>)> for ReadStatus<T> {
+496    fn from(status: (Option<T>, Option<proto::ReadError>)) -> Self {
+497        match status {
+498            (read, None) => Self::Finished(read),
+499            (read, Some(e)) => Self::Failed(read, e),
+500        }
+501    }
+502}
+503
+504/// Future produced by [`RecvStream::read_to_end()`].
+505///
+506/// [`RecvStream::read_to_end()`]: crate::RecvStream::read_to_end
+507struct ReadToEnd<'a> {
+508    stream: &'a mut RecvStream,
+509    read: Vec<(Bytes, u64)>,
+510    start: u64,
+511    end: u64,
+512    size_limit: usize,
+513}
+514
+515impl Future for ReadToEnd<'_> {
+516    type Output = Result<Vec<u8>, ReadToEndError>;
+517    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+518        loop {
+519            match ready!(self.stream.poll_read_chunk(cx, usize::MAX, false))? {
+520                Some(chunk) => {
+521                    self.start = self.start.min(chunk.offset);
+522                    let end = chunk.bytes.len() as u64 + chunk.offset;
+523                    if (end - self.start) > self.size_limit as u64 {
+524                        return Poll::Ready(Err(ReadToEndError::TooLong));
+525                    }
+526                    self.end = self.end.max(end);
+527                    self.read.push((chunk.bytes, chunk.offset));
+528                }
+529                None => {
+530                    if self.end == 0 {
+531                        // Never received anything
+532                        return Poll::Ready(Ok(Vec::new()));
+533                    }
+534                    let start = self.start;
+535                    let mut buffer = vec![0; (self.end - start) as usize];
+536                    for (data, offset) in self.read.drain(..) {
+537                        let offset = (offset - start) as usize;
+538                        buffer[offset..offset + data.len()].copy_from_slice(&data);
+539                    }
+540                    return Poll::Ready(Ok(buffer));
+541                }
+542            }
+543        }
+544    }
+545}
+546
+547/// Errors from [`RecvStream::read_to_end`]
+548#[derive(Debug, Error, Clone, PartialEq, Eq)]
+549pub enum ReadToEndError {
+550    /// An error occurred during reading
+551    #[error("read error: {0}")]
+552    Read(#[from] ReadError),
+553    /// The stream is larger than the user-supplied limit
+554    #[error("stream too long")]
+555    TooLong,
+556}
+557
+558#[cfg(feature = "futures-io")]
+559impl futures_io::AsyncRead for RecvStream {
+560    fn poll_read(
+561        self: Pin<&mut Self>,
+562        cx: &mut Context<'_>,
+563        buf: &mut [u8],
+564    ) -> Poll<io::Result<usize>> {
+565        let mut buf = ReadBuf::new(buf);
+566        ready!(Self::poll_read_buf(self.get_mut(), cx, &mut buf))?;
+567        Poll::Ready(Ok(buf.filled().len()))
+568    }
+569}
+570
+571impl tokio::io::AsyncRead for RecvStream {
+572    fn poll_read(
+573        self: Pin<&mut Self>,
+574        cx: &mut Context<'_>,
+575        buf: &mut ReadBuf<'_>,
+576    ) -> Poll<io::Result<()>> {
+577        ready!(Self::poll_read_buf(self.get_mut(), cx, buf))?;
+578        Poll::Ready(Ok(()))
+579    }
+580}
+581
+582impl Drop for RecvStream {
+583    fn drop(&mut self) {
+584        let mut conn = self.conn.state.lock("RecvStream::drop");
+585
+586        // clean up any previously registered wakers
+587        conn.blocked_readers.remove(&self.stream);
+588
+589        if conn.error.is_some() || (self.is_0rtt && conn.check_0rtt().is_err()) {
+590            return;
+591        }
+592        if !self.all_data_read {
+593            // Ignore ClosedStream errors
+594            let _ = conn.inner.recv_stream(self.stream).stop(0u32.into());
+595            conn.wake();
+596        }
+597    }
+598}
+599
+600/// Errors that arise from reading from a stream.
+601#[derive(Debug, Error, Clone, PartialEq, Eq)]
+602pub enum ReadError {
+603    /// The peer abandoned transmitting data on this stream
+604    ///
+605    /// Carries an application-defined error code.
+606    #[error("stream reset by peer: error {0}")]
+607    Reset(VarInt),
+608    /// The connection was lost
+609    #[error("connection lost")]
+610    ConnectionLost(#[from] ConnectionError),
+611    /// The stream has already been stopped, finished, or reset
+612    #[error("closed stream")]
+613    ClosedStream,
+614    /// This was a 0-RTT stream and the server rejected it
+615    ///
+616    /// Can only occur on clients for 0-RTT streams, which can be opened using
+617    /// [`Connecting::into_0rtt()`].
+618    ///
+619    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+620    #[error("0-RTT rejected")]
+621    ZeroRttRejected,
+622}
+623
+624impl From<ResetError> for ReadError {
+625    fn from(e: ResetError) -> Self {
+626        match e {
+627            ResetError::ConnectionLost(e) => Self::ConnectionLost(e),
+628            ResetError::ZeroRttRejected => Self::ZeroRttRejected,
+629        }
+630    }
+631}
+632
+633impl From<ReadError> for io::Error {
+634    fn from(x: ReadError) -> Self {
+635        use ReadError::*;
+636        let kind = match x {
+637            Reset { .. } | ZeroRttRejected => io::ErrorKind::ConnectionReset,
+638            ConnectionLost(_) | ClosedStream => io::ErrorKind::NotConnected,
+639        };
+640        Self::new(kind, x)
+641    }
+642}
+643
+644/// Errors that arise while waiting for a stream to be reset
+645#[derive(Debug, Error, Clone, PartialEq, Eq)]
+646pub enum ResetError {
+647    /// The connection was lost
+648    #[error("connection lost")]
+649    ConnectionLost(#[from] ConnectionError),
+650    /// This was a 0-RTT stream and the server rejected it
+651    ///
+652    /// Can only occur on clients for 0-RTT streams, which can be opened using
+653    /// [`Connecting::into_0rtt()`].
+654    ///
+655    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+656    #[error("0-RTT rejected")]
+657    ZeroRttRejected,
+658}
+659
+660impl From<ResetError> for io::Error {
+661    fn from(x: ResetError) -> Self {
+662        use ResetError::*;
+663        let kind = match x {
+664            ZeroRttRejected => io::ErrorKind::ConnectionReset,
+665            ConnectionLost(_) => io::ErrorKind::NotConnected,
+666        };
+667        Self::new(kind, x)
+668    }
+669}
+670
+671/// Future produced by [`RecvStream::read()`].
+672///
+673/// [`RecvStream::read()`]: crate::RecvStream::read
+674struct Read<'a> {
+675    stream: &'a mut RecvStream,
+676    buf: ReadBuf<'a>,
+677}
+678
+679impl Future for Read<'_> {
+680    type Output = Result<Option<usize>, ReadError>;
+681
+682    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+683        let this = self.get_mut();
+684        ready!(this.stream.poll_read_buf(cx, &mut this.buf))?;
+685        match this.buf.filled().len() {
+686            0 if this.buf.capacity() != 0 => Poll::Ready(Ok(None)),
+687            n => Poll::Ready(Ok(Some(n))),
+688        }
+689    }
+690}
+691
+692/// Future produced by [`RecvStream::read_exact()`].
+693///
+694/// [`RecvStream::read_exact()`]: crate::RecvStream::read_exact
+695struct ReadExact<'a> {
+696    stream: &'a mut RecvStream,
+697    buf: ReadBuf<'a>,
+698}
+699
+700impl Future for ReadExact<'_> {
+701    type Output = Result<(), ReadExactError>;
+702    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+703        let this = self.get_mut();
+704        let mut remaining = this.buf.remaining();
+705        while remaining > 0 {
+706            ready!(this.stream.poll_read_buf(cx, &mut this.buf))?;
+707            let new = this.buf.remaining();
+708            if new == remaining {
+709                return Poll::Ready(Err(ReadExactError::FinishedEarly(this.buf.filled().len())));
+710            }
+711            remaining = new;
+712        }
+713        Poll::Ready(Ok(()))
+714    }
+715}
+716
+717/// Errors that arise from reading from a stream.
+718#[derive(Debug, Error, Clone, PartialEq, Eq)]
+719pub enum ReadExactError {
+720    /// The stream finished before all bytes were read
+721    #[error("stream finished early ({0} bytes read)")]
+722    FinishedEarly(usize),
+723    /// A read error occurred
+724    #[error(transparent)]
+725    ReadError(#[from] ReadError),
+726}
+727
+728/// Future produced by [`RecvStream::read_chunk()`].
+729///
+730/// [`RecvStream::read_chunk()`]: crate::RecvStream::read_chunk
+731struct ReadChunk<'a> {
+732    stream: &'a mut RecvStream,
+733    max_length: usize,
+734    ordered: bool,
+735}
+736
+737impl Future for ReadChunk<'_> {
+738    type Output = Result<Option<Chunk>, ReadError>;
+739    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+740        let (max_length, ordered) = (self.max_length, self.ordered);
+741        self.stream.poll_read_chunk(cx, max_length, ordered)
+742    }
+743}
+744
+745/// Future produced by [`RecvStream::read_chunks()`].
+746///
+747/// [`RecvStream::read_chunks()`]: crate::RecvStream::read_chunks
+748struct ReadChunks<'a> {
+749    stream: &'a mut RecvStream,
+750    bufs: &'a mut [Bytes],
+751}
+752
+753impl Future for ReadChunks<'_> {
+754    type Output = Result<Option<usize>, ReadError>;
+755    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+756        let this = self.get_mut();
+757        this.stream.poll_read_chunks(cx, this.bufs)
+758    }
+759}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn/runtime/mod.rs.html b/pr/395/docs/src/iroh_quinn/runtime/mod.rs.html new file mode 100644 index 000000000..20b0650f5 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn/runtime/mod.rs.html @@ -0,0 +1,251 @@ +mod.rs - source

iroh_quinn/runtime/
mod.rs

1#[cfg(any(feature = "runtime-tokio", feature = "runtime-smol"))]
+2use std::sync::Arc;
+3use std::{
+4    fmt::{self, Debug},
+5    future::Future,
+6    io::{self, IoSliceMut},
+7    net::SocketAddr,
+8    num::NonZeroUsize,
+9    pin::Pin,
+10    task::{Context, Poll},
+11};
+12
+13use udp::{RecvMeta, Transmit};
+14
+15use crate::Instant;
+16
+17/// Abstracts I/O and timer operations for runtime independence
+18pub trait Runtime: Send + Sync + Debug + 'static {
+19    /// Construct a timer that will expire at `i`
+20    fn new_timer(&self, i: Instant) -> Pin<Box<dyn AsyncTimer>>;
+21    /// Drive `future` to completion in the background
+22    #[track_caller]
+23    fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>);
+24    /// Convert `t` into the socket type used by this runtime
+25    #[cfg(not(wasm_browser))]
+26    fn wrap_udp_socket(&self, t: std::net::UdpSocket) -> io::Result<Box<dyn AsyncUdpSocket>>;
+27    /// Look up the current time
+28    ///
+29    /// Allows simulating the flow of time for testing.
+30    fn now(&self) -> Instant {
+31        Instant::now()
+32    }
+33}
+34
+35/// Abstract implementation of an async timer for runtime independence
+36pub trait AsyncTimer: Send + Debug + 'static {
+37    /// Update the timer to expire at `i`
+38    fn reset(self: Pin<&mut Self>, i: Instant);
+39    /// Check whether the timer has expired, and register to be woken if not
+40    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()>;
+41}
+42
+43/// Abstract implementation of a UDP socket for runtime independence
+44pub trait AsyncUdpSocket: Send + Sync + Debug + 'static {
+45    /// Create a [`UdpSender`] that can register a single task for write-readiness notifications
+46    /// and send a transmit, if ready.
+47    ///
+48    /// A `poll_send` method on a single object can usually store only one [`Waker`] at a time,
+49    /// i.e. allow at most one caller to wait for an event. This method allows any number of
+50    /// interested tasks to construct their own [`UdpSender`] object. They can all then wait for the
+51    /// same event and be notified concurrently, because each [`UdpSender`] can store a separate
+52    /// [`Waker`].
+53    ///
+54    /// [`Waker`]: std::task::Waker
+55    fn create_sender(&self) -> Pin<Box<dyn UdpSender>>;
+56
+57    /// Receive UDP datagrams, or register to be woken if receiving may succeed in the future
+58    fn poll_recv(
+59        &mut self,
+60        cx: &mut Context<'_>,
+61        bufs: &mut [IoSliceMut<'_>],
+62        meta: &mut [RecvMeta],
+63    ) -> Poll<io::Result<usize>>;
+64
+65    /// Look up the local IP address and port used by this socket
+66    fn local_addr(&self) -> io::Result<SocketAddr>;
+67
+68    /// Maximum number of datagrams that might be described by a single [`RecvMeta`]
+69    fn max_receive_segments(&self) -> NonZeroUsize {
+70        NonZeroUsize::MIN
+71    }
+72
+73    /// Whether datagrams might get fragmented into multiple parts
+74    ///
+75    /// Sockets should prevent this for best performance. See e.g. the `IPV6_DONTFRAG` socket
+76    /// option.
+77    fn may_fragment(&self) -> bool {
+78        true
+79    }
+80}
+81
+82/// An object for asynchronously writing to an associated [`AsyncUdpSocket`].
+83///
+84/// Any number of [`UdpSender`]s may exist for a single [`AsyncUdpSocket`]. Each [`UdpSender`] is
+85/// responsible for notifying at most one task for send readiness.
+86pub trait UdpSender: Send + Sync + Debug + 'static {
+87    /// Send a UDP datagram, or register to be woken if sending may succeed in the future.
+88    ///
+89    /// Usually implementations of this will poll the socket for writability before trying to
+90    /// write to them, and retry both if writing fails.
+91    ///
+92    /// Quinn will create multiple [`UdpSender`]s, one for each task it's using it from. Thus it's
+93    /// important to poll the underlying socket in a way that doesn't overwrite wakers.
+94    ///
+95    /// A single [`UdpSender`] will be reused, even if `poll_send` returns `Poll::Ready` once,
+96    /// unlike [`Future::poll`], so calling it again after readiness should not panic.
+97    fn poll_send(
+98        self: Pin<&mut Self>,
+99        transmit: &Transmit<'_>,
+100        cx: &mut Context<'_>,
+101    ) -> Poll<io::Result<()>>;
+102
+103    /// Maximum number of datagrams that a [`Transmit`] may encode.
+104    fn max_transmit_segments(&self) -> NonZeroUsize {
+105        NonZeroUsize::MIN
+106    }
+107}
+108
+109pin_project_lite::pin_project! {
+110    /// A helper for constructing [`UdpSender`]s from an underlying `Socket` type.
+111    ///
+112    /// This struct implements [`UdpSender`] if `MakeWritableFn` produces a `WritableFut`.
+113    ///
+114    /// Also serves as a trick, since `WritableFut` doesn't need to be a named future,
+115    /// it can be an anonymous async block, as long as `MakeWritableFn` produces that
+116    /// anonymous async block type.
+117    ///
+118    /// The `UdpSenderHelper` generic type parameters don't need to named, as it will be
+119    /// used in its dyn-compatible form as a `Pin<Box<dyn UdpSender>>`.
+120    struct UdpSenderHelper<Socket, MakeWritableFutFn, WritableFut> {
+121        socket: Socket,
+122        make_writable_fut_fn: MakeWritableFutFn,
+123        #[pin]
+124        writable_fut: Option<WritableFut>,
+125    }
+126}
+127
+128impl<Socket, MakeWritableFutFn, WritableFut> Debug
+129    for UdpSenderHelper<Socket, MakeWritableFutFn, WritableFut>
+130{
+131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+132        f.write_str("UdpSender")
+133    }
+134}
+135
+136impl<Socket, MakeWritableFutFn, WriteableFut>
+137    UdpSenderHelper<Socket, MakeWritableFutFn, WriteableFut>
+138{
+139    /// Create helper that implements [`UdpSender`] from a socket.
+140    ///
+141    /// Additionally you need to provide what is essentially an async function
+142    /// that resolves once the socket is write-ready.
+143    ///
+144    /// See also the bounds on this struct's [`UdpSender`] implementation.
+145    #[cfg(any(feature = "runtime-smol", feature = "runtime-tokio",))]
+146    fn new(inner: Socket, make_fut: MakeWritableFutFn) -> Self {
+147        Self {
+148            socket: inner,
+149            make_writable_fut_fn: make_fut,
+150            writable_fut: None,
+151        }
+152    }
+153}
+154
+155impl<Socket, MakeWritableFutFn, WritableFut> super::UdpSender
+156    for UdpSenderHelper<Socket, MakeWritableFutFn, WritableFut>
+157where
+158    Socket: UdpSenderHelperSocket,
+159    MakeWritableFutFn: Fn(&Socket) -> WritableFut + Send + Sync + 'static,
+160    WritableFut: Future<Output = io::Result<()>> + Send + Sync + 'static,
+161{
+162    fn poll_send(
+163        self: Pin<&mut Self>,
+164        transmit: &udp::Transmit<'_>,
+165        cx: &mut Context<'_>,
+166    ) -> Poll<io::Result<()>> {
+167        let mut this = self.project();
+168        loop {
+169            if this.writable_fut.is_none() {
+170                this.writable_fut
+171                    .set(Some((this.make_writable_fut_fn)(this.socket)));
+172            }
+173            // We're forced to `unwrap` here because `Fut` may be `!Unpin`, which means we can't safely
+174            // obtain an `&mut WritableFut` after storing it in `self.writable_fut` when `self` is already behind `Pin`,
+175            // and if we didn't store it then we wouldn't be able to keep it alive between
+176            // `poll_send` calls.
+177            let result =
+178                std::task::ready!(this.writable_fut.as_mut().as_pin_mut().unwrap().poll(cx));
+179
+180            // Polling an arbitrary `Future` after it becomes ready is a logic error, so arrange for
+181            // a new `Future` to be created on the next call.
+182            this.writable_fut.set(None);
+183
+184            // If .writable() fails, propagate the error
+185            result?;
+186
+187            match this.socket.try_send(transmit) {
+188                // We thought the socket was writable, but it wasn't, then retry so that either another
+189                // `writable().await` call determines that the socket is indeed not writable and
+190                // registers us for a wakeup, or the send succeeds if this really was just a
+191                // transient failure.
+192                Err(e) if e.kind() == io::ErrorKind::WouldBlock => continue,
+193                // In all other cases, either propagate the error or we're Ok
+194                result => return Poll::Ready(result),
+195            }
+196        }
+197    }
+198
+199    fn max_transmit_segments(&self) -> NonZeroUsize {
+200        self.socket.max_transmit_segments()
+201    }
+202}
+203
+204/// Parts of the [`UdpSender`] trait that aren't asynchronous or require storing wakers.
+205///
+206/// This trait is used by [`UdpSenderHelper`] to help construct [`UdpSender`]s.
+207trait UdpSenderHelperSocket: Send + Sync + 'static {
+208    /// Try to send a transmit, if the socket happens to be write-ready.
+209    ///
+210    /// If not write-ready, this is allowed to return [`std::io::ErrorKind::WouldBlock`].
+211    ///
+212    /// The [`UdpSenderHelper`] will use this to implement [`UdpSender::poll_send`].
+213    fn try_send(&self, transmit: &udp::Transmit<'_>) -> io::Result<()>;
+214
+215    /// See [`UdpSender::max_transmit_segments`].
+216    fn max_transmit_segments(&self) -> NonZeroUsize;
+217}
+218
+219/// Automatically select an appropriate runtime from those enabled at compile time
+220///
+221/// If `runtime-tokio` is enabled and this function is called from within a Tokio runtime context,
+222/// then `TokioRuntime` is returned. Otherwise, if `runtime-smol` is enabled, `SmolRuntime` is
+223/// returned. Otherwise, `None` is returned.
+224#[cfg(any(feature = "runtime-tokio", feature = "runtime-smol"))]
+225#[allow(clippy::needless_return)] // Be sure we return the right thing
+226pub fn default_runtime() -> Option<Arc<dyn Runtime>> {
+227    #[cfg(feature = "runtime-tokio")]
+228    {
+229        if ::tokio::runtime::Handle::try_current().is_ok() {
+230            return Some(Arc::new(TokioRuntime));
+231        }
+232    }
+233
+234    #[cfg(feature = "runtime-smol")]
+235    {
+236        return Some(Arc::new(SmolRuntime));
+237    }
+238
+239    #[cfg(not(feature = "runtime-smol"))]
+240    None
+241}
+242
+243#[cfg(feature = "runtime-tokio")]
+244mod tokio;
+245#[cfg(feature = "runtime-tokio")]
+246pub use tokio::TokioRuntime;
+247
+248#[cfg(feature = "runtime-smol")]
+249mod smol;
+250#[cfg(feature = "runtime-smol")]
+251pub use smol::*;
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn/runtime/smol.rs.html b/pr/395/docs/src/iroh_quinn/runtime/smol.rs.html new file mode 100644 index 000000000..61fb58950 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn/runtime/smol.rs.html @@ -0,0 +1,102 @@ +smol.rs - source

iroh_quinn/runtime/
smol.rs

1use std::{
+2    future::Future,
+3    num::NonZeroUsize,
+4    pin::Pin,
+5    task::{Context, Poll},
+6    time::Instant,
+7};
+8use std::{io, sync::Arc, task::ready};
+9
+10use async_io::Async;
+11use async_io::Timer;
+12
+13use super::AsyncTimer;
+14use super::{AsyncUdpSocket, Runtime, UdpSender, UdpSenderHelper, UdpSenderHelperSocket};
+15
+16/// A Quinn runtime for smol
+17#[derive(Debug)]
+18pub struct SmolRuntime;
+19
+20impl Runtime for SmolRuntime {
+21    fn new_timer(&self, t: Instant) -> Pin<Box<dyn AsyncTimer>> {
+22        Box::pin(Timer::at(t))
+23    }
+24
+25    fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>) {
+26        ::smol::spawn(future).detach();
+27    }
+28
+29    fn wrap_udp_socket(&self, sock: std::net::UdpSocket) -> io::Result<Box<dyn AsyncUdpSocket>> {
+30        Ok(Box::new(UdpSocket::new(sock)?))
+31    }
+32}
+33
+34impl AsyncTimer for Timer {
+35    fn reset(mut self: Pin<&mut Self>, t: Instant) {
+36        self.set_at(t)
+37    }
+38
+39    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
+40        Future::poll(self, cx).map(|_| ())
+41    }
+42}
+43
+44#[derive(Debug, Clone)]
+45struct UdpSocket {
+46    io: Arc<Async<std::net::UdpSocket>>,
+47    inner: Arc<udp::UdpSocketState>,
+48}
+49
+50impl UdpSocket {
+51    fn new(sock: std::net::UdpSocket) -> io::Result<Self> {
+52        Ok(Self {
+53            inner: Arc::new(udp::UdpSocketState::new((&sock).into())?),
+54            io: Arc::new(Async::new_nonblocking(sock)?),
+55        })
+56    }
+57}
+58
+59impl UdpSenderHelperSocket for UdpSocket {
+60    fn max_transmit_segments(&self) -> NonZeroUsize {
+61        self.inner.max_gso_segments()
+62    }
+63
+64    fn try_send(&self, transmit: &udp::Transmit<'_>) -> io::Result<()> {
+65        self.inner.send((&self.io).into(), transmit)
+66    }
+67}
+68
+69impl AsyncUdpSocket for UdpSocket {
+70    fn create_sender(&self) -> Pin<Box<dyn UdpSender>> {
+71        Box::pin(UdpSenderHelper::new(self.clone(), |socket: &Self| {
+72            let socket = socket.clone();
+73            async move { socket.io.writable().await }
+74        }))
+75    }
+76
+77    fn poll_recv(
+78        &mut self,
+79        cx: &mut Context<'_>,
+80        bufs: &mut [io::IoSliceMut<'_>],
+81        meta: &mut [udp::RecvMeta],
+82    ) -> Poll<io::Result<usize>> {
+83        loop {
+84            ready!(self.io.poll_readable(cx))?;
+85            if let Ok(res) = self.inner.recv((&self.io).into(), bufs, meta) {
+86                return Poll::Ready(Ok(res));
+87            }
+88        }
+89    }
+90
+91    fn local_addr(&self) -> io::Result<std::net::SocketAddr> {
+92        self.io.as_ref().as_ref().local_addr()
+93    }
+94
+95    fn may_fragment(&self) -> bool {
+96        self.inner.may_fragment()
+97    }
+98
+99    fn max_receive_segments(&self) -> NonZeroUsize {
+100        self.inner.gro_segments()
+101    }
+102}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn/runtime/tokio.rs.html b/pr/395/docs/src/iroh_quinn/runtime/tokio.rs.html new file mode 100644 index 000000000..a981b3445 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn/runtime/tokio.rs.html @@ -0,0 +1,106 @@ +tokio.rs - source

iroh_quinn/runtime/
tokio.rs

1use std::{
+2    fmt::Debug,
+3    future::Future,
+4    io,
+5    num::NonZeroUsize,
+6    pin::Pin,
+7    sync::Arc,
+8    task::{Context, Poll, ready},
+9    time::Instant,
+10};
+11
+12use tokio::{
+13    io::Interest,
+14    time::{Sleep, sleep_until},
+15};
+16
+17use super::{AsyncTimer, AsyncUdpSocket, Runtime, UdpSenderHelper, UdpSenderHelperSocket};
+18
+19/// A Quinn runtime for Tokio
+20#[derive(Debug)]
+21pub struct TokioRuntime;
+22
+23impl Runtime for TokioRuntime {
+24    fn new_timer(&self, t: Instant) -> Pin<Box<dyn AsyncTimer>> {
+25        Box::pin(sleep_until(t.into()))
+26    }
+27
+28    fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>) {
+29        tokio::spawn(future);
+30    }
+31
+32    fn wrap_udp_socket(&self, sock: std::net::UdpSocket) -> io::Result<Box<dyn AsyncUdpSocket>> {
+33        Ok(Box::new(UdpSocket {
+34            inner: Arc::new(udp::UdpSocketState::new((&sock).into())?),
+35            io: Arc::new(tokio::net::UdpSocket::from_std(sock)?),
+36        }))
+37    }
+38
+39    fn now(&self) -> Instant {
+40        tokio::time::Instant::now().into_std()
+41    }
+42}
+43
+44impl AsyncTimer for Sleep {
+45    fn reset(self: Pin<&mut Self>, t: Instant) {
+46        Self::reset(self, t.into())
+47    }
+48    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
+49        Future::poll(self, cx)
+50    }
+51}
+52
+53#[derive(Debug, Clone)]
+54struct UdpSocket {
+55    io: Arc<tokio::net::UdpSocket>,
+56    inner: Arc<udp::UdpSocketState>,
+57}
+58
+59impl UdpSenderHelperSocket for UdpSocket {
+60    fn max_transmit_segments(&self) -> NonZeroUsize {
+61        self.inner.max_gso_segments()
+62    }
+63
+64    fn try_send(&self, transmit: &udp::Transmit<'_>) -> io::Result<()> {
+65        self.io.try_io(Interest::WRITABLE, || {
+66            self.inner.send((&self.io).into(), transmit)
+67        })
+68    }
+69}
+70
+71impl AsyncUdpSocket for UdpSocket {
+72    fn create_sender(&self) -> Pin<Box<dyn super::UdpSender>> {
+73        Box::pin(UdpSenderHelper::new(self.clone(), |socket: &Self| {
+74            let socket = socket.clone();
+75            async move { socket.io.writable().await }
+76        }))
+77    }
+78
+79    fn poll_recv(
+80        &mut self,
+81        cx: &mut Context<'_>,
+82        bufs: &mut [std::io::IoSliceMut<'_>],
+83        meta: &mut [udp::RecvMeta],
+84    ) -> Poll<io::Result<usize>> {
+85        loop {
+86            ready!(self.io.poll_recv_ready(cx))?;
+87            if let Ok(res) = self.io.try_io(Interest::READABLE, || {
+88                self.inner.recv((&self.io).into(), bufs, meta)
+89            }) {
+90                return Poll::Ready(Ok(res));
+91            }
+92        }
+93    }
+94
+95    fn local_addr(&self) -> io::Result<std::net::SocketAddr> {
+96        self.io.local_addr()
+97    }
+98
+99    fn may_fragment(&self) -> bool {
+100        self.inner.may_fragment()
+101    }
+102
+103    fn max_receive_segments(&self) -> NonZeroUsize {
+104        self.inner.gro_segments()
+105    }
+106}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn/send_stream.rs.html b/pr/395/docs/src/iroh_quinn/send_stream.rs.html new file mode 100644 index 000000000..0cbbea68f --- /dev/null +++ b/pr/395/docs/src/iroh_quinn/send_stream.rs.html @@ -0,0 +1,451 @@ +send_stream.rs - source

iroh_quinn/
send_stream.rs

1use std::{
+2    future::{Future, poll_fn},
+3    io,
+4    pin::{Pin, pin},
+5    task::{Context, Poll},
+6};
+7
+8use bytes::Bytes;
+9use proto::{ClosedStream, ConnectionError, FinishError, StreamId, Written};
+10use thiserror::Error;
+11
+12use crate::{
+13    VarInt,
+14    connection::{ConnectionRef, State},
+15};
+16
+17/// A stream that can only be used to send data
+18///
+19/// If dropped, streams that haven't been explicitly [`reset()`] will be implicitly [`finish()`]ed,
+20/// continuing to (re)transmit previously written data until it has been fully acknowledged or the
+21/// connection is closed.
+22///
+23/// # Cancellation
+24///
+25/// A `write` method is said to be *cancel-safe* when dropping its future before the future becomes
+26/// ready will always result in no data being written to the stream. This is true of methods which
+27/// succeed immediately when any progress is made, and is not true of methods which might need to
+28/// perform multiple writes internally before succeeding. Each `write` method documents whether it is
+29/// cancel-safe.
+30///
+31/// [`reset()`]: SendStream::reset
+32/// [`finish()`]: SendStream::finish
+33#[derive(Debug)]
+34pub struct SendStream {
+35    conn: ConnectionRef,
+36    stream: StreamId,
+37    is_0rtt: bool,
+38}
+39
+40impl SendStream {
+41    pub(crate) fn new(conn: ConnectionRef, stream: StreamId, is_0rtt: bool) -> Self {
+42        Self {
+43            conn,
+44            stream,
+45            is_0rtt,
+46        }
+47    }
+48
+49    /// Write a buffer into this stream, returning how many bytes were written
+50    ///
+51    /// Unless this method errors, it waits until some amount of `buf` can be written into this
+52    /// stream, and then writes as much as it can without waiting again. Due to congestion and flow
+53    /// control, this may be shorter than `buf.len()`. On success this yields the length of the
+54    /// prefix that was written.
+55    ///
+56    /// # Cancel safety
+57    ///
+58    /// This method is cancellation safe. If this does not resolve, no bytes were written.
+59    pub async fn write(&mut self, buf: &[u8]) -> Result<usize, WriteError> {
+60        poll_fn(|cx| self.execute_poll(cx, |s| s.write(buf))).await
+61    }
+62
+63    /// Write a buffer into this stream in its entirety
+64    ///
+65    /// This method repeatedly calls [`write`](Self::write) until all bytes are written, or an
+66    /// error occurs.
+67    ///
+68    /// # Cancel safety
+69    ///
+70    /// This method is *not* cancellation safe. Even if this does not resolve, some prefix of `buf`
+71    /// may have been written when previously polled.
+72    pub async fn write_all(&mut self, mut buf: &[u8]) -> Result<(), WriteError> {
+73        while !buf.is_empty() {
+74            let written = self.write(buf).await?;
+75            buf = &buf[written..];
+76        }
+77        Ok(())
+78    }
+79
+80    /// Write a slice of [`Bytes`] into this stream, returning how much was written
+81    ///
+82    /// Bytes to try to write are provided to this method as an array of cheaply cloneable chunks.
+83    /// Unless this method errors, it waits until some amount of those bytes can be written into
+84    /// this stream, and then writes as much as it can without waiting again. Due to congestion and
+85    /// flow control, this may be less than the total number of bytes.
+86    ///
+87    /// On success, this method both mutates `bufs` and yields an informative [`Written`] struct
+88    /// indicating how much was written:
+89    ///
+90    /// - [`Bytes`] chunks that were fully written are mutated to be [empty](Bytes::is_empty).
+91    /// - If a [`Bytes`] chunk was partially written, it is [split to](Bytes::split_to) contain
+92    ///   only the suffix of bytes that were not written.
+93    /// - The yielded [`Written`] struct indicates how many chunks were fully written as well as
+94    ///   how many bytes were written.
+95    ///
+96    /// # Cancel safety
+97    ///
+98    /// This method is cancellation safe. If this does not resolve, no bytes were written.
+99    pub async fn write_chunks(&mut self, bufs: &mut [Bytes]) -> Result<Written, WriteError> {
+100        poll_fn(|cx| self.execute_poll(cx, |s| s.write_chunks(bufs))).await
+101    }
+102
+103    /// Write a single [`Bytes`] into this stream in its entirety
+104    ///
+105    /// Bytes to write are provided to this method as an single cheaply cloneable chunk. This
+106    /// method repeatedly calls [`write_chunks`](Self::write_chunks) until all bytes are written,
+107    /// or an error occurs.
+108    ///
+109    /// # Cancel safety
+110    ///
+111    /// This method is *not* cancellation safe. Even if this does not resolve, some bytes may have
+112    /// been written when previously polled.
+113    pub async fn write_chunk(&mut self, buf: Bytes) -> Result<(), WriteError> {
+114        self.write_all_chunks(&mut [buf]).await?;
+115        Ok(())
+116    }
+117
+118    /// Write a slice of [`Bytes`] into this stream in its entirety
+119    ///
+120    /// Bytes to write are provided to this method as an array of cheaply cloneable chunks. This
+121    /// method repeatedly calls [`write_chunks`](Self::write_chunks) until all bytes are written,
+122    /// or an error occurs. This method mutates `bufs` by mutating all chunks to be
+123    /// [empty](Bytes::is_empty).
+124    ///
+125    /// # Cancel safety
+126    ///
+127    /// This method is *not* cancellation safe. Even if this does not resolve, some bytes may have
+128    /// been written when previously polled.
+129    pub async fn write_all_chunks(&mut self, mut bufs: &mut [Bytes]) -> Result<(), WriteError> {
+130        while !bufs.is_empty() {
+131            let written = self.write_chunks(bufs).await?;
+132            bufs = &mut bufs[written.chunks..];
+133        }
+134        Ok(())
+135    }
+136
+137    fn execute_poll<F, R>(
+138        &mut self,
+139        cx: &mut Context<'_>,
+140        write_fn: F,
+141    ) -> Poll<Result<R, WriteError>>
+142    where
+143        F: FnOnce(&mut proto::SendStream<'_>) -> Result<R, proto::WriteError>,
+144    {
+145        use proto::WriteError::*;
+146        let mut conn = self.conn.state.lock("SendStream::poll_write");
+147        if self.is_0rtt {
+148            conn.check_0rtt()
+149                .map_err(|()| WriteError::ZeroRttRejected)?;
+150        }
+151        if let Some(ref x) = conn.error {
+152            return Poll::Ready(Err(WriteError::ConnectionLost(x.clone())));
+153        }
+154
+155        let result = match write_fn(&mut conn.inner.send_stream(self.stream)) {
+156            Ok(result) => result,
+157            Err(Blocked) => {
+158                conn.blocked_writers.insert(self.stream, cx.waker().clone());
+159                return Poll::Pending;
+160            }
+161            Err(Stopped(error_code)) => {
+162                return Poll::Ready(Err(WriteError::Stopped(error_code)));
+163            }
+164            Err(ClosedStream) => {
+165                return Poll::Ready(Err(WriteError::ClosedStream));
+166            }
+167        };
+168
+169        conn.wake();
+170        Poll::Ready(Ok(result))
+171    }
+172
+173    /// Notify the peer that no more data will ever be written to this stream
+174    ///
+175    /// It is an error to write to a [`SendStream`] after `finish()`ing it. [`reset()`](Self::reset)
+176    /// may still be called after `finish` to abandon transmission of any stream data that might
+177    /// still be buffered.
+178    ///
+179    /// To wait for the peer to receive all buffered stream data, see [`stopped()`](Self::stopped).
+180    ///
+181    /// May fail if [`finish()`](Self::finish) or [`reset()`](Self::reset) was previously
+182    /// called. This error is harmless and serves only to indicate that the caller may have
+183    /// incorrect assumptions about the stream's state.
+184    pub fn finish(&mut self) -> Result<(), ClosedStream> {
+185        let mut conn = self.conn.state.lock("finish");
+186        match conn.inner.send_stream(self.stream).finish() {
+187            Ok(()) => {
+188                conn.wake();
+189                Ok(())
+190            }
+191            Err(FinishError::ClosedStream) => Err(ClosedStream::default()),
+192            // Harmless. If the application needs to know about stopped streams at this point, it
+193            // should call `stopped`.
+194            Err(FinishError::Stopped(_)) => Ok(()),
+195        }
+196    }
+197
+198    /// Close the send stream immediately.
+199    ///
+200    /// No new data can be written after calling this method. Locally buffered data is dropped, and
+201    /// previously transmitted data will no longer be retransmitted if lost. If an attempt has
+202    /// already been made to finish the stream, the peer may still receive all written data.
+203    ///
+204    /// May fail if [`finish()`](Self::finish) or [`reset()`](Self::reset) was previously
+205    /// called. This error is harmless and serves only to indicate that the caller may have
+206    /// incorrect assumptions about the stream's state.
+207    pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+208        let mut conn = self.conn.state.lock("SendStream::reset");
+209        if self.is_0rtt && conn.check_0rtt().is_err() {
+210            return Ok(());
+211        }
+212        conn.inner.send_stream(self.stream).reset(error_code)?;
+213        conn.wake();
+214        Ok(())
+215    }
+216
+217    /// Set the priority of the send stream
+218    ///
+219    /// Every send stream has an initial priority of 0. Locally buffered data from streams with
+220    /// higher priority will be transmitted before data from streams with lower priority. Changing
+221    /// the priority of a stream with pending data may only take effect after that data has been
+222    /// transmitted. Using many different priority levels per connection may have a negative
+223    /// impact on performance.
+224    pub fn set_priority(&self, priority: i32) -> Result<(), ClosedStream> {
+225        let mut conn = self.conn.state.lock("SendStream::set_priority");
+226        conn.inner.send_stream(self.stream).set_priority(priority)?;
+227        Ok(())
+228    }
+229
+230    /// Get the priority of the send stream
+231    pub fn priority(&self) -> Result<i32, ClosedStream> {
+232        let mut conn = self.conn.state.lock("SendStream::priority");
+233        conn.inner.send_stream(self.stream).priority()
+234    }
+235
+236    /// Completes when the peer stops the stream or reads the stream to completion
+237    ///
+238    /// Yields `Some` with the stop error code if the peer stops the stream. Yields `None` if the
+239    /// local side [`finish()`](Self::finish)es the stream and then the peer acknowledges receipt
+240    /// of all stream data (although not necessarily the processing of it), after which the peer
+241    /// closing the stream is no longer meaningful.
+242    ///
+243    /// For a variety of reasons, the peer may not send acknowledgements immediately upon receiving
+244    /// data. As such, relying on `stopped` to know when the peer has read a stream to completion
+245    /// may introduce more latency than using an application-level response of some sort.
+246    pub fn stopped(
+247        &self,
+248    ) -> impl Future<Output = Result<Option<VarInt>, StoppedError>> + Send + Sync + 'static {
+249        let conn = self.conn.clone();
+250        let stream = self.stream;
+251        let is_0rtt = self.is_0rtt;
+252        async move {
+253            loop {
+254                // The `Notify::notified` future needs to be created while the lock is being held,
+255                // otherwise a wakeup could be missed if triggered in between releasing the lock
+256                // and creating the future.
+257                // The lock may only be held in a block without `await`s, otherwise the future
+258                // becomes `!Send`. `Notify::notified` is lifetime-bound to `Notify`, therefore
+259                // we need to declare `notify` outside of the block, and initialize it inside.
+260                let notify;
+261                {
+262                    let mut conn = conn.state.lock("SendStream::stopped");
+263                    if let Some(output) = send_stream_stopped(&mut conn, stream, is_0rtt) {
+264                        return output;
+265                    }
+266
+267                    notify = conn.stopped.entry(stream).or_default().clone();
+268                    notify.notified()
+269                }
+270                .await
+271            }
+272        }
+273    }
+274
+275    /// Get the identity of this stream
+276    pub fn id(&self) -> StreamId {
+277        self.stream
+278    }
+279
+280    /// Attempt to write bytes from buf into the stream.
+281    ///
+282    /// On success, returns Poll::Ready(Ok(num_bytes_written)).
+283    ///
+284    /// If the stream is not ready for writing, the method returns Poll::Pending and arranges
+285    /// for the current task (via cx.waker().wake_by_ref()) to receive a notification when the
+286    /// stream becomes writable or is closed.
+287    pub fn poll_write(
+288        self: Pin<&mut Self>,
+289        cx: &mut Context<'_>,
+290        buf: &[u8],
+291    ) -> Poll<Result<usize, WriteError>> {
+292        pin!(self.get_mut().write(buf)).as_mut().poll(cx)
+293    }
+294}
+295
+296/// Check if a send stream is stopped.
+297///
+298/// Returns `Some` if the stream is stopped or the connection is closed.
+299/// Returns `None` if the stream is not stopped.
+300fn send_stream_stopped(
+301    conn: &mut State,
+302    stream: StreamId,
+303    is_0rtt: bool,
+304) -> Option<Result<Option<VarInt>, StoppedError>> {
+305    if is_0rtt && conn.check_0rtt().is_err() {
+306        return Some(Err(StoppedError::ZeroRttRejected));
+307    }
+308    match conn.inner.send_stream(stream).stopped() {
+309        Err(ClosedStream { .. }) => Some(Ok(None)),
+310        Ok(Some(error_code)) => Some(Ok(Some(error_code))),
+311        Ok(None) => conn.error.clone().map(|error| Err(error.into())),
+312    }
+313}
+314
+315#[cfg(feature = "futures-io")]
+316impl futures_io::AsyncWrite for SendStream {
+317    fn poll_write(
+318        self: Pin<&mut Self>,
+319        cx: &mut Context<'_>,
+320        buf: &[u8],
+321    ) -> Poll<io::Result<usize>> {
+322        self.poll_write(cx, buf).map_err(Into::into)
+323    }
+324
+325    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+326        Poll::Ready(Ok(()))
+327    }
+328
+329    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+330        Poll::Ready(self.get_mut().finish().map_err(Into::into))
+331    }
+332}
+333
+334impl tokio::io::AsyncWrite for SendStream {
+335    fn poll_write(
+336        self: Pin<&mut Self>,
+337        cx: &mut Context<'_>,
+338        buf: &[u8],
+339    ) -> Poll<io::Result<usize>> {
+340        self.poll_write(cx, buf).map_err(Into::into)
+341    }
+342
+343    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+344        Poll::Ready(Ok(()))
+345    }
+346
+347    fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+348        Poll::Ready(self.get_mut().finish().map_err(Into::into))
+349    }
+350}
+351
+352impl Drop for SendStream {
+353    fn drop(&mut self) {
+354        let mut conn = self.conn.state.lock("SendStream::drop");
+355
+356        // clean up any previously registered wakers
+357        conn.blocked_writers.remove(&self.stream);
+358
+359        if conn.error.is_some() || (self.is_0rtt && conn.check_0rtt().is_err()) {
+360            return;
+361        }
+362        match conn.inner.send_stream(self.stream).finish() {
+363            Ok(()) => conn.wake(),
+364            Err(FinishError::Stopped(reason)) => {
+365                if conn.inner.send_stream(self.stream).reset(reason).is_ok() {
+366                    conn.wake();
+367                }
+368            }
+369            // Already finished or reset, which is fine.
+370            Err(FinishError::ClosedStream) => {}
+371        }
+372    }
+373}
+374
+375/// Errors that arise from writing to a stream
+376#[derive(Debug, Error, Clone, PartialEq, Eq)]
+377pub enum WriteError {
+378    /// The peer is no longer accepting data on this stream
+379    ///
+380    /// Carries an application-defined error code.
+381    #[error("sending stopped by peer: error {0}")]
+382    Stopped(VarInt),
+383    /// The connection was lost
+384    #[error("connection lost")]
+385    ConnectionLost(#[from] ConnectionError),
+386    /// The stream has already been finished or reset
+387    #[error("closed stream")]
+388    ClosedStream,
+389    /// This was a 0-RTT stream and the server rejected it
+390    ///
+391    /// Can only occur on clients for 0-RTT streams, which can be opened using
+392    /// [`Connecting::into_0rtt()`].
+393    ///
+394    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+395    #[error("0-RTT rejected")]
+396    ZeroRttRejected,
+397}
+398
+399impl From<ClosedStream> for WriteError {
+400    #[inline]
+401    fn from(_: ClosedStream) -> Self {
+402        Self::ClosedStream
+403    }
+404}
+405
+406impl From<StoppedError> for WriteError {
+407    fn from(x: StoppedError) -> Self {
+408        match x {
+409            StoppedError::ConnectionLost(e) => Self::ConnectionLost(e),
+410            StoppedError::ZeroRttRejected => Self::ZeroRttRejected,
+411        }
+412    }
+413}
+414
+415impl From<WriteError> for io::Error {
+416    fn from(x: WriteError) -> Self {
+417        use WriteError::*;
+418        let kind = match x {
+419            Stopped(_) | ZeroRttRejected => io::ErrorKind::ConnectionReset,
+420            ConnectionLost(_) | ClosedStream => io::ErrorKind::NotConnected,
+421        };
+422        Self::new(kind, x)
+423    }
+424}
+425
+426/// Errors that arise while monitoring for a send stream stop from the peer
+427#[derive(Debug, Error, Clone, PartialEq, Eq)]
+428pub enum StoppedError {
+429    /// The connection was lost
+430    #[error("connection lost")]
+431    ConnectionLost(#[from] ConnectionError),
+432    /// This was a 0-RTT stream and the server rejected it
+433    ///
+434    /// Can only occur on clients for 0-RTT streams, which can be opened using
+435    /// [`Connecting::into_0rtt()`].
+436    ///
+437    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+438    #[error("0-RTT rejected")]
+439    ZeroRttRejected,
+440}
+441
+442impl From<StoppedError> for io::Error {
+443    fn from(x: StoppedError) -> Self {
+444        use StoppedError::*;
+445        let kind = match x {
+446            ZeroRttRejected => io::ErrorKind::ConnectionReset,
+447            ConnectionLost(_) => io::ErrorKind::NotConnected,
+448        };
+449        Self::new(kind, x)
+450    }
+451}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn/work_limiter.rs.html b/pr/395/docs/src/iroh_quinn/work_limiter.rs.html new file mode 100644 index 000000000..ad6c82eea --- /dev/null +++ b/pr/395/docs/src/iroh_quinn/work_limiter.rs.html @@ -0,0 +1,224 @@ +work_limiter.rs - source

iroh_quinn/
work_limiter.rs

1use crate::{Duration, Instant};
+2
+3/// Limits the amount of time spent on a certain type of work in a cycle
+4///
+5/// The limiter works dynamically: For a sampled subset of cycles it measures
+6/// the time that is approximately required for fulfilling 1 work item, and
+7/// calculates the amount of allowed work items per cycle.
+8/// The estimates are smoothed over all cycles where the exact duration is measured.
+9///
+10/// In cycles where no measurement is performed the previously determined work limit
+11/// is used.
+12///
+13/// For the limiter the exact definition of a work item does not matter.
+14/// It could for example track the amount of transmitted bytes per cycle,
+15/// or the amount of transmitted datagrams per cycle.
+16/// It will however work best if the required time to complete a work item is
+17/// constant.
+18#[derive(Debug)]
+19pub(crate) struct WorkLimiter {
+20    /// Whether to measure the required work time, or to use the previous estimates
+21    mode: Mode,
+22    /// The current cycle number
+23    cycle: u16,
+24    /// The time the cycle started - only used in measurement mode
+25    start_time: Option<Instant>,
+26    /// How many work items have been completed in the cycle
+27    completed: usize,
+28    /// The amount of work items which are allowed for a cycle
+29    allowed: usize,
+30    /// The desired cycle time
+31    desired_cycle_time: Duration,
+32    /// The estimated and smoothed time per work item in nanoseconds
+33    smoothed_time_per_work_item_nanos: f64,
+34}
+35
+36impl WorkLimiter {
+37    pub(crate) fn new(desired_cycle_time: Duration) -> Self {
+38        Self {
+39            mode: Mode::Measure,
+40            cycle: 0,
+41            start_time: None,
+42            completed: 0,
+43            allowed: 0,
+44            desired_cycle_time,
+45            smoothed_time_per_work_item_nanos: 0.0,
+46        }
+47    }
+48
+49    /// Starts one work cycle
+50    pub(crate) fn start_cycle(&mut self, now: impl Fn() -> Instant) {
+51        self.completed = 0;
+52        if let Mode::Measure = self.mode {
+53            self.start_time = Some(now());
+54        }
+55    }
+56
+57    /// Returns whether more work can be performed inside the `desired_cycle_time`
+58    ///
+59    /// Requires that previous work was tracked using `record_work`.
+60    pub(crate) fn allow_work(&mut self, now: impl Fn() -> Instant) -> bool {
+61        match self.mode {
+62            Mode::Measure => (now() - self.start_time.unwrap()) < self.desired_cycle_time,
+63            Mode::HistoricData => self.completed < self.allowed,
+64        }
+65    }
+66
+67    /// Records that `work` additional work items have been completed inside the cycle
+68    ///
+69    /// Must be called between `start_cycle` and `finish_cycle`.
+70    pub(crate) fn record_work(&mut self, work: usize) {
+71        self.completed += work;
+72    }
+73
+74    /// Finishes one work cycle
+75    ///
+76    /// For cycles where the exact duration is measured this will update the estimates
+77    /// for the time per work item and the limit of allowed work items per cycle.
+78    /// The estimate is updated using the same exponential averaging (smoothing)
+79    /// mechanism which is used for determining QUIC path rtts: The last value is
+80    /// weighted by 1/8, and the previous average by 7/8.
+81    pub(crate) fn finish_cycle(&mut self, now: impl Fn() -> Instant) {
+82        // If no work was done in the cycle drop the measurement, it won't be useful
+83        if self.completed == 0 {
+84            return;
+85        }
+86
+87        if let Mode::Measure = self.mode {
+88            let elapsed = now() - self.start_time.unwrap();
+89
+90            let time_per_work_item_nanos = (elapsed.as_nanos()) as f64 / self.completed as f64;
+91
+92            // Calculate the time per work item. We set this to at least 1ns to avoid
+93            // dividing by 0 when calculating the allowed amount of work items.
+94            self.smoothed_time_per_work_item_nanos = if self.allowed == 0 {
+95                // Initial estimate
+96                time_per_work_item_nanos
+97            } else {
+98                // Smoothed estimate
+99                (7.0 * self.smoothed_time_per_work_item_nanos + time_per_work_item_nanos) / 8.0
+100            }
+101            .max(1.0);
+102
+103            // Allow at least 1 work item in order to make progress
+104            self.allowed = (((self.desired_cycle_time.as_nanos()) as f64
+105                / self.smoothed_time_per_work_item_nanos) as usize)
+106                .max(1);
+107            self.start_time = None;
+108        }
+109
+110        self.cycle = self.cycle.wrapping_add(1);
+111        self.mode = match self.cycle % SAMPLING_INTERVAL {
+112            0 => Mode::Measure,
+113            _ => Mode::HistoricData,
+114        };
+115    }
+116}
+117
+118/// We take a measurement sample once every `SAMPLING_INTERVAL` cycles
+119const SAMPLING_INTERVAL: u16 = 256;
+120
+121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+122enum Mode {
+123    Measure,
+124    HistoricData,
+125}
+126
+127#[cfg(test)]
+128mod tests {
+129    use super::*;
+130    use std::cell::RefCell;
+131
+132    #[test]
+133    fn limit_work() {
+134        const CYCLE_TIME: Duration = Duration::from_millis(500);
+135        const BATCH_WORK_ITEMS: usize = 12;
+136        const BATCH_TIME: Duration = Duration::from_millis(100);
+137
+138        const EXPECTED_INITIAL_BATCHES: usize =
+139            (CYCLE_TIME.as_nanos() / BATCH_TIME.as_nanos()) as usize;
+140        const EXPECTED_ALLOWED_WORK_ITEMS: usize = EXPECTED_INITIAL_BATCHES * BATCH_WORK_ITEMS;
+141
+142        let mut limiter = WorkLimiter::new(CYCLE_TIME);
+143        reset_time();
+144
+145        // The initial cycle is measuring
+146        limiter.start_cycle(get_time);
+147        let mut initial_batches = 0;
+148        while limiter.allow_work(get_time) {
+149            limiter.record_work(BATCH_WORK_ITEMS);
+150            advance_time(BATCH_TIME);
+151            initial_batches += 1;
+152        }
+153        limiter.finish_cycle(get_time);
+154
+155        assert_eq!(initial_batches, EXPECTED_INITIAL_BATCHES);
+156        assert_eq!(limiter.allowed, EXPECTED_ALLOWED_WORK_ITEMS);
+157        let initial_time_per_work_item = limiter.smoothed_time_per_work_item_nanos;
+158
+159        // The next cycles are using historic data
+160        const BATCH_SIZES: [usize; 4] = [1, 2, 3, 5];
+161        for &batch_size in &BATCH_SIZES {
+162            limiter.start_cycle(get_time);
+163            let mut allowed_work = 0;
+164            while limiter.allow_work(get_time) {
+165                limiter.record_work(batch_size);
+166                allowed_work += batch_size;
+167            }
+168            limiter.finish_cycle(get_time);
+169
+170            assert_eq!(allowed_work, EXPECTED_ALLOWED_WORK_ITEMS);
+171        }
+172
+173        // After `SAMPLING_INTERVAL`, we get into measurement mode again
+174        for _ in 0..(SAMPLING_INTERVAL as usize - BATCH_SIZES.len() - 1) {
+175            limiter.start_cycle(get_time);
+176            limiter.record_work(1);
+177            limiter.finish_cycle(get_time);
+178        }
+179
+180        // We now do more work per cycle, and expect the estimate of allowed
+181        // work items to go up
+182        const BATCH_WORK_ITEMS_2: usize = 96;
+183        const TIME_PER_WORK_ITEMS_2_NANOS: f64 =
+184            CYCLE_TIME.as_nanos() as f64 / (EXPECTED_INITIAL_BATCHES * BATCH_WORK_ITEMS_2) as f64;
+185
+186        let expected_updated_time_per_work_item =
+187            (initial_time_per_work_item * 7.0 + TIME_PER_WORK_ITEMS_2_NANOS) / 8.0;
+188        let expected_updated_allowed_work_items =
+189            (CYCLE_TIME.as_nanos() as f64 / expected_updated_time_per_work_item) as usize;
+190
+191        limiter.start_cycle(get_time);
+192        let mut initial_batches = 0;
+193        while limiter.allow_work(get_time) {
+194            limiter.record_work(BATCH_WORK_ITEMS_2);
+195            advance_time(BATCH_TIME);
+196            initial_batches += 1;
+197        }
+198        limiter.finish_cycle(get_time);
+199
+200        assert_eq!(initial_batches, EXPECTED_INITIAL_BATCHES);
+201        assert_eq!(limiter.allowed, expected_updated_allowed_work_items);
+202    }
+203
+204    thread_local! {
+205        /// Mocked time
+206        pub static TIME: RefCell<Instant> = RefCell::new(Instant::now());
+207    }
+208
+209    fn reset_time() {
+210        TIME.with(|t| {
+211            *t.borrow_mut() = Instant::now();
+212        })
+213    }
+214
+215    fn get_time() -> Instant {
+216        TIME.with(|t| *t.borrow())
+217    }
+218
+219    fn advance_time(duration: Duration) {
+220        TIME.with(|t| {
+221            *t.borrow_mut() += duration;
+222        })
+223    }
+224}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/address_discovery.rs.html b/pr/395/docs/src/iroh_quinn_proto/address_discovery.rs.html new file mode 100644 index 000000000..d65b64801 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/address_discovery.rs.html @@ -0,0 +1,126 @@ +address_discovery.rs - source

iroh_quinn_proto/
address_discovery.rs

1//! Address discovery types from
+2//! <https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/>
+3
+4use crate::VarInt;
+5
+6/// The role of each participant.
+7///
+8/// When enabled, this is reported as a transport parameter.
+9#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
+10pub(crate) enum Role {
+11    /// Is able to report observer addresses to other peers, but it's not interested in receiving
+12    /// reports about its own address.
+13    SendOnly,
+14    /// Is interested on reports about its own observed address, but will not report back to other
+15    /// peers.
+16    ReceiveOnly,
+17    /// Will both report and receive reports of observed addresses.
+18    Both,
+19    /// Address discovery is disabled.
+20    #[default]
+21    Disabled,
+22}
+23
+24impl TryFrom<VarInt> for Role {
+25    type Error = crate::transport_parameters::Error;
+26
+27    fn try_from(value: VarInt) -> Result<Self, Self::Error> {
+28        match value.0 {
+29            0 => Ok(Self::SendOnly),
+30            1 => Ok(Self::ReceiveOnly),
+31            2 => Ok(Self::Both),
+32            _ => Err(crate::transport_parameters::Error::IllegalValue),
+33        }
+34    }
+35}
+36
+37impl Role {
+38    /// Whether address discovery is disabled.
+39    pub(crate) fn is_disabled(&self) -> bool {
+40        matches!(self, Self::Disabled)
+41    }
+42
+43    /// Whether this peer's role allows for address reporting to other peers.
+44    fn is_reporter(&self) -> bool {
+45        matches!(self, Self::SendOnly | Self::Both)
+46    }
+47
+48    /// Whether this peer's role accepts observed address reports.
+49    fn receives_reports(&self) -> bool {
+50        matches!(self, Self::ReceiveOnly | Self::Both)
+51    }
+52
+53    /// Whether this peer should report observed addresses to the other peer.
+54    pub(crate) fn should_report(&self, other: &Self) -> bool {
+55        self.is_reporter() && other.receives_reports()
+56    }
+57
+58    /// Sets whether this peer should provide observed addresses to other peers.
+59    pub(crate) fn send_reports_to_peers(&mut self, provide: bool) {
+60        if provide {
+61            self.enable_sending_reports_to_peers()
+62        } else {
+63            self.disable_sending_reports_to_peers()
+64        }
+65    }
+66
+67    /// Enables sending reports of observed addresses to other peers.
+68    fn enable_sending_reports_to_peers(&mut self) {
+69        match self {
+70            Self::SendOnly => {} // already enabled
+71            Self::ReceiveOnly => *self = Self::Both,
+72            Self::Both => {} // already enabled
+73            Self::Disabled => *self = Self::SendOnly,
+74        }
+75    }
+76
+77    /// Disables sending reports of observed addresses to other peers.
+78    fn disable_sending_reports_to_peers(&mut self) {
+79        match self {
+80            Self::SendOnly => *self = Self::Disabled,
+81            Self::ReceiveOnly => {} // already disabled
+82            Self::Both => *self = Self::ReceiveOnly,
+83            Self::Disabled => {} // already disabled
+84        }
+85    }
+86
+87    /// Sets whether this peer should accept received reports of observed addresses from other
+88    /// peers.
+89    pub(crate) fn receive_reports_from_peers(&mut self, receive: bool) {
+90        if receive {
+91            self.enable_receiving_reports_from_peers()
+92        } else {
+93            self.disable_receiving_reports_from_peers()
+94        }
+95    }
+96
+97    /// Enables receiving reports of observed addresses from other peers.
+98    fn enable_receiving_reports_from_peers(&mut self) {
+99        match self {
+100            Self::SendOnly => *self = Self::Both,
+101            Self::ReceiveOnly => {} // already enabled
+102            Self::Both => {}        // already enabled
+103            Self::Disabled => *self = Self::ReceiveOnly,
+104        }
+105    }
+106
+107    /// Disables receiving reports of observed addresses from other peers.
+108    fn disable_receiving_reports_from_peers(&mut self) {
+109        match self {
+110            Self::SendOnly => {} // already disabled
+111            Self::ReceiveOnly => *self = Self::Disabled,
+112            Self::Both => *self = Self::SendOnly,
+113            Self::Disabled => {} // already disabled
+114        }
+115    }
+116
+117    /// Gives the [`VarInt`] representing this [`Role`] as a transport parameter.
+118    pub(crate) fn as_transport_parameter(&self) -> Option<VarInt> {
+119        match self {
+120            Self::SendOnly => Some(VarInt(0)),
+121            Self::ReceiveOnly => Some(VarInt(1)),
+122            Self::Both => Some(VarInt(2)),
+123            Self::Disabled => None,
+124        }
+125    }
+126}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/bloom_token_log.rs.html b/pr/395/docs/src/iroh_quinn_proto/bloom_token_log.rs.html new file mode 100644 index 000000000..a2c6b8721 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/bloom_token_log.rs.html @@ -0,0 +1,368 @@ +bloom_token_log.rs - source

iroh_quinn_proto/
bloom_token_log.rs

1use std::{
+2    collections::HashSet,
+3    f64::consts::LN_2,
+4    hash::{BuildHasher, Hasher},
+5    mem::{size_of, take},
+6    sync::Mutex,
+7};
+8
+9use fastbloom::BloomFilter;
+10use rustc_hash::FxBuildHasher;
+11use tracing::{trace, warn};
+12
+13use crate::{Duration, SystemTime, TokenLog, TokenReuseError, UNIX_EPOCH};
+14
+15/// Bloom filter-based [`TokenLog`]
+16///
+17/// Parameterizable over an approximate maximum number of bytes to allocate. Starts out by storing
+18/// used tokens in a hash set. Once the hash set becomes too large, converts it to a bloom filter.
+19/// This achieves a memory profile of linear growth with an upper bound.
+20///
+21/// Divides time into periods based on `lifetime` and stores two filters at any given moment, for
+22/// each of the two periods currently non-expired tokens could expire in. As such, turns over
+23/// filters as time goes on to avoid bloom filter false positive rate increasing infinitely over
+24/// time.
+25pub struct BloomTokenLog(Mutex<State>);
+26
+27impl BloomTokenLog {
+28    /// Construct with an approximate maximum memory usage and expected number of validation token
+29    /// usages per expiration period
+30    ///
+31    /// Calculates the optimal bloom filter k number automatically.
+32    pub fn new_expected_items(max_bytes: usize, expected_hits: u64) -> Self {
+33        Self::new(max_bytes, optimal_k_num(max_bytes, expected_hits))
+34    }
+35
+36    /// Construct with an approximate maximum memory usage and a [bloom filter k number][bloom]
+37    ///
+38    /// [bloom]: https://en.wikipedia.org/wiki/Bloom_filter
+39    ///
+40    /// If choosing a custom k number, note that `BloomTokenLog` always maintains two filters
+41    /// between them and divides the allocation budget of `max_bytes` evenly between them. As such,
+42    /// each bloom filter will contain `max_bytes * 4` bits.
+43    pub fn new(max_bytes: usize, k_num: u32) -> Self {
+44        Self(Mutex::new(State {
+45            config: FilterConfig {
+46                filter_max_bytes: max_bytes / 2,
+47                k_num,
+48            },
+49            period_1_start: UNIX_EPOCH,
+50            filter_1: Filter::default(),
+51            filter_2: Filter::default(),
+52        }))
+53    }
+54}
+55
+56impl TokenLog for BloomTokenLog {
+57    fn check_and_insert(
+58        &self,
+59        nonce: u128,
+60        issued: SystemTime,
+61        lifetime: Duration,
+62    ) -> Result<(), TokenReuseError> {
+63        trace!(%nonce, "check_and_insert");
+64
+65        if lifetime.is_zero() {
+66            // avoid divide-by-zero if lifetime is zero
+67            return Err(TokenReuseError);
+68        }
+69
+70        let mut guard = self.0.lock().unwrap();
+71        let state = &mut *guard;
+72
+73        // calculate how many periods past period 1 the token expires
+74        let expires_at = issued + lifetime;
+75        let Ok(periods_forward) = expires_at
+76            .duration_since(state.period_1_start)
+77            .map(|duration| duration.as_nanos() / lifetime.as_nanos())
+78        else {
+79            // shouldn't happen unless time travels backwards or lifetime changes or the current
+80            // system time is before the Unix epoch
+81            warn!("BloomTokenLog presented with token too far in past");
+82            return Err(TokenReuseError);
+83        };
+84
+85        // get relevant filter
+86        let filter = match periods_forward {
+87            0 => &mut state.filter_1,
+88            1 => &mut state.filter_2,
+89            2 => {
+90                // turn over filter 1
+91                state.filter_1 = take(&mut state.filter_2);
+92                state.period_1_start += lifetime;
+93                &mut state.filter_2
+94            }
+95            _ => {
+96                // turn over both filters
+97                state.filter_1 = Filter::default();
+98                state.filter_2 = Filter::default();
+99                state.period_1_start = expires_at;
+100                &mut state.filter_1
+101            }
+102        };
+103
+104        // insert into the filter
+105        //
+106        // the token's nonce needs to guarantee uniqueness because of the role it plays in the
+107        // encryption of the tokens, so it is 128 bits. but since the token log can tolerate false
+108        // positives, we trim it down to 64 bits, which would still only have a small collision
+109        // rate even at significant amounts of usage, while allowing us to store twice as many in
+110        // the hash set variant.
+111        //
+112        // token nonce values are uniformly randomly generated server-side and cryptographically
+113        // integrity-checked, so we don't need to employ secure hashing to trim it down to 64 bits,
+114        // we can simply truncate.
+115        //
+116        // per the Rust reference, we can truncate by simply casting:
+117        // https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#numeric-cast
+118        filter.check_and_insert(nonce as u64, &state.config)
+119    }
+120}
+121
+122/// Default to 20 MiB max memory consumption and expected one million hits
+123///
+124/// With the default validation token lifetime of 2 weeks, this corresponds to one token usage per
+125/// 1.21 seconds.
+126impl Default for BloomTokenLog {
+127    fn default() -> Self {
+128        Self::new_expected_items(DEFAULT_MAX_BYTES, DEFAULT_EXPECTED_HITS)
+129    }
+130}
+131
+132/// Lockable state of [`BloomTokenLog`]
+133struct State {
+134    config: FilterConfig,
+135    // filter_1 covers tokens that expire in the period starting at period_1_start and extending
+136    // lifetime after. filter_2 covers tokens for the next lifetime after that.
+137    period_1_start: SystemTime,
+138    filter_1: Filter,
+139    filter_2: Filter,
+140}
+141
+142/// Unchanging parameters governing [`Filter`] behavior
+143struct FilterConfig {
+144    filter_max_bytes: usize,
+145    k_num: u32,
+146}
+147
+148/// Period filter within [`State`]
+149enum Filter {
+150    Set(HashSet<u64, IdentityBuildHasher>),
+151    Bloom(BloomFilter<FxBuildHasher>),
+152}
+153
+154impl Filter {
+155    fn check_and_insert(
+156        &mut self,
+157        fingerprint: u64,
+158        config: &FilterConfig,
+159    ) -> Result<(), TokenReuseError> {
+160        match self {
+161            Self::Set(hset) => {
+162                if !hset.insert(fingerprint) {
+163                    return Err(TokenReuseError);
+164                }
+165
+166                if hset.capacity() * size_of::<u64>() <= config.filter_max_bytes {
+167                    return Ok(());
+168                }
+169
+170                // convert to bloom
+171                // avoid panicking if user passed in filter_max_bytes of 0. we document that this
+172                // limit is approximate, so just fudge it up to 1.
+173                let mut bloom = BloomFilter::with_num_bits((config.filter_max_bytes * 8).max(1))
+174                    .hasher(FxBuildHasher)
+175                    .hashes(config.k_num);
+176                for item in &*hset {
+177                    bloom.insert(item);
+178                }
+179                *self = Self::Bloom(bloom);
+180            }
+181            Self::Bloom(bloom) => {
+182                if bloom.insert(&fingerprint) {
+183                    return Err(TokenReuseError);
+184                }
+185            }
+186        }
+187        Ok(())
+188    }
+189}
+190
+191impl Default for Filter {
+192    fn default() -> Self {
+193        Self::Set(HashSet::default())
+194    }
+195}
+196
+197/// `BuildHasher` of `IdentityHasher`
+198#[derive(Default)]
+199struct IdentityBuildHasher;
+200
+201impl BuildHasher for IdentityBuildHasher {
+202    type Hasher = IdentityHasher;
+203
+204    fn build_hasher(&self) -> Self::Hasher {
+205        IdentityHasher::default()
+206    }
+207}
+208
+209/// Hasher that is the identity operation--it assumes that exactly 8 bytes will be hashed, and the
+210/// resultant hash is those bytes as a `u64`
+211#[derive(Default)]
+212struct IdentityHasher {
+213    data: [u8; 8],
+214    #[cfg(debug_assertions)]
+215    wrote_8_byte_slice: bool,
+216}
+217
+218impl Hasher for IdentityHasher {
+219    fn write(&mut self, bytes: &[u8]) {
+220        #[cfg(debug_assertions)]
+221        {
+222            assert!(!self.wrote_8_byte_slice);
+223            assert_eq!(bytes.len(), 8);
+224            self.wrote_8_byte_slice = true;
+225        }
+226        self.data.copy_from_slice(bytes);
+227    }
+228
+229    fn finish(&self) -> u64 {
+230        #[cfg(debug_assertions)]
+231        assert!(self.wrote_8_byte_slice);
+232        u64::from_ne_bytes(self.data)
+233    }
+234}
+235
+236fn optimal_k_num(num_bytes: usize, expected_hits: u64) -> u32 {
+237    // be more forgiving rather than panickey here. excessively high num_bits may occur if the user
+238    // wishes it to be unbounded, so just saturate. expected_hits of 0 would cause divide-by-zero,
+239    // so just fudge it up to 1 in that case.
+240    let num_bits = (num_bytes as u64).saturating_mul(8);
+241    let expected_hits = expected_hits.max(1);
+242    // reference for this formula: https://programming.guide/bloom-filter-calculator.html
+243    // optimal k = (m ln 2) / n
+244    // wherein m is the number of bits, and n is the number of elements in the set.
+245    //
+246    // we also impose a minimum return value of 1, to avoid making the bloom filter entirely
+247    // useless in the case that the user provided an absurdly high ratio of hits / bytes.
+248    (((num_bits as f64 / expected_hits as f64) * LN_2).round() as u32).max(1)
+249}
+250
+251// remember to change the doc comment for `impl Default for BloomTokenLog` if these ever change
+252const DEFAULT_MAX_BYTES: usize = 10 << 20;
+253const DEFAULT_EXPECTED_HITS: u64 = 1_000_000;
+254
+255#[cfg(test)]
+256mod test {
+257    use super::*;
+258    use rand::prelude::*;
+259    use rand_pcg::Pcg32;
+260
+261    fn new_rng() -> impl Rng {
+262        Pcg32::from_seed(0xdeadbeefdeadbeefdeadbeefdeadbeef_u128.to_le_bytes())
+263    }
+264
+265    #[test]
+266    fn identity_hash_test() {
+267        let mut rng = new_rng();
+268        let builder = IdentityBuildHasher;
+269        for _ in 0..100 {
+270            let n = rng.random::<u64>();
+271            let hash = builder.hash_one(n);
+272            assert_eq!(hash, n);
+273        }
+274    }
+275
+276    #[test]
+277    fn optimal_k_num_test() {
+278        assert_eq!(optimal_k_num(10 << 20, 1_000_000), 58);
+279        assert_eq!(optimal_k_num(10 << 20, 1_000_000_000_000_000), 1);
+280        // assert that these don't panic:
+281        optimal_k_num(10 << 20, 0);
+282        optimal_k_num(usize::MAX, 1_000_000);
+283    }
+284
+285    #[test]
+286    fn bloom_token_log_conversion() {
+287        let mut rng = new_rng();
+288        let mut log = BloomTokenLog::new_expected_items(800, 200);
+289
+290        let issued = SystemTime::now();
+291        let lifetime = Duration::from_secs(1_000_000);
+292
+293        for i in 0..200 {
+294            let token = rng.random::<u128>();
+295            let result = log.check_and_insert(token, issued, lifetime);
+296            {
+297                let filter = &log.0.lock().unwrap().filter_1;
+298                if let Filter::Set(ref hset) = *filter {
+299                    assert!(hset.capacity() * size_of::<u64>() <= 800);
+300                    assert_eq!(hset.len(), i + 1);
+301                    assert!(result.is_ok());
+302                } else {
+303                    assert!(i > 10, "definitely bloomed too early");
+304                }
+305            }
+306            assert!(log.check_and_insert(token, issued, lifetime).is_err());
+307        }
+308
+309        assert!(
+310            matches!(log.0.get_mut().unwrap().filter_1, Filter::Bloom { .. }),
+311            "didn't bloom"
+312        );
+313    }
+314
+315    #[test]
+316    fn turn_over() {
+317        let mut rng = new_rng();
+318        let log = BloomTokenLog::new_expected_items(800, 200);
+319        let lifetime = Duration::from_secs(1_000);
+320        let mut old = Vec::default();
+321        let mut accepted = 0;
+322
+323        for i in 0..200 {
+324            let token = rng.random::<u128>();
+325            let now = UNIX_EPOCH + lifetime * 10 + lifetime * i / 10;
+326            let issued = now - lifetime.mul_f32(rng.random_range(0.0..3.0));
+327            let result = log.check_and_insert(token, issued, lifetime);
+328            if result.is_ok() {
+329                accepted += 1;
+330            }
+331            old.push((token, issued));
+332            let old_idx = rng.random_range(0..old.len());
+333            let (old_token, old_issued) = old[old_idx];
+334            assert!(
+335                log.check_and_insert(old_token, old_issued, lifetime)
+336                    .is_err()
+337            );
+338        }
+339        assert!(accepted > 0);
+340    }
+341
+342    fn test_doesnt_panic(log: BloomTokenLog) {
+343        let mut rng = new_rng();
+344
+345        let issued = SystemTime::now();
+346        let lifetime = Duration::from_secs(1_000_000);
+347
+348        for _ in 0..200 {
+349            let _ = log.check_and_insert(rng.random::<u128>(), issued, lifetime);
+350        }
+351    }
+352
+353    #[test]
+354    fn max_bytes_zero() {
+355        // "max bytes" is documented to be approximate. but make sure it doesn't panic.
+356        test_doesnt_panic(BloomTokenLog::new_expected_items(0, 200));
+357    }
+358
+359    #[test]
+360    fn expected_hits_zero() {
+361        test_doesnt_panic(BloomTokenLog::new_expected_items(100, 0));
+362    }
+363
+364    #[test]
+365    fn k_num_zero() {
+366        test_doesnt_panic(BloomTokenLog::new(100, 0));
+367    }
+368}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/cid_generator.rs.html b/pr/395/docs/src/iroh_quinn_proto/cid_generator.rs.html new file mode 100644 index 000000000..111ef0b80 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/cid_generator.rs.html @@ -0,0 +1,180 @@ +cid_generator.rs - source

iroh_quinn_proto/
cid_generator.rs

1use std::hash::Hasher;
+2
+3use rand::{Rng, RngCore};
+4
+5use crate::Duration;
+6use crate::MAX_CID_SIZE;
+7use crate::shared::ConnectionId;
+8
+9/// Generates connection IDs for incoming connections
+10pub trait ConnectionIdGenerator: Send + Sync {
+11    /// Generates a new CID
+12    ///
+13    /// Connection IDs MUST NOT contain any information that can be used by
+14    /// an external observer (that is, one that does not cooperate with the
+15    /// issuer) to correlate them with other connection IDs for the same
+16    /// connection. They MUST have high entropy, e.g. due to encrypted data
+17    /// or cryptographic-grade random data.
+18    fn generate_cid(&mut self) -> ConnectionId;
+19
+20    /// Quickly determine whether `cid` could have been generated by this generator
+21    ///
+22    /// False positives are permitted, but increase the cost of handling invalid packets.
+23    fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid> {
+24        Ok(())
+25    }
+26
+27    /// Returns the length of a CID for connections created by this generator
+28    fn cid_len(&self) -> usize;
+29    /// Returns the lifetime of generated Connection IDs
+30    ///
+31    /// Connection IDs will be retired after the returned `Duration`, if any. Assumed to be constant.
+32    fn cid_lifetime(&self) -> Option<Duration>;
+33}
+34
+35/// The connection ID was not recognized by the [`ConnectionIdGenerator`]
+36#[derive(Debug, Copy, Clone)]
+37pub struct InvalidCid;
+38
+39/// Generates purely random connection IDs of a specified length
+40///
+41/// Random CIDs can be smaller than those produced by [`HashedConnectionIdGenerator`], but cannot be
+42/// usefully [`validate`](ConnectionIdGenerator::validate)d.
+43#[derive(Debug, Clone, Copy)]
+44pub struct RandomConnectionIdGenerator {
+45    cid_len: usize,
+46    lifetime: Option<Duration>,
+47}
+48
+49impl Default for RandomConnectionIdGenerator {
+50    fn default() -> Self {
+51        Self {
+52            cid_len: 8,
+53            lifetime: None,
+54        }
+55    }
+56}
+57
+58impl RandomConnectionIdGenerator {
+59    /// Initialize Random CID generator with a fixed CID length
+60    ///
+61    /// The given length must be less than or equal to MAX_CID_SIZE.
+62    pub fn new(cid_len: usize) -> Self {
+63        debug_assert!(cid_len <= MAX_CID_SIZE);
+64        Self {
+65            cid_len,
+66            ..Self::default()
+67        }
+68    }
+69
+70    /// Set the lifetime of CIDs created by this generator
+71    pub fn set_lifetime(&mut self, d: Duration) -> &mut Self {
+72        self.lifetime = Some(d);
+73        self
+74    }
+75}
+76
+77impl ConnectionIdGenerator for RandomConnectionIdGenerator {
+78    fn generate_cid(&mut self) -> ConnectionId {
+79        let mut bytes_arr = [0; MAX_CID_SIZE];
+80        rand::rng().fill_bytes(&mut bytes_arr[..self.cid_len]);
+81
+82        ConnectionId::new(&bytes_arr[..self.cid_len])
+83    }
+84
+85    /// Provide the length of dst_cid in short header packet
+86    fn cid_len(&self) -> usize {
+87        self.cid_len
+88    }
+89
+90    fn cid_lifetime(&self) -> Option<Duration> {
+91        self.lifetime
+92    }
+93}
+94
+95/// Generates 8-byte connection IDs that can be efficiently
+96/// [`validate`](ConnectionIdGenerator::validate)d
+97///
+98/// This generator uses a non-cryptographic hash and can therefore still be spoofed, but nonetheless
+99/// helps prevents Quinn from responding to non-QUIC packets at very low cost.
+100pub struct HashedConnectionIdGenerator {
+101    key: u64,
+102    lifetime: Option<Duration>,
+103}
+104
+105impl HashedConnectionIdGenerator {
+106    /// Create a generator with a random key
+107    pub fn new() -> Self {
+108        Self::from_key(rand::rng().random())
+109    }
+110
+111    /// Create a generator with a specific key
+112    ///
+113    /// Allows [`validate`](ConnectionIdGenerator::validate) to recognize a consistent set of
+114    /// connection IDs across restarts
+115    pub fn from_key(key: u64) -> Self {
+116        Self {
+117            key,
+118            lifetime: None,
+119        }
+120    }
+121
+122    /// Set the lifetime of CIDs created by this generator
+123    pub fn set_lifetime(&mut self, d: Duration) -> &mut Self {
+124        self.lifetime = Some(d);
+125        self
+126    }
+127}
+128
+129impl Default for HashedConnectionIdGenerator {
+130    fn default() -> Self {
+131        Self::new()
+132    }
+133}
+134
+135impl ConnectionIdGenerator for HashedConnectionIdGenerator {
+136    fn generate_cid(&mut self) -> ConnectionId {
+137        let mut bytes_arr = [0; NONCE_LEN + SIGNATURE_LEN];
+138        rand::rng().fill_bytes(&mut bytes_arr[..NONCE_LEN]);
+139        let mut hasher = rustc_hash::FxHasher::default();
+140        hasher.write_u64(self.key);
+141        hasher.write(&bytes_arr[..NONCE_LEN]);
+142        bytes_arr[NONCE_LEN..].copy_from_slice(&hasher.finish().to_le_bytes()[..SIGNATURE_LEN]);
+143        ConnectionId::new(&bytes_arr)
+144    }
+145
+146    fn validate(&self, cid: ConnectionId) -> Result<(), InvalidCid> {
+147        let (nonce, signature) = cid.split_at(NONCE_LEN);
+148        let mut hasher = rustc_hash::FxHasher::default();
+149        hasher.write_u64(self.key);
+150        hasher.write(nonce);
+151        let expected = hasher.finish().to_le_bytes();
+152        match expected[..SIGNATURE_LEN] == signature[..] {
+153            true => Ok(()),
+154            false => Err(InvalidCid),
+155        }
+156    }
+157
+158    fn cid_len(&self) -> usize {
+159        NONCE_LEN + SIGNATURE_LEN
+160    }
+161
+162    fn cid_lifetime(&self) -> Option<Duration> {
+163        self.lifetime
+164    }
+165}
+166
+167const NONCE_LEN: usize = 3; // Good for more than 16 million connections
+168const SIGNATURE_LEN: usize = 8 - NONCE_LEN; // 8-byte total CID length
+169
+170#[cfg(test)]
+171mod tests {
+172    use super::*;
+173
+174    #[test]
+175    fn validate_keyed_cid() {
+176        let mut generator = HashedConnectionIdGenerator::new();
+177        let cid = generator.generate_cid();
+178        generator.validate(cid).unwrap();
+179    }
+180}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/cid_queue.rs.html b/pr/395/docs/src/iroh_quinn_proto/cid_queue.rs.html new file mode 100644 index 000000000..f48a3ad5a --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/cid_queue.rs.html @@ -0,0 +1,484 @@ +cid_queue.rs - source

iroh_quinn_proto/
cid_queue.rs

1use std::{fmt::Debug, ops::Range};
+2
+3use crate::{ConnectionId, ResetToken, frame::NewConnectionId};
+4
+5/// DataType stored in CidQueue buffer
+6#[derive(Debug, Clone, Copy)]
+7struct CidData(ConnectionId, Option<ResetToken>);
+8
+9/// Sliding window of active Connection IDs.
+10///
+11/// This represents a circular buffer that can contain gaps due to packet loss or reordering.
+12/// The buffer has three regions:
+13/// - Exactly one active CID at `self.buffer[self.cursor]`.
+14/// - Zero to `Self::LEN - 1` reserved CIDs from `self.cursor` up to `self.cursor_reserved`.
+15/// - More "available"/"ready" CIDs after `self.cursor_reserved`.
+16///
+17/// The range of reserved CIDs is grown by calling `CidQueue::next_reserved`, which takes one of
+18/// the available ones and returns the CID that was reserved.
+19///
+20/// New available/ready CIDs are added by calling [`CidQueue::insert`].
+21///
+22/// May contain gaps due to packet loss or reordering.
+23#[derive(Debug)]
+24pub(crate) struct CidQueue {
+25    /// Ring buffer indexed by `self.cursor`
+26    buffer: [Option<CidData>; Self::LEN],
+27    /// Index at which circular buffer addressing is based
+28    cursor: usize,
+29    /// Sequence number of `self.buffer[cursor]`
+30    ///
+31    /// The sequence number of the active CID; must be the smallest among CIDs in `buffer`.
+32    offset: u64,
+33    /// Circular index for the last reserved CID, i.e. a CID that is not the active CID, but was
+34    /// used for probing packets on a different remote address.
+35    ///
+36    /// When [`Self::cursor_reserved`] and [`Self::cursor`] are equal, no CID is considered
+37    /// reserved.
+38    cursor_reserved: usize,
+39}
+40
+41impl CidQueue {
+42    pub(crate) fn new(cid: ConnectionId) -> Self {
+43        let mut buffer = [None; Self::LEN];
+44        buffer[0] = Some(CidData(cid, None));
+45        Self {
+46            buffer,
+47            cursor: 0,
+48            offset: 0,
+49            cursor_reserved: 0,
+50        }
+51    }
+52
+53    /// Handle a `NEW_CONNECTION_ID` frame
+54    ///
+55    /// Returns a non-empty range of retired sequence numbers and the reset token of the new active
+56    /// CID iff any CIDs were retired.
+57    pub(crate) fn insert(
+58        &mut self,
+59        cid: NewConnectionId,
+60    ) -> Result<Option<(Range<u64>, ResetToken)>, InsertError> {
+61        // Position of new CID wrt. the current active CID
+62        let Some(index) = cid.sequence.checked_sub(self.offset) else {
+63            return Err(InsertError::Retired);
+64        };
+65
+66        let retired_count = cid.retire_prior_to.saturating_sub(self.offset);
+67        if index >= Self::LEN as u64 + retired_count {
+68            return Err(InsertError::ExceedsLimit);
+69        }
+70
+71        // Discard retired CIDs, if any
+72        for i in 0..(retired_count.min(Self::LEN as u64) as usize) {
+73            self.buffer[(self.cursor + i) % Self::LEN] = None;
+74        }
+75
+76        // Record the new CID
+77        let index = ((self.cursor as u64 + index) % Self::LEN as u64) as usize;
+78        self.buffer[index] = Some(CidData(cid.id, Some(cid.reset_token)));
+79
+80        if retired_count == 0 {
+81            return Ok(None);
+82        }
+83
+84        // The active CID was retired. Find the first known CID with sequence number of at least
+85        // retire_prior_to, and inform the caller that all prior CIDs have been retired, and of
+86        // the new CID's reset token.
+87        self.cursor = ((self.cursor as u64 + retired_count) % Self::LEN as u64) as usize;
+88        let (i, CidData(_, token)) = self
+89            .iter_from_active()
+90            .next()
+91            .expect("it is impossible to retire a CID without supplying a new one");
+92        self.cursor = (self.cursor + i) % Self::LEN;
+93        self.cursor_reserved = self.cursor;
+94        let orig_offset = self.offset;
+95        self.offset = cid.retire_prior_to + i as u64;
+96        // We don't immediately retire CIDs in the range (orig_offset +
+97        // Self::LEN)..self.offset. These are CIDs that we haven't yet received from a
+98        // NEW_CONNECTION_ID frame, since having previously received them would violate the
+99        // connection ID limit we specified based on Self::LEN. If we do receive a such a frame
+100        // in the future, e.g. due to reordering, we'll retire it then. This ensures we can't be
+101        // made to buffer an arbitrarily large number of RETIRE_CONNECTION_ID frames.
+102        Ok(Some((
+103            orig_offset..self.offset.min(orig_offset + Self::LEN as u64),
+104            token.expect("non-initial CID missing reset token"),
+105        )))
+106    }
+107
+108    /// Switch to next active CID if possible, return
+109    /// 1) the corresponding ResetToken and 2) a non-empty range preceding it to retire
+110    pub(crate) fn next(&mut self) -> Option<(ResetToken, Range<u64>)> {
+111        let (i, cid_data) = self.iter_from_reserved().nth(1)?;
+112        let reserved = self.reserved_len();
+113        for j in 0..=reserved {
+114            self.buffer[self.cursor + j] = None;
+115        }
+116        let orig_offset = self.offset;
+117        self.offset += (i + reserved) as u64;
+118        self.cursor = (self.cursor_reserved + i) % Self::LEN;
+119        self.cursor_reserved = self.cursor;
+120
+121        Some((cid_data.1.unwrap(), orig_offset..self.offset))
+122    }
+123
+124    /// Returns a CID from the available ones and marks it as reserved.
+125    ///
+126    /// If there's no more CIDs in the ready set, this will return None.
+127    /// CIDs marked as reserved will be skipped when the active one advances.
+128    pub(crate) fn next_reserved(&mut self) -> Option<ConnectionId> {
+129        let (i, cid_data) = self.iter_from_reserved().nth(1)?;
+130
+131        self.cursor_reserved = (self.cursor_reserved + i) % Self::LEN;
+132        Some(cid_data.0)
+133    }
+134
+135    /// Iterate CIDs in CidQueue that are not `None`, including the active CID
+136    fn iter_from_active(&self) -> impl Iterator<Item = (usize, CidData)> + '_ {
+137        (0..Self::LEN).filter_map(move |step| {
+138            let index = (self.cursor + step) % Self::LEN;
+139            self.buffer[index].map(|cid_data| (step, cid_data))
+140        })
+141    }
+142
+143    /// Iterate CIDs in CidQueue that are not `None`, including the active CID.
+144    ///
+145    /// Along with the CID, it returns the offset counted from [`Self::cursor_reserved`] where the CID is stored.
+146    fn iter_from_reserved(&self) -> impl Iterator<Item = (usize, CidData)> + '_ {
+147        (0..(Self::LEN - self.reserved_len())).filter_map(move |step| {
+148            let index = (self.cursor_reserved + step) % Self::LEN;
+149            self.buffer[index].map(|cid_data| (step, cid_data))
+150        })
+151    }
+152
+153    /// The length of the internal buffer's section of CIDs that are marked as reserved.
+154    fn reserved_len(&self) -> usize {
+155        if self.cursor_reserved >= self.cursor {
+156            self.cursor_reserved - self.cursor
+157        } else {
+158            self.cursor_reserved + Self::LEN - self.cursor
+159        }
+160    }
+161
+162    /// Replace the initial CID
+163    pub(crate) fn update_initial_cid(&mut self, cid: ConnectionId) {
+164        debug_assert_eq!(self.offset, 0);
+165        self.buffer[self.cursor] = Some(CidData(cid, None));
+166    }
+167
+168    /// Return active remote CID itself
+169    pub(crate) fn active(&self) -> ConnectionId {
+170        self.buffer[self.cursor].unwrap().0
+171    }
+172
+173    /// Return the sequence number of active remote CID
+174    pub(crate) fn active_seq(&self) -> u64 {
+175        self.offset
+176    }
+177
+178    pub(crate) const LEN: usize = 5;
+179}
+180
+181#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+182pub(crate) enum InsertError {
+183    /// CID was already retired
+184    Retired,
+185    /// Sequence number violates the leading edge of the window
+186    ExceedsLimit,
+187}
+188
+189#[cfg(test)]
+190mod tests {
+191    use super::*;
+192
+193    fn cid(sequence: u64, retire_prior_to: u64) -> NewConnectionId {
+194        NewConnectionId {
+195            path_id: None,
+196            sequence,
+197            id: ConnectionId::new(&sequence.to_be_bytes()),
+198            reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]),
+199            retire_prior_to,
+200        }
+201    }
+202
+203    fn initial_cid() -> ConnectionId {
+204        ConnectionId::new(&[0xFF; 8])
+205    }
+206
+207    #[test]
+208    fn next_dense() {
+209        let mut q = CidQueue::new(initial_cid());
+210        assert!(q.next().is_none());
+211        assert!(q.next().is_none());
+212
+213        for i in 1..CidQueue::LEN as u64 {
+214            q.insert(cid(i, 0)).unwrap();
+215        }
+216        for i in 1..CidQueue::LEN as u64 {
+217            let (_, retire) = q.next().unwrap();
+218            assert_eq!(q.active_seq(), i);
+219            assert_eq!(retire.end - retire.start, 1);
+220        }
+221        assert!(q.next().is_none());
+222    }
+223
+224    #[test]
+225    fn next_sparse() {
+226        let mut q = CidQueue::new(initial_cid());
+227        let seqs = (1..CidQueue::LEN as u64).filter(|x| x % 2 == 0);
+228        for i in seqs.clone() {
+229            q.insert(cid(i, 0)).unwrap();
+230        }
+231        for i in seqs {
+232            let (_, retire) = q.next().unwrap();
+233            dbg!(&retire);
+234            assert_eq!(q.active_seq(), i);
+235            assert_eq!(retire, (q.active_seq().saturating_sub(2))..q.active_seq());
+236        }
+237        assert!(q.next().is_none());
+238    }
+239
+240    #[test]
+241    fn wrap() {
+242        let mut q = CidQueue::new(initial_cid());
+243
+244        for i in 1..CidQueue::LEN as u64 {
+245            q.insert(cid(i, 0)).unwrap();
+246        }
+247        for _ in 1..(CidQueue::LEN as u64 - 1) {
+248            q.next().unwrap();
+249        }
+250        for i in CidQueue::LEN as u64..(CidQueue::LEN as u64 + 3) {
+251            q.insert(cid(i, 0)).unwrap();
+252        }
+253        for i in (CidQueue::LEN as u64 - 1)..(CidQueue::LEN as u64 + 3) {
+254            q.next().unwrap();
+255            assert_eq!(q.active_seq(), i);
+256        }
+257        assert!(q.next().is_none());
+258    }
+259
+260    #[test]
+261    fn retire_dense() {
+262        let mut q = CidQueue::new(initial_cid());
+263
+264        for i in 1..CidQueue::LEN as u64 {
+265            q.insert(cid(i, 0)).unwrap();
+266        }
+267        assert_eq!(q.active_seq(), 0);
+268
+269        assert_eq!(q.insert(cid(4, 2)).unwrap().unwrap().0, 0..2);
+270        assert_eq!(q.active_seq(), 2);
+271        assert_eq!(q.insert(cid(4, 2)), Ok(None));
+272
+273        for i in 2..(CidQueue::LEN as u64 - 1) {
+274            let _ = q.next().unwrap();
+275            assert_eq!(q.active_seq(), i + 1);
+276            assert_eq!(q.insert(cid(i + 1, i + 1)), Ok(None));
+277        }
+278
+279        assert!(q.next().is_none());
+280    }
+281
+282    #[test]
+283    fn retire_sparse() {
+284        // Retiring CID 0 when CID 1 is not known should retire CID 1 as we move to CID 2
+285        let mut q = CidQueue::new(initial_cid());
+286        q.insert(cid(2, 0)).unwrap();
+287        assert_eq!(q.insert(cid(3, 1)).unwrap().unwrap().0, 0..2,);
+288        assert_eq!(q.active_seq(), 2);
+289    }
+290
+291    #[test]
+292    fn retire_many() {
+293        let mut q = CidQueue::new(initial_cid());
+294        q.insert(cid(2, 0)).unwrap();
+295        assert_eq!(
+296            q.insert(cid(1_000_000, 1_000_000)).unwrap().unwrap().0,
+297            0..CidQueue::LEN as u64,
+298        );
+299        assert_eq!(q.active_seq(), 1_000_000);
+300    }
+301
+302    #[test]
+303    fn insert_limit() {
+304        let mut q = CidQueue::new(initial_cid());
+305        assert_eq!(q.insert(cid(CidQueue::LEN as u64 - 1, 0)), Ok(None));
+306        assert_eq!(
+307            q.insert(cid(CidQueue::LEN as u64, 0)),
+308            Err(InsertError::ExceedsLimit)
+309        );
+310    }
+311
+312    #[test]
+313    fn insert_duplicate() {
+314        let mut q = CidQueue::new(initial_cid());
+315        q.insert(cid(0, 0)).unwrap();
+316        q.insert(cid(0, 0)).unwrap();
+317    }
+318
+319    #[test]
+320    fn insert_retired() {
+321        let mut q = CidQueue::new(initial_cid());
+322        assert_eq!(
+323            q.insert(cid(0, 0)),
+324            Ok(None),
+325            "reinserting active CID succeeds"
+326        );
+327        assert!(q.next().is_none(), "active CID isn't requeued");
+328        q.insert(cid(1, 0)).unwrap();
+329        q.next().unwrap();
+330        assert_eq!(
+331            q.insert(cid(0, 0)),
+332            Err(InsertError::Retired),
+333            "previous active CID is already retired"
+334        );
+335    }
+336
+337    #[test]
+338    fn retire_then_insert_next() {
+339        let mut q = CidQueue::new(initial_cid());
+340        for i in 1..CidQueue::LEN as u64 {
+341            q.insert(cid(i, 0)).unwrap();
+342        }
+343        q.next().unwrap();
+344        q.insert(cid(CidQueue::LEN as u64, 0)).unwrap();
+345        assert_eq!(
+346            q.insert(cid(CidQueue::LEN as u64 + 1, 0)),
+347            Err(InsertError::ExceedsLimit)
+348        );
+349    }
+350
+351    #[test]
+352    fn always_valid() {
+353        let mut q = CidQueue::new(initial_cid());
+354        assert!(q.next().is_none());
+355        assert_eq!(q.active(), initial_cid());
+356        assert_eq!(q.active_seq(), 0);
+357    }
+358
+359    #[test]
+360    fn reserved_smoke() {
+361        let mut q = CidQueue::new(initial_cid());
+362        assert_eq!(q.next_reserved(), None);
+363
+364        let one = cid(1, 0);
+365        q.insert(one).unwrap();
+366        assert_eq!(q.next_reserved(), Some(one.id));
+367
+368        let two = cid(2, 2);
+369        let (retired_range, reset_token) = q.insert(two).unwrap().unwrap();
+370        assert_eq!(reset_token, two.reset_token);
+371        assert_eq!(retired_range, 0..2);
+372
+373        assert_eq!(q.next_reserved(), None);
+374
+375        let four = cid(4, 2);
+376        q.insert(four).unwrap();
+377        println!("{q:?}");
+378        assert_eq!(q.next_reserved(), Some(four.id));
+379        assert_eq!(q.active(), two.id);
+380
+381        assert_eq!(q.next(), None);
+382    }
+383
+384    #[test]
+385    fn reserve_multiple() {
+386        let mut q = CidQueue::new(initial_cid());
+387        let one = cid(1, 0);
+388        let two = cid(2, 0);
+389        q.insert(one).unwrap();
+390        q.insert(two).unwrap();
+391        assert_eq!(q.next_reserved(), Some(one.id));
+392        assert_eq!(q.next_reserved(), Some(two.id));
+393        assert_eq!(q.next_reserved(), None);
+394    }
+395
+396    #[test]
+397    fn reserve_multiple_sparse() {
+398        let mut q = CidQueue::new(initial_cid());
+399        let two = cid(2, 0);
+400        let four = cid(4, 0);
+401        q.insert(two).unwrap();
+402        q.insert(four).unwrap();
+403        assert_eq!(q.next_reserved(), Some(two.id));
+404        assert_eq!(q.next_reserved(), Some(four.id));
+405        assert_eq!(q.next_reserved(), None);
+406    }
+407
+408    #[test]
+409    fn reserve_many_next_clears() {
+410        let mut q = CidQueue::new(initial_cid());
+411        for i in 1..CidQueue::LEN {
+412            q.insert(cid(i as u64, 0)).unwrap();
+413        }
+414
+415        for _ in 0..CidQueue::LEN - 2 {
+416            assert!(q.next_reserved().is_some());
+417        }
+418
+419        assert!(q.next().is_some());
+420        assert_eq!(q.next(), None);
+421    }
+422
+423    #[test]
+424    fn reserve_many_next_reserved_none() {
+425        let mut q = CidQueue::new(initial_cid());
+426        for i in 1..CidQueue::LEN {
+427            q.insert(cid(i as u64, 0)).unwrap();
+428        }
+429
+430        for _ in 0..CidQueue::LEN - 1 {
+431            assert!(q.next_reserved().is_some());
+432        }
+433
+434        assert_eq!(q.next_reserved(), None);
+435    }
+436
+437    #[test]
+438    fn one_active_all_else_reserved_next_none() {
+439        let mut q = CidQueue::new(initial_cid());
+440        for i in 1..CidQueue::LEN {
+441            q.insert(cid(i as u64, 0)).unwrap();
+442        }
+443
+444        for _ in 0..CidQueue::LEN - 1 {
+445            assert!(q.next_reserved().is_some());
+446        }
+447
+448        assert_eq!(q.next(), None);
+449    }
+450
+451    #[test]
+452    fn insert_reserve_advance() {
+453        let mut q = CidQueue::new(initial_cid());
+454
+455        let first = cid(1, 0);
+456        let second = cid(2, 0);
+457        let third = cid(3, 0);
+458
+459        q.insert(first).unwrap();
+460        q.insert(second).unwrap();
+461
+462        assert_eq!(q.next_reserved(), Some(first.id));
+463        q.insert(third).unwrap();
+464        q.next();
+465        assert_eq!(q.active(), second.id);
+466    }
+467
+468    #[test]
+469    fn sparse_insert_reserve_insert_advance() {
+470        let mut q = CidQueue::new(initial_cid());
+471
+472        let one = cid(1, 0);
+473        let two = cid(2, 0);
+474        let three = cid(3, 0);
+475
+476        q.insert(two).unwrap();
+477        q.insert(three).unwrap();
+478        assert_eq!(q.next_reserved(), Some(two.id));
+479        q.insert(one).unwrap();
+480        q.next();
+481        assert_eq!(q.active(), three.id);
+482        assert_eq!(q.next_reserved(), None);
+483    }
+484}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/coding.rs.html b/pr/395/docs/src/iroh_quinn_proto/coding.rs.html new file mode 100644 index 000000000..cb4b3a460 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/coding.rs.html @@ -0,0 +1,152 @@ +coding.rs - source

iroh_quinn_proto/
coding.rs

1//! Coding related traits.
+2
+3use std::net::{Ipv4Addr, Ipv6Addr};
+4
+5use bytes::{Buf, BufMut};
+6use thiserror::Error;
+7
+8use crate::VarInt;
+9
+10/// Error indicating that the provided buffer was too small
+11#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)]
+12#[error("unexpected end of buffer")]
+13pub struct UnexpectedEnd;
+14
+15/// Coding result type
+16pub type Result<T> = ::std::result::Result<T, UnexpectedEnd>;
+17
+18/// Infallible encoding of QUIC primitives.
+19pub trait Encodable {
+20    /// Append the encoding of `self` to the provided buffer.
+21    fn encode<B: BufMut>(&self, buf: &mut B);
+22}
+23
+24/// Decoding of QUIC primitives.
+25pub trait Decodable: Sized {
+26    /// Decode a `Self` from the provided buffer, if the buffer is large enough
+27    fn decode<B: Buf>(buf: &mut B) -> Result<Self>;
+28}
+29
+30impl Decodable for u8 {
+31    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+32        if buf.remaining() < 1 {
+33            return Err(UnexpectedEnd);
+34        }
+35        Ok(buf.get_u8())
+36    }
+37}
+38
+39impl Encodable for u8 {
+40    fn encode<B: BufMut>(&self, buf: &mut B) {
+41        buf.put_u8(*self);
+42    }
+43}
+44
+45impl Decodable for u16 {
+46    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+47        if buf.remaining() < 2 {
+48            return Err(UnexpectedEnd);
+49        }
+50        Ok(buf.get_u16())
+51    }
+52}
+53
+54impl Encodable for u16 {
+55    fn encode<B: BufMut>(&self, buf: &mut B) {
+56        buf.put_u16(*self);
+57    }
+58}
+59
+60impl Decodable for u32 {
+61    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+62        if buf.remaining() < 4 {
+63            return Err(UnexpectedEnd);
+64        }
+65        Ok(buf.get_u32())
+66    }
+67}
+68
+69impl Encodable for u32 {
+70    fn encode<B: BufMut>(&self, buf: &mut B) {
+71        buf.put_u32(*self);
+72    }
+73}
+74
+75impl Decodable for u64 {
+76    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+77        if buf.remaining() < 8 {
+78            return Err(UnexpectedEnd);
+79        }
+80        Ok(buf.get_u64())
+81    }
+82}
+83
+84impl Encodable for u64 {
+85    fn encode<B: BufMut>(&self, buf: &mut B) {
+86        buf.put_u64(*self);
+87    }
+88}
+89
+90impl Decodable for Ipv4Addr {
+91    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+92        if buf.remaining() < 4 {
+93            return Err(UnexpectedEnd);
+94        }
+95        let mut octets = [0; 4];
+96        buf.copy_to_slice(&mut octets);
+97        Ok(octets.into())
+98    }
+99}
+100
+101impl Encodable for Ipv4Addr {
+102    fn encode<B: BufMut>(&self, buf: &mut B) {
+103        buf.put_slice(&self.octets());
+104    }
+105}
+106
+107impl Decodable for Ipv6Addr {
+108    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+109        if buf.remaining() < 16 {
+110            return Err(UnexpectedEnd);
+111        }
+112        let mut octets = [0; 16];
+113        buf.copy_to_slice(&mut octets);
+114        Ok(octets.into())
+115    }
+116}
+117
+118impl Encodable for Ipv6Addr {
+119    fn encode<B: BufMut>(&self, buf: &mut B) {
+120        buf.put_slice(&self.octets());
+121    }
+122}
+123
+124pub(crate) trait BufExt {
+125    fn get<T: Decodable>(&mut self) -> Result<T>;
+126    fn get_var(&mut self) -> Result<u64>;
+127}
+128
+129impl<T: Buf> BufExt for T {
+130    fn get<U: Decodable>(&mut self) -> Result<U> {
+131        U::decode(self)
+132    }
+133
+134    fn get_var(&mut self) -> Result<u64> {
+135        Ok(VarInt::decode(self)?.into_inner())
+136    }
+137}
+138
+139pub(crate) trait BufMutExt {
+140    fn write<T: Encodable>(&mut self, x: T);
+141    fn write_var(&mut self, x: u64);
+142}
+143
+144impl<T: BufMut> BufMutExt for T {
+145    fn write<U: Encodable>(&mut self, x: U) {
+146        x.encode(self);
+147    }
+148
+149    fn write_var(&mut self, x: u64) {
+150        VarInt::from_u64(x).unwrap().encode(self);
+151    }
+152}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/config/mod.rs.html b/pr/395/docs/src/iroh_quinn_proto/config/mod.rs.html new file mode 100644 index 000000000..093d67514 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/config/mod.rs.html @@ -0,0 +1,696 @@ +mod.rs - source

iroh_quinn_proto/config/
mod.rs

1use std::{
+2    fmt,
+3    net::{SocketAddrV4, SocketAddrV6},
+4    num::TryFromIntError,
+5    sync::Arc,
+6};
+7
+8#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+9use rustls::client::WebPkiServerVerifier;
+10#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+11use rustls::pki_types::{CertificateDer, PrivateKeyDer};
+12use thiserror::Error;
+13
+14#[cfg(feature = "bloom")]
+15use crate::BloomTokenLog;
+16#[cfg(not(feature = "bloom"))]
+17use crate::NoneTokenLog;
+18#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+19use crate::crypto::rustls::{QuicServerConfig, configured_provider};
+20use crate::{
+21    DEFAULT_SUPPORTED_VERSIONS, Duration, MAX_CID_SIZE, RandomConnectionIdGenerator, SystemTime,
+22    TokenLog, TokenMemoryCache, TokenStore, VarInt, VarIntBoundsExceeded,
+23    cid_generator::{ConnectionIdGenerator, HashedConnectionIdGenerator},
+24    crypto::{self, HandshakeTokenKey, HmacKey},
+25    shared::ConnectionId,
+26};
+27
+28#[cfg(feature = "qlog")]
+29mod qlog;
+30mod transport;
+31
+32#[cfg(feature = "qlog")]
+33pub use qlog::{QlogConfig, QlogFactory, QlogFileFactory};
+34pub use transport::{AckFrequencyConfig, IdleTimeout, MtuDiscoveryConfig, TransportConfig};
+35
+36#[cfg(doc)]
+37pub use transport::DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED;
+38
+39/// Global configuration for the endpoint, affecting all connections
+40///
+41/// Default values should be suitable for most internet applications.
+42#[derive(Clone)]
+43pub struct EndpointConfig {
+44    pub(crate) reset_key: Arc<dyn HmacKey>,
+45    pub(crate) max_udp_payload_size: VarInt,
+46    /// CID generator factory
+47    ///
+48    /// Create a cid generator for local cid in Endpoint struct
+49    pub(crate) connection_id_generator_factory:
+50        Arc<dyn Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync>,
+51    pub(crate) supported_versions: Vec<u32>,
+52    pub(crate) grease_quic_bit: bool,
+53    /// Minimum interval between outgoing stateless reset packets
+54    pub(crate) min_reset_interval: Duration,
+55    /// Optional seed to be used internally for random number generation
+56    pub(crate) rng_seed: Option<[u8; 32]>,
+57}
+58
+59impl EndpointConfig {
+60    /// Create a default config with a particular `reset_key`
+61    pub fn new(reset_key: Arc<dyn HmacKey>) -> Self {
+62        let cid_factory =
+63            || -> Box<dyn ConnectionIdGenerator> { Box::<HashedConnectionIdGenerator>::default() };
+64        Self {
+65            reset_key,
+66            max_udp_payload_size: (1500u32 - 28).into(), // Ethernet MTU minus IP + UDP headers
+67            connection_id_generator_factory: Arc::new(cid_factory),
+68            supported_versions: DEFAULT_SUPPORTED_VERSIONS.to_vec(),
+69            grease_quic_bit: true,
+70            min_reset_interval: Duration::from_millis(20),
+71            rng_seed: None,
+72        }
+73    }
+74
+75    /// Supply a custom connection ID generator factory
+76    ///
+77    /// Called once by each `Endpoint` constructed from this configuration to obtain the CID
+78    /// generator which will be used to generate the CIDs used for incoming packets on all
+79    /// connections involving that  `Endpoint`. A custom CID generator allows applications to embed
+80    /// information in local connection IDs, e.g. to support stateless packet-level load balancers.
+81    ///
+82    /// Defaults to [`HashedConnectionIdGenerator`].
+83    pub fn cid_generator<F: Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync + 'static>(
+84        &mut self,
+85        factory: F,
+86    ) -> &mut Self {
+87        self.connection_id_generator_factory = Arc::new(factory);
+88        self
+89    }
+90
+91    /// Private key used to send authenticated connection resets to peers who were
+92    /// communicating with a previous instance of this endpoint.
+93    pub fn reset_key(&mut self, key: Arc<dyn HmacKey>) -> &mut Self {
+94        self.reset_key = key;
+95        self
+96    }
+97
+98    /// Maximum UDP payload size accepted from peers (excluding UDP and IP overhead).
+99    ///
+100    /// Must be greater or equal than 1200.
+101    ///
+102    /// Defaults to 1472, which is the largest UDP payload that can be transmitted in the typical
+103    /// 1500 byte Ethernet MTU. Deployments on links with larger MTUs (e.g. loopback or Ethernet
+104    /// with jumbo frames) can raise this to improve performance at the cost of a linear increase in
+105    /// datagram receive buffer size.
+106    pub fn max_udp_payload_size(&mut self, value: u16) -> Result<&mut Self, ConfigError> {
+107        if !(1200..=65_527).contains(&value) {
+108            return Err(ConfigError::OutOfBounds);
+109        }
+110
+111        self.max_udp_payload_size = value.into();
+112        Ok(self)
+113    }
+114
+115    /// Get the current value of [`max_udp_payload_size`](Self::max_udp_payload_size)
+116    //
+117    // While most parameters don't need to be readable, this must be exposed to allow higher-level
+118    // layers, e.g. the `quinn` crate, to determine how large a receive buffer to allocate to
+119    // support an externally-defined `EndpointConfig`.
+120    //
+121    // While `get_` accessors are typically unidiomatic in Rust, we favor concision for setters,
+122    // which will be used far more heavily.
+123    pub fn get_max_udp_payload_size(&self) -> u64 {
+124        self.max_udp_payload_size.into()
+125    }
+126
+127    /// Override supported QUIC versions
+128    pub fn supported_versions(&mut self, supported_versions: Vec<u32>) -> &mut Self {
+129        self.supported_versions = supported_versions;
+130        self
+131    }
+132
+133    /// Whether to accept QUIC packets containing any value for the fixed bit
+134    ///
+135    /// Enabled by default. Helps protect against protocol ossification and makes traffic less
+136    /// identifiable to observers. Disable if helping observers identify this traffic as QUIC is
+137    /// desired.
+138    pub fn grease_quic_bit(&mut self, value: bool) -> &mut Self {
+139        self.grease_quic_bit = value;
+140        self
+141    }
+142
+143    /// Minimum interval between outgoing stateless reset packets
+144    ///
+145    /// Defaults to 20ms. Limits the impact of attacks which flood an endpoint with garbage packets,
+146    /// e.g. [ISAKMP/IKE amplification]. Larger values provide a stronger defense, but may delay
+147    /// detection of some error conditions by clients. Using a [`ConnectionIdGenerator`] with a low
+148    /// rate of false positives in [`validate`](ConnectionIdGenerator::validate) reduces the risk
+149    /// incurred by a small minimum reset interval.
+150    ///
+151    /// [ISAKMP/IKE
+152    /// amplification]: https://bughunters.google.com/blog/5960150648750080/preventing-cross-service-udp-loops-in-quic#isakmp-ike-amplification-vs-quic
+153    pub fn min_reset_interval(&mut self, value: Duration) -> &mut Self {
+154        self.min_reset_interval = value;
+155        self
+156    }
+157
+158    /// Optional seed to be used internally for random number generation
+159    ///
+160    /// By default, quinn will initialize an endpoint's rng using a platform entropy source.
+161    /// However, you can seed the rng yourself through this method (e.g. if you need to run quinn
+162    /// deterministically or if you are using quinn in an environment that doesn't have a source of
+163    /// entropy available).
+164    pub fn rng_seed(&mut self, seed: Option<[u8; 32]>) -> &mut Self {
+165        self.rng_seed = seed;
+166        self
+167    }
+168}
+169
+170impl fmt::Debug for EndpointConfig {
+171    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+172        fmt.debug_struct("EndpointConfig")
+173            // reset_key not debug
+174            .field("max_udp_payload_size", &self.max_udp_payload_size)
+175            // cid_generator_factory not debug
+176            .field("supported_versions", &self.supported_versions)
+177            .field("grease_quic_bit", &self.grease_quic_bit)
+178            .field("rng_seed", &self.rng_seed)
+179            .finish_non_exhaustive()
+180    }
+181}
+182
+183#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+184impl Default for EndpointConfig {
+185    fn default() -> Self {
+186        #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+187        use aws_lc_rs::hmac;
+188        use rand::RngCore;
+189        #[cfg(feature = "ring")]
+190        use ring::hmac;
+191
+192        let mut reset_key = [0; 64];
+193        rand::rng().fill_bytes(&mut reset_key);
+194
+195        Self::new(Arc::new(hmac::Key::new(hmac::HMAC_SHA256, &reset_key)))
+196    }
+197}
+198
+199/// Parameters governing incoming connections
+200///
+201/// Default values should be suitable for most internet applications.
+202#[derive(Clone)]
+203pub struct ServerConfig {
+204    /// Transport configuration to use for incoming connections
+205    pub transport: Arc<TransportConfig>,
+206
+207    /// TLS configuration used for incoming connections
+208    ///
+209    /// Must be set to use TLS 1.3 only.
+210    pub crypto: Arc<dyn crypto::ServerConfig>,
+211
+212    /// Configuration for sending and handling validation tokens
+213    pub validation_token: ValidationTokenConfig,
+214
+215    /// Used to generate one-time AEAD keys to protect handshake tokens
+216    pub(crate) token_key: Arc<dyn HandshakeTokenKey>,
+217
+218    /// Duration after a retry token was issued for which it's considered valid
+219    pub(crate) retry_token_lifetime: Duration,
+220
+221    /// Whether to allow clients to migrate to new addresses
+222    ///
+223    /// Improves behavior for clients that move between different internet connections or suffer NAT
+224    /// rebinding. Enabled by default.
+225    pub(crate) migration: bool,
+226
+227    pub(crate) preferred_address_v4: Option<SocketAddrV4>,
+228    pub(crate) preferred_address_v6: Option<SocketAddrV6>,
+229
+230    pub(crate) max_incoming: usize,
+231    pub(crate) incoming_buffer_size: u64,
+232    pub(crate) incoming_buffer_size_total: u64,
+233
+234    pub(crate) time_source: Arc<dyn TimeSource>,
+235}
+236
+237impl ServerConfig {
+238    /// Create a default config with a particular handshake token key
+239    pub fn new(
+240        crypto: Arc<dyn crypto::ServerConfig>,
+241        token_key: Arc<dyn HandshakeTokenKey>,
+242    ) -> Self {
+243        Self {
+244            transport: Arc::new(TransportConfig::default()),
+245            crypto,
+246
+247            token_key,
+248            retry_token_lifetime: Duration::from_secs(15),
+249
+250            migration: true,
+251
+252            validation_token: ValidationTokenConfig::default(),
+253
+254            preferred_address_v4: None,
+255            preferred_address_v6: None,
+256
+257            max_incoming: 1 << 16,
+258            incoming_buffer_size: 10 << 20,
+259            incoming_buffer_size_total: 100 << 20,
+260
+261            time_source: Arc::new(StdSystemTime),
+262        }
+263    }
+264
+265    /// Set a custom [`TransportConfig`]
+266    pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
+267        self.transport = transport;
+268        self
+269    }
+270
+271    /// Set a custom [`ValidationTokenConfig`]
+272    pub fn validation_token_config(
+273        &mut self,
+274        validation_token: ValidationTokenConfig,
+275    ) -> &mut Self {
+276        self.validation_token = validation_token;
+277        self
+278    }
+279
+280    /// Private key used to authenticate data included in handshake tokens
+281    pub fn token_key(&mut self, value: Arc<dyn HandshakeTokenKey>) -> &mut Self {
+282        self.token_key = value;
+283        self
+284    }
+285
+286    /// Duration after a retry token was issued for which it's considered valid
+287    ///
+288    /// Defaults to 15 seconds.
+289    pub fn retry_token_lifetime(&mut self, value: Duration) -> &mut Self {
+290        self.retry_token_lifetime = value;
+291        self
+292    }
+293
+294    /// Whether to allow clients to migrate to new addresses
+295    ///
+296    /// Improves behavior for clients that move between different internet connections or suffer NAT
+297    /// rebinding. Enabled by default.
+298    pub fn migration(&mut self, value: bool) -> &mut Self {
+299        self.migration = value;
+300        self
+301    }
+302
+303    /// The preferred IPv4 address that will be communicated to clients during handshaking
+304    ///
+305    /// If the client is able to reach this address, it will switch to it.
+306    pub fn preferred_address_v4(&mut self, address: Option<SocketAddrV4>) -> &mut Self {
+307        self.preferred_address_v4 = address;
+308        self
+309    }
+310
+311    /// The preferred IPv6 address that will be communicated to clients during handshaking
+312    ///
+313    /// If the client is able to reach this address, it will switch to it.
+314    pub fn preferred_address_v6(&mut self, address: Option<SocketAddrV6>) -> &mut Self {
+315        self.preferred_address_v6 = address;
+316        self
+317    }
+318
+319    /// Maximum number of [`Incoming`][crate::Incoming] to allow to exist at a time
+320    ///
+321    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
+322    /// is received and stops existing when the application either accepts it or otherwise disposes
+323    /// of it. While this limit is reached, new incoming connection attempts are immediately
+324    /// refused. Larger values have greater worst-case memory consumption, but accommodate greater
+325    /// application latency in handling incoming connection attempts.
+326    ///
+327    /// The default value is set to 65536. With a typical Ethernet MTU of 1500 bytes, this limits
+328    /// memory consumption from this to under 100 MiB--a generous amount that still prevents memory
+329    /// exhaustion in most contexts.
+330    pub fn max_incoming(&mut self, max_incoming: usize) -> &mut Self {
+331        self.max_incoming = max_incoming;
+332        self
+333    }
+334
+335    /// Maximum number of received bytes to buffer for each [`Incoming`][crate::Incoming]
+336    ///
+337    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
+338    /// is received and stops existing when the application either accepts it or otherwise disposes
+339    /// of it. This limit governs only packets received within that period, and does not include
+340    /// the first packet. Packets received in excess of this limit are dropped, which may cause
+341    /// 0-RTT or handshake data to have to be retransmitted.
+342    ///
+343    /// The default value is set to 10 MiB--an amount such that in most situations a client would
+344    /// not transmit that much 0-RTT data faster than the server handles the corresponding
+345    /// [`Incoming`][crate::Incoming].
+346    pub fn incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self {
+347        self.incoming_buffer_size = incoming_buffer_size;
+348        self
+349    }
+350
+351    /// Maximum number of received bytes to buffer for all [`Incoming`][crate::Incoming]
+352    /// collectively
+353    ///
+354    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
+355    /// is received and stops existing when the application either accepts it or otherwise disposes
+356    /// of it. This limit governs only packets received within that period, and does not include
+357    /// the first packet. Packets received in excess of this limit are dropped, which may cause
+358    /// 0-RTT or handshake data to have to be retransmitted.
+359    ///
+360    /// The default value is set to 100 MiB--a generous amount that still prevents memory
+361    /// exhaustion in most contexts.
+362    pub fn incoming_buffer_size_total(&mut self, incoming_buffer_size_total: u64) -> &mut Self {
+363        self.incoming_buffer_size_total = incoming_buffer_size_total;
+364        self
+365    }
+366
+367    /// Object to get current [`SystemTime`]
+368    ///
+369    /// This exists to allow system time to be mocked in tests, or wherever else desired.
+370    ///
+371    /// Defaults to [`StdSystemTime`], which simply calls [`SystemTime::now()`](SystemTime::now).
+372    pub fn time_source(&mut self, time_source: Arc<dyn TimeSource>) -> &mut Self {
+373        self.time_source = time_source;
+374        self
+375    }
+376
+377    pub(crate) fn has_preferred_address(&self) -> bool {
+378        self.preferred_address_v4.is_some() || self.preferred_address_v6.is_some()
+379    }
+380}
+381
+382#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+383impl ServerConfig {
+384    /// Create a server config with the given certificate chain to be presented to clients
+385    ///
+386    /// Uses a randomized handshake token key.
+387    pub fn with_single_cert(
+388        cert_chain: Vec<CertificateDer<'static>>,
+389        key: PrivateKeyDer<'static>,
+390    ) -> Result<Self, rustls::Error> {
+391        Ok(Self::with_crypto(Arc::new(QuicServerConfig::new(
+392            cert_chain, key,
+393        )?)))
+394    }
+395}
+396
+397#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+398impl ServerConfig {
+399    /// Create a server config with the given [`crypto::ServerConfig`]
+400    ///
+401    /// Uses a randomized handshake token key.
+402    pub fn with_crypto(crypto: Arc<dyn crypto::ServerConfig>) -> Self {
+403        #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+404        use aws_lc_rs::hkdf;
+405        use rand::RngCore;
+406        #[cfg(feature = "ring")]
+407        use ring::hkdf;
+408
+409        let rng = &mut rand::rng();
+410        let mut master_key = [0u8; 64];
+411        rng.fill_bytes(&mut master_key);
+412        let master_key = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key);
+413
+414        Self::new(crypto, Arc::new(master_key))
+415    }
+416}
+417
+418impl fmt::Debug for ServerConfig {
+419    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+420        fmt.debug_struct("ServerConfig")
+421            .field("transport", &self.transport)
+422            // crypto not debug
+423            // token not debug
+424            .field("retry_token_lifetime", &self.retry_token_lifetime)
+425            .field("validation_token", &self.validation_token)
+426            .field("migration", &self.migration)
+427            .field("preferred_address_v4", &self.preferred_address_v4)
+428            .field("preferred_address_v6", &self.preferred_address_v6)
+429            .field("max_incoming", &self.max_incoming)
+430            .field("incoming_buffer_size", &self.incoming_buffer_size)
+431            .field(
+432                "incoming_buffer_size_total",
+433                &self.incoming_buffer_size_total,
+434            )
+435            // system_time_clock not debug
+436            .finish_non_exhaustive()
+437    }
+438}
+439
+440/// Configuration for sending and handling validation tokens in incoming connections
+441///
+442/// Default values should be suitable for most internet applications.
+443///
+444/// ## QUIC Tokens
+445///
+446/// The QUIC protocol defines a concept of "[address validation][1]". Essentially, one side of a
+447/// QUIC connection may appear to be receiving QUIC packets from a particular remote UDP address,
+448/// but it will only consider that remote address "validated" once it has convincing evidence that
+449/// the address is not being [spoofed][2].
+450///
+451/// Validation is important primarily because of QUIC's "anti-amplification limit." This limit
+452/// prevents a QUIC server from sending a client more than three times the number of bytes it has
+453/// received from the client on a given address until that address is validated. This is designed
+454/// to mitigate the ability of attackers to use QUIC-based servers as reflectors in [amplification
+455/// attacks][3].
+456///
+457/// A path may become validated in several ways. The server is always considered validated by the
+458/// client. The client usually begins in an unvalidated state upon first connecting or migrating,
+459/// but then becomes validated through various mechanisms that usually take one network round trip.
+460/// However, in some cases, a client which has previously attempted to connect to a server may have
+461/// been given a one-time use cryptographically secured "token" that it can send in a subsequent
+462/// connection attempt to be validated immediately.
+463///
+464/// There are two ways these tokens can originate:
+465///
+466/// - If the server responds to an incoming connection with `retry`, a "retry token" is minted and
+467///   sent to the client, which the client immediately uses to attempt to connect again. Retry
+468///   tokens operate on short timescales, such as 15 seconds.
+469/// - If a client's path within an active connection is validated, the server may send the client
+470///   one or more "validation tokens," which the client may store for use in later connections to
+471///   the same server. Validation tokens may be valid for much longer lifetimes than retry token.
+472///
+473/// The usage of validation tokens is most impactful in situations where 0-RTT data is also being
+474/// used--in particular, in situations where the server sends the client more than three times more
+475/// 0.5-RTT data than it has received 0-RTT data. Since the successful completion of a connection
+476/// handshake implicitly causes the client's address to be validated, transmission of 0.5-RTT data
+477/// is the main situation where a server might be sending application data to an address that could
+478/// be validated by token usage earlier than it would become validated without token usage.
+479///
+480/// [1]: https://www.rfc-editor.org/rfc/rfc9000.html#section-8
+481/// [2]: https://en.wikipedia.org/wiki/IP_address_spoofing
+482/// [3]: https://en.wikipedia.org/wiki/Denial-of-service_attack#Amplification
+483///
+484/// These tokens should not be confused with "stateless reset tokens," which are similarly named
+485/// but entirely unrelated.
+486#[derive(Clone)]
+487pub struct ValidationTokenConfig {
+488    pub(crate) lifetime: Duration,
+489    pub(crate) log: Arc<dyn TokenLog>,
+490    pub(crate) sent: u32,
+491}
+492
+493impl ValidationTokenConfig {
+494    /// Duration after an address validation token was issued for which it's considered valid
+495    ///
+496    /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
+497    ///
+498    /// Defaults to 2 weeks.
+499    pub fn lifetime(&mut self, value: Duration) -> &mut Self {
+500        self.lifetime = value;
+501        self
+502    }
+503
+504    #[allow(rustdoc::redundant_explicit_links)] // which links are redundant depends on features
+505    /// Set a custom [`TokenLog`]
+506    ///
+507    /// If the `bloom` feature is enabled (which it is by default), defaults to a default
+508    /// [`BloomTokenLog`][crate::BloomTokenLog], which is suitable for most internet applications.
+509    ///
+510    /// If the `bloom` feature is disabled, defaults to [`NoneTokenLog`][crate::NoneTokenLog],
+511    /// which makes the server ignore all address validation tokens (that is, tokens originating
+512    /// from NEW_TOKEN frames--retry tokens are not affected).
+513    pub fn log(&mut self, log: Arc<dyn TokenLog>) -> &mut Self {
+514        self.log = log;
+515        self
+516    }
+517
+518    /// Number of address validation tokens sent to a client when its path is validated
+519    ///
+520    /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
+521    ///
+522    /// If the `bloom` feature is enabled (which it is by default), defaults to 2. Otherwise,
+523    /// defaults to 0.
+524    pub fn sent(&mut self, value: u32) -> &mut Self {
+525        self.sent = value;
+526        self
+527    }
+528}
+529
+530impl Default for ValidationTokenConfig {
+531    fn default() -> Self {
+532        #[cfg(feature = "bloom")]
+533        let log = Arc::new(BloomTokenLog::default());
+534        #[cfg(not(feature = "bloom"))]
+535        let log = Arc::new(NoneTokenLog);
+536        Self {
+537            lifetime: Duration::from_secs(2 * 7 * 24 * 60 * 60),
+538            log,
+539            sent: if cfg!(feature = "bloom") { 2 } else { 0 },
+540        }
+541    }
+542}
+543
+544impl fmt::Debug for ValidationTokenConfig {
+545    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+546        fmt.debug_struct("ServerValidationTokenConfig")
+547            .field("lifetime", &self.lifetime)
+548            // log not debug
+549            .field("sent", &self.sent)
+550            .finish_non_exhaustive()
+551    }
+552}
+553
+554/// Configuration for outgoing connections
+555///
+556/// Default values should be suitable for most internet applications.
+557#[derive(Clone)]
+558#[non_exhaustive]
+559pub struct ClientConfig {
+560    /// Transport configuration to use
+561    pub(crate) transport: Arc<TransportConfig>,
+562
+563    /// Cryptographic configuration to use
+564    pub(crate) crypto: Arc<dyn crypto::ClientConfig>,
+565
+566    /// Validation token store to use
+567    pub(crate) token_store: Arc<dyn TokenStore>,
+568
+569    /// Provider that populates the destination connection ID of Initial Packets
+570    pub(crate) initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
+571
+572    /// QUIC protocol version to use
+573    pub(crate) version: u32,
+574}
+575
+576impl ClientConfig {
+577    /// Create a default config with a particular cryptographic config
+578    pub fn new(crypto: Arc<dyn crypto::ClientConfig>) -> Self {
+579        Self {
+580            transport: Default::default(),
+581            crypto,
+582            token_store: Arc::new(TokenMemoryCache::default()),
+583            initial_dst_cid_provider: Arc::new(|| {
+584                RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid()
+585            }),
+586            version: 1,
+587        }
+588    }
+589
+590    /// Configure how to populate the destination CID of the initial packet when attempting to
+591    /// establish a new connection
+592    ///
+593    /// By default, it's populated with random bytes with reasonable length, so unless you have
+594    /// a good reason, you do not need to change it.
+595    ///
+596    /// When prefer to override the default, please note that the generated connection ID MUST be
+597    /// at least 8 bytes long and unpredictable, as per section 7.2 of RFC 9000.
+598    pub fn initial_dst_cid_provider(
+599        &mut self,
+600        initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
+601    ) -> &mut Self {
+602        self.initial_dst_cid_provider = initial_dst_cid_provider;
+603        self
+604    }
+605
+606    /// Set a custom [`TransportConfig`]
+607    pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
+608        self.transport = transport;
+609        self
+610    }
+611
+612    /// Set a custom [`TokenStore`]
+613    ///
+614    /// Defaults to [`TokenMemoryCache`], which is suitable for most internet applications.
+615    pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut Self {
+616        self.token_store = store;
+617        self
+618    }
+619
+620    /// Set the QUIC version to use
+621    pub fn version(&mut self, version: u32) -> &mut Self {
+622        self.version = version;
+623        self
+624    }
+625}
+626
+627#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+628impl ClientConfig {
+629    /// Create a client configuration that trusts the platform's native roots
+630    #[cfg(feature = "platform-verifier")]
+631    pub fn try_with_platform_verifier() -> Result<Self, rustls::Error> {
+632        Ok(Self::new(Arc::new(
+633            crypto::rustls::QuicClientConfig::with_platform_verifier()?,
+634        )))
+635    }
+636
+637    /// Create a client configuration that trusts specified trust anchors
+638    pub fn with_root_certificates(
+639        roots: Arc<rustls::RootCertStore>,
+640    ) -> Result<Self, rustls::client::VerifierBuilderError> {
+641        Ok(Self::new(Arc::new(crypto::rustls::QuicClientConfig::new(
+642            WebPkiServerVerifier::builder_with_provider(roots, configured_provider()).build()?,
+643        ))))
+644    }
+645}
+646
+647impl fmt::Debug for ClientConfig {
+648    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+649        fmt.debug_struct("ClientConfig")
+650            .field("transport", &self.transport)
+651            // crypto not debug
+652            // token_store not debug
+653            .field("version", &self.version)
+654            .finish_non_exhaustive()
+655    }
+656}
+657
+658/// Errors in the configuration of an endpoint
+659#[derive(Debug, Error, Clone, PartialEq, Eq)]
+660#[non_exhaustive]
+661pub enum ConfigError {
+662    /// Value exceeds supported bounds
+663    #[error("value exceeds supported bounds")]
+664    OutOfBounds,
+665}
+666
+667impl From<TryFromIntError> for ConfigError {
+668    fn from(_: TryFromIntError) -> Self {
+669        Self::OutOfBounds
+670    }
+671}
+672
+673impl From<VarIntBoundsExceeded> for ConfigError {
+674    fn from(_: VarIntBoundsExceeded) -> Self {
+675        Self::OutOfBounds
+676    }
+677}
+678
+679/// Object to get current [`SystemTime`]
+680///
+681/// This exists to allow system time to be mocked in tests, or wherever else desired.
+682pub trait TimeSource: Send + Sync {
+683    /// Get [`SystemTime::now()`](SystemTime::now) or the mocked equivalent
+684    fn now(&self) -> SystemTime;
+685}
+686
+687/// Default implementation of [`TimeSource`]
+688///
+689/// Implements `now` by calling [`SystemTime::now()`](SystemTime::now).
+690pub struct StdSystemTime;
+691
+692impl TimeSource for StdSystemTime {
+693    fn now(&self) -> SystemTime {
+694        SystemTime::now()
+695    }
+696}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/config/qlog.rs.html b/pr/395/docs/src/iroh_quinn_proto/config/qlog.rs.html new file mode 100644 index 000000000..faa2f75fd --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/config/qlog.rs.html @@ -0,0 +1,173 @@ +qlog.rs - source

iroh_quinn_proto/config/
qlog.rs

1#[cfg(feature = "qlog")]
+2use std::io;
+3use std::{io::BufWriter, net::SocketAddr, path::PathBuf, time::SystemTime};
+4
+5use tracing::{trace, warn};
+6
+7use crate::{ConnectionId, Instant, Side};
+8
+9/// Constructs a [`QlogConfig`] for individual connections.
+10///
+11/// This is set via [`TransportConfig::qlog_factory`].
+12///
+13/// [`TransportConfig::qlog_factory`]: crate::TransportConfig::qlog_factory
+14pub trait QlogFactory: Send + Sync + 'static {
+15    /// Returns a [`QlogConfig`] for a connection, if logging should be enabled.
+16    ///
+17    /// If `None` is returned, qlog capture is disabled for the connection.
+18    fn for_connection(
+19        &self,
+20        side: Side,
+21        remote: SocketAddr,
+22        initial_dst_cid: ConnectionId,
+23        now: Instant,
+24    ) -> Option<QlogConfig>;
+25}
+26
+27/// Configuration for qlog trace logging.
+28///
+29/// This struct is returned from [`QlogFactory::for_connection`] if qlog logging should
+30/// be enabled for a connection. It allows to set metadata for the qlog trace.
+31///
+32/// The trace will be written to the provided writer in the [`JSON-SEQ format`] defined in the qlog spec.
+33///
+34/// [`JSON-SEQ format`](https://www.ietf.org/archive/id/draft-ietf-quic-qlog-main-schema-13.html#section-5)
+35#[cfg(feature = "qlog")]
+36pub struct QlogConfig {
+37    pub(crate) writer: Box<dyn io::Write + Send + Sync>,
+38    pub(crate) title: Option<String>,
+39    pub(crate) description: Option<String>,
+40    pub(crate) start_time: Option<Instant>,
+41}
+42
+43#[cfg(feature = "qlog")]
+44impl QlogConfig {
+45    /// Creates a new [`QlogConfig`] that writes a qlog trace to the specified `writer`.
+46    pub fn new(writer: Box<dyn io::Write + Send + Sync>) -> Self {
+47        Self {
+48            writer,
+49            title: None,
+50            description: None,
+51            start_time: None,
+52        }
+53    }
+54
+55    /// Title to record in the qlog capture
+56    pub fn title(&mut self, title: Option<String>) -> &mut Self {
+57        self.title = title;
+58        self
+59    }
+60
+61    /// Description to record in the qlog capture
+62    pub fn description(&mut self, description: Option<String>) -> &mut Self {
+63        self.description = description;
+64        self
+65    }
+66
+67    /// Epoch qlog event times are recorded relative to
+68    ///
+69    /// If unset, the start of the connection is used.
+70    pub fn start_time(&mut self, start_time: Instant) -> &mut Self {
+71        self.start_time = Some(start_time);
+72        self
+73    }
+74}
+75
+76/// Enables writing qlog traces to a directory.
+77#[derive(Debug)]
+78pub struct QlogFileFactory {
+79    dir: Option<PathBuf>,
+80    prefix: Option<String>,
+81    start_instant: Option<Instant>,
+82}
+83
+84impl QlogFileFactory {
+85    /// Creates a new qlog factory that writes files into the specified directory.
+86    pub fn new(dir: PathBuf) -> Self {
+87        Self {
+88            dir: Some(dir),
+89            prefix: None,
+90            start_instant: None,
+91        }
+92    }
+93
+94    /// Creates a new qlog factory that writes files into `QLOGDIR`, if set.
+95    ///
+96    /// If the environment variable `QLOGDIR` is set, qlog traces for all connections handled
+97    /// by this endpoint will be written into that directory.
+98    /// If the directory doesn't exist it will be created.
+99    pub fn from_env() -> Self {
+100        let dir = match std::env::var("QLOGDIR") {
+101            Ok(dir) => {
+102                if let Err(err) = std::fs::create_dir_all(&dir) {
+103                    warn!("qlog not enabled: failed to create qlog directory at {dir}: {err}",);
+104                    None
+105                } else {
+106                    Some(PathBuf::from(dir))
+107                }
+108            }
+109            Err(_) => None,
+110        };
+111        Self {
+112            dir,
+113            prefix: None,
+114            start_instant: None,
+115        }
+116    }
+117
+118    /// Sets a prefix to the filename of the generated files.
+119    pub fn with_prefix(mut self, prefix: impl ToString) -> Self {
+120        self.prefix = Some(prefix.to_string());
+121        self
+122    }
+123
+124    /// Override the instant relative to which all events are recorded.
+125    ///
+126    /// If not set, events will be recorded relative to the start of the connection.
+127    pub fn with_start_instant(mut self, start: Instant) -> Self {
+128        self.start_instant = Some(start);
+129        self
+130    }
+131}
+132
+133impl QlogFactory for QlogFileFactory {
+134    fn for_connection(
+135        &self,
+136        side: Side,
+137        _remote: SocketAddr,
+138        initial_dst_cid: ConnectionId,
+139        now: Instant,
+140    ) -> Option<QlogConfig> {
+141        let dir = self.dir.as_ref()?;
+142
+143        let name = {
+144            let timestamp = SystemTime::now()
+145                .checked_sub(Instant::now().duration_since(now))?
+146                .duration_since(SystemTime::UNIX_EPOCH)
+147                .ok()?
+148                .as_millis();
+149            let prefix = self
+150                .prefix
+151                .as_ref()
+152                .filter(|prefix| !prefix.is_empty())
+153                .map(|prefix| format!("{prefix}-"))
+154                .unwrap_or_default();
+155            let side = format!("{side:?}").to_lowercase();
+156            format!("{prefix}{timestamp}-{initial_dst_cid}-{side}.qlog")
+157        };
+158        let path = dir.join(name);
+159        let file = std::fs::File::create(&path)
+160            .inspect_err(|err| warn!("Failed to create qlog file at {}: {err}", path.display()))
+161            .ok()?;
+162        trace!(
+163            "Initialized qlog file for connection {initial_dst_cid} at {}",
+164            path.display()
+165        );
+166        let writer = BufWriter::new(file);
+167        let mut config = QlogConfig::new(Box::new(writer));
+168        if let Some(instant) = self.start_instant {
+169            config.start_time(instant);
+170        }
+171        Some(config)
+172    }
+173}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/config/transport.rs.html b/pr/395/docs/src/iroh_quinn_proto/config/transport.rs.html new file mode 100644 index 000000000..dc343d2ce --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/config/transport.rs.html @@ -0,0 +1,912 @@ +transport.rs - source

iroh_quinn_proto/config/
transport.rs

1#[cfg(feature = "qlog")]
+2use std::path::Path;
+3use std::{
+4    fmt,
+5    net::SocketAddr,
+6    num::{NonZeroU8, NonZeroU32},
+7    sync::Arc,
+8};
+9
+10use crate::{
+11    ConnectionId, Duration, INITIAL_MTU, Instant, MAX_UDP_PAYLOAD, Side, VarInt,
+12    VarIntBoundsExceeded, address_discovery, congestion, connection::qlog::QlogSink,
+13};
+14#[cfg(feature = "qlog")]
+15use crate::{QlogFactory, QlogFileFactory};
+16
+17/// When multipath is required and has not been explicitly enabled, this value will be used for
+18/// [`TransportConfig::max_concurrent_multipath_paths`].
+19const DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED_: NonZeroU32 = {
+20    match NonZeroU32::new(12) {
+21        Some(v) => v,
+22        None => panic!("to enable multipath this must be positive, which clearly it is"),
+23    }
+24};
+25
+26/// When multipath is required and has not been explicitly enabled, this value will be used for
+27///
+28/// [`TransportConfig::max_concurrent_multipath_paths`].
+29#[cfg(doc)]
+30pub const DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED: NonZeroU32 =
+31    DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED_;
+32
+33/// Parameters governing the core QUIC state machine
+34///
+35/// Default values should be suitable for most internet applications. Applications protocols which
+36/// forbid remotely-initiated streams should set `max_concurrent_bidi_streams` and
+37/// `max_concurrent_uni_streams` to zero.
+38///
+39/// In some cases, performance or resource requirements can be improved by tuning these values to
+40/// suit a particular application and/or network connection. In particular, data window sizes can be
+41/// tuned for a particular expected round trip time, link capacity, and memory availability. Tuning
+42/// for higher bandwidths and latencies increases worst-case memory consumption, but does not impair
+43/// performance at lower bandwidths and latencies. The default configuration is tuned for a 100Mbps
+44/// link with a 100ms round trip time.
+45#[derive(Clone)]
+46pub struct TransportConfig {
+47    pub(crate) max_concurrent_bidi_streams: VarInt,
+48    pub(crate) max_concurrent_uni_streams: VarInt,
+49    pub(crate) max_idle_timeout: Option<VarInt>,
+50    pub(crate) stream_receive_window: VarInt,
+51    pub(crate) receive_window: VarInt,
+52    pub(crate) send_window: u64,
+53    pub(crate) send_fairness: bool,
+54
+55    pub(crate) packet_threshold: u32,
+56    pub(crate) time_threshold: f32,
+57    pub(crate) initial_rtt: Duration,
+58    pub(crate) initial_mtu: u16,
+59    pub(crate) min_mtu: u16,
+60    pub(crate) mtu_discovery_config: Option<MtuDiscoveryConfig>,
+61    pub(crate) pad_to_mtu: bool,
+62    pub(crate) ack_frequency_config: Option<AckFrequencyConfig>,
+63
+64    pub(crate) persistent_congestion_threshold: u32,
+65    pub(crate) keep_alive_interval: Option<Duration>,
+66    pub(crate) crypto_buffer_size: usize,
+67    pub(crate) allow_spin: bool,
+68    pub(crate) datagram_receive_buffer_size: Option<usize>,
+69    pub(crate) datagram_send_buffer_size: usize,
+70    #[cfg(test)]
+71    pub(crate) deterministic_packet_numbers: bool,
+72
+73    pub(crate) congestion_controller_factory: Arc<dyn congestion::ControllerFactory + Send + Sync>,
+74
+75    pub(crate) enable_segmentation_offload: bool,
+76
+77    pub(crate) address_discovery_role: address_discovery::Role,
+78
+79    pub(crate) max_concurrent_multipath_paths: Option<NonZeroU32>,
+80
+81    pub(crate) default_path_max_idle_timeout: Option<Duration>,
+82    pub(crate) default_path_keep_alive_interval: Option<Duration>,
+83
+84    pub(crate) max_remote_nat_traversal_addresses: Option<NonZeroU8>,
+85
+86    #[cfg(feature = "qlog")]
+87    pub(crate) qlog_factory: Option<Arc<dyn QlogFactory>>,
+88}
+89
+90impl TransportConfig {
+91    /// Maximum number of incoming bidirectional streams that may be open concurrently
+92    ///
+93    /// Must be nonzero for the peer to open any bidirectional streams.
+94    ///
+95    /// Worst-case memory use is directly proportional to `max_concurrent_bidi_streams *
+96    /// stream_receive_window`, with an upper bound proportional to `receive_window`.
+97    pub fn max_concurrent_bidi_streams(&mut self, value: VarInt) -> &mut Self {
+98        self.max_concurrent_bidi_streams = value;
+99        self
+100    }
+101
+102    /// Variant of `max_concurrent_bidi_streams` affecting unidirectional streams
+103    pub fn max_concurrent_uni_streams(&mut self, value: VarInt) -> &mut Self {
+104        self.max_concurrent_uni_streams = value;
+105        self
+106    }
+107
+108    /// Maximum duration of inactivity to accept before timing out the connection.
+109    ///
+110    /// The true idle timeout is the minimum of this and the peer's own max idle timeout. `None`
+111    /// represents an infinite timeout. Defaults to 30 seconds.
+112    ///
+113    /// **WARNING**: If a peer or its network path malfunctions or acts maliciously, an infinite
+114    /// idle timeout can result in permanently hung futures!
+115    ///
+116    /// ```
+117    /// # use std::{convert::TryInto, time::Duration};
+118    /// # use iroh_quinn_proto::{TransportConfig, VarInt, VarIntBoundsExceeded};
+119    /// # fn main() -> Result<(), VarIntBoundsExceeded> {
+120    /// let mut config = TransportConfig::default();
+121    ///
+122    /// // Set the idle timeout as `VarInt`-encoded milliseconds
+123    /// config.max_idle_timeout(Some(VarInt::from_u32(10_000).into()));
+124    ///
+125    /// // Set the idle timeout as a `Duration`
+126    /// config.max_idle_timeout(Some(Duration::from_secs(10).try_into()?));
+127    /// # Ok(())
+128    /// # }
+129    /// ```
+130    pub fn max_idle_timeout(&mut self, value: Option<IdleTimeout>) -> &mut Self {
+131        self.max_idle_timeout = value.map(|t| t.0);
+132        self
+133    }
+134
+135    /// Maximum number of bytes the peer may transmit without acknowledgement on any one stream
+136    /// before becoming blocked.
+137    ///
+138    /// This should be set to at least the expected connection latency multiplied by the maximum
+139    /// desired throughput. Setting this smaller than `receive_window` helps ensure that a single
+140    /// stream doesn't monopolize receive buffers, which may otherwise occur if the application
+141    /// chooses not to read from a large stream for a time while still requiring data on other
+142    /// streams.
+143    pub fn stream_receive_window(&mut self, value: VarInt) -> &mut Self {
+144        self.stream_receive_window = value;
+145        self
+146    }
+147
+148    /// Maximum number of bytes the peer may transmit across all streams of a connection before
+149    /// becoming blocked.
+150    ///
+151    /// This should be set to at least the expected connection latency multiplied by the maximum
+152    /// desired throughput. Larger values can be useful to allow maximum throughput within a
+153    /// stream while another is blocked.
+154    pub fn receive_window(&mut self, value: VarInt) -> &mut Self {
+155        self.receive_window = value;
+156        self
+157    }
+158
+159    /// Maximum number of bytes to transmit to a peer without acknowledgment
+160    ///
+161    /// Provides an upper bound on memory when communicating with peers that issue large amounts of
+162    /// flow control credit. Endpoints that wish to handle large numbers of connections robustly
+163    /// should take care to set this low enough to guarantee memory exhaustion does not occur if
+164    /// every connection uses the entire window.
+165    pub fn send_window(&mut self, value: u64) -> &mut Self {
+166        self.send_window = value;
+167        self
+168    }
+169
+170    /// Whether to implement fair queuing for send streams having the same priority.
+171    ///
+172    /// When enabled, connections schedule data from outgoing streams having the same priority in a
+173    /// round-robin fashion. When disabled, streams are scheduled in the order they are written to.
+174    ///
+175    /// Note that this only affects streams with the same priority. Higher priority streams always
+176    /// take precedence over lower priority streams.
+177    ///
+178    /// Disabling fairness can reduce fragmentation and protocol overhead for workloads that use
+179    /// many small streams.
+180    pub fn send_fairness(&mut self, value: bool) -> &mut Self {
+181        self.send_fairness = value;
+182        self
+183    }
+184
+185    /// Maximum reordering in packet number space before FACK style loss detection considers a
+186    /// packet lost. Should not be less than 3, per RFC5681.
+187    pub fn packet_threshold(&mut self, value: u32) -> &mut Self {
+188        self.packet_threshold = value;
+189        self
+190    }
+191
+192    /// Maximum reordering in time space before time based loss detection considers a packet lost,
+193    /// as a factor of RTT
+194    pub fn time_threshold(&mut self, value: f32) -> &mut Self {
+195        self.time_threshold = value;
+196        self
+197    }
+198
+199    /// The RTT used before an RTT sample is taken
+200    pub fn initial_rtt(&mut self, value: Duration) -> &mut Self {
+201        self.initial_rtt = value;
+202        self
+203    }
+204
+205    /// The initial value to be used as the maximum UDP payload size before running MTU discovery
+206    /// (see [`TransportConfig::mtu_discovery_config`]).
+207    ///
+208    /// Must be at least 1200, which is the default, and known to be safe for typical internet
+209    /// applications. Larger values are more efficient, but increase the risk of packet loss due to
+210    /// exceeding the network path's IP MTU. If the provided value is higher than what the network
+211    /// path actually supports, packet loss will eventually trigger black hole detection and bring
+212    /// it down to [`TransportConfig::min_mtu`].
+213    pub fn initial_mtu(&mut self, value: u16) -> &mut Self {
+214        self.initial_mtu = value.max(INITIAL_MTU);
+215        self
+216    }
+217
+218    pub(crate) fn get_initial_mtu(&self) -> u16 {
+219        self.initial_mtu.max(self.min_mtu)
+220    }
+221
+222    /// The maximum UDP payload size guaranteed to be supported by the network.
+223    ///
+224    /// Must be at least 1200, which is the default, and lower than or equal to
+225    /// [`TransportConfig::initial_mtu`].
+226    ///
+227    /// Real-world MTUs can vary according to ISP, VPN, and properties of intermediate network links
+228    /// outside of either endpoint's control. Extreme care should be used when raising this value
+229    /// outside of private networks where these factors are fully controlled. If the provided value
+230    /// is higher than what the network path actually supports, the result will be unpredictable and
+231    /// catastrophic packet loss, without a possibility of repair. Prefer
+232    /// [`TransportConfig::initial_mtu`] together with
+233    /// [`TransportConfig::mtu_discovery_config`] to set a maximum UDP payload size that robustly
+234    /// adapts to the network.
+235    pub fn min_mtu(&mut self, value: u16) -> &mut Self {
+236        self.min_mtu = value.max(INITIAL_MTU);
+237        self
+238    }
+239
+240    /// Specifies the MTU discovery config (see [`MtuDiscoveryConfig`] for details).
+241    ///
+242    /// Enabled by default.
+243    pub fn mtu_discovery_config(&mut self, value: Option<MtuDiscoveryConfig>) -> &mut Self {
+244        self.mtu_discovery_config = value;
+245        self
+246    }
+247
+248    /// Pad UDP datagrams carrying application data to current maximum UDP payload size
+249    ///
+250    /// Disabled by default. UDP datagrams containing loss probes are exempt from padding.
+251    ///
+252    /// Enabling this helps mitigate traffic analysis by network observers, but it increases
+253    /// bandwidth usage. Without this mitigation precise plain text size of application datagrams as
+254    /// well as the total size of stream write bursts can be inferred by observers under certain
+255    /// conditions. This analysis requires either an uncongested connection or application datagrams
+256    /// too large to be coalesced.
+257    pub fn pad_to_mtu(&mut self, value: bool) -> &mut Self {
+258        self.pad_to_mtu = value;
+259        self
+260    }
+261
+262    /// Specifies the ACK frequency config (see [`AckFrequencyConfig`] for details)
+263    ///
+264    /// The provided configuration will be ignored if the peer does not support the acknowledgement
+265    /// frequency QUIC extension.
+266    ///
+267    /// Defaults to `None`, which disables controlling the peer's acknowledgement frequency. Even
+268    /// if set to `None`, the local side still supports the acknowledgement frequency QUIC
+269    /// extension and may use it in other ways.
+270    pub fn ack_frequency_config(&mut self, value: Option<AckFrequencyConfig>) -> &mut Self {
+271        self.ack_frequency_config = value;
+272        self
+273    }
+274
+275    /// Number of consecutive PTOs after which network is considered to be experiencing persistent congestion.
+276    pub fn persistent_congestion_threshold(&mut self, value: u32) -> &mut Self {
+277        self.persistent_congestion_threshold = value;
+278        self
+279    }
+280
+281    /// Period of inactivity before sending a keep-alive packet
+282    ///
+283    /// Keep-alive packets prevent an inactive but otherwise healthy connection from timing out.
+284    ///
+285    /// `None` to disable, which is the default. Only one side of any given connection needs keep-alive
+286    /// enabled for the connection to be preserved. Must be set lower than the idle_timeout of both
+287    /// peers to be effective.
+288    pub fn keep_alive_interval(&mut self, value: Option<Duration>) -> &mut Self {
+289        self.keep_alive_interval = value;
+290        self
+291    }
+292
+293    /// Maximum quantity of out-of-order crypto layer data to buffer
+294    pub fn crypto_buffer_size(&mut self, value: usize) -> &mut Self {
+295        self.crypto_buffer_size = value;
+296        self
+297    }
+298
+299    /// Whether the implementation is permitted to set the spin bit on this connection
+300    ///
+301    /// This allows passive observers to easily judge the round trip time of a connection, which can
+302    /// be useful for network administration but sacrifices a small amount of privacy.
+303    pub fn allow_spin(&mut self, value: bool) -> &mut Self {
+304        self.allow_spin = value;
+305        self
+306    }
+307
+308    /// Maximum number of incoming application datagram bytes to buffer, or None to disable
+309    /// incoming datagrams
+310    ///
+311    /// The peer is forbidden to send single datagrams larger than this size. If the aggregate size
+312    /// of all datagrams that have been received from the peer but not consumed by the application
+313    /// exceeds this value, old datagrams are dropped until it is no longer exceeded.
+314    pub fn datagram_receive_buffer_size(&mut self, value: Option<usize>) -> &mut Self {
+315        self.datagram_receive_buffer_size = value;
+316        self
+317    }
+318
+319    /// Maximum number of outgoing application datagram bytes to buffer
+320    ///
+321    /// While datagrams are sent ASAP, it is possible for an application to generate data faster
+322    /// than the link, or even the underlying hardware, can transmit them. This limits the amount of
+323    /// memory that may be consumed in that case. When the send buffer is full and a new datagram is
+324    /// sent, older datagrams are dropped until sufficient space is available.
+325    pub fn datagram_send_buffer_size(&mut self, value: usize) -> &mut Self {
+326        self.datagram_send_buffer_size = value;
+327        self
+328    }
+329
+330    /// Whether to force every packet number to be used
+331    ///
+332    /// By default, packet numbers are occasionally skipped to ensure peers aren't ACKing packets
+333    /// before they see them.
+334    #[cfg(test)]
+335    pub(crate) fn deterministic_packet_numbers(&mut self, enabled: bool) -> &mut Self {
+336        self.deterministic_packet_numbers = enabled;
+337        self
+338    }
+339
+340    /// How to construct new `congestion::Controller`s
+341    ///
+342    /// Typically the refcounted configuration of a `congestion::Controller`,
+343    /// e.g. a `congestion::NewRenoConfig`.
+344    ///
+345    /// # Example
+346    /// ```
+347    /// # use iroh_quinn_proto::*; use std::sync::Arc;
+348    /// let mut config = TransportConfig::default();
+349    /// config.congestion_controller_factory(Arc::new(congestion::NewRenoConfig::default()));
+350    /// ```
+351    pub fn congestion_controller_factory(
+352        &mut self,
+353        factory: Arc<dyn congestion::ControllerFactory + Send + Sync + 'static>,
+354    ) -> &mut Self {
+355        self.congestion_controller_factory = factory;
+356        self
+357    }
+358
+359    /// Whether to use "Generic Segmentation Offload" to accelerate transmits, when supported by the
+360    /// environment
+361    ///
+362    /// Defaults to `true`.
+363    ///
+364    /// GSO dramatically reduces CPU consumption when sending large numbers of packets with the same
+365    /// headers, such as when transmitting bulk data on a connection. However, it is not supported
+366    /// by all network interface drivers or packet inspection tools. `quinn-udp` will attempt to
+367    /// disable GSO automatically when unavailable, but this can lead to spurious packet loss at
+368    /// startup, temporarily degrading performance.
+369    pub fn enable_segmentation_offload(&mut self, enabled: bool) -> &mut Self {
+370        self.enable_segmentation_offload = enabled;
+371        self
+372    }
+373
+374    /// Whether to send observed address reports to peers.
+375    ///
+376    /// This will aid peers in inferring their reachable address, which in most NATd networks
+377    /// will not be easily available to them.
+378    pub fn send_observed_address_reports(&mut self, enabled: bool) -> &mut Self {
+379        self.address_discovery_role.send_reports_to_peers(enabled);
+380        self
+381    }
+382
+383    /// Whether to receive observed address reports from other peers.
+384    ///
+385    /// Peers with the address discovery extension enabled that are willing to provide observed
+386    /// address reports will do so if this transport parameter is set. In general, observed address
+387    /// reports cannot be trusted. This, however, can aid the current endpoint in inferring its
+388    /// reachable address, which in most NATd networks will not be easily available.
+389    pub fn receive_observed_address_reports(&mut self, enabled: bool) -> &mut Self {
+390        self.address_discovery_role
+391            .receive_reports_from_peers(enabled);
+392        self
+393    }
+394
+395    /// Enables the Multipath Extension for QUIC.
+396    ///
+397    /// Setting this to any nonzero value will enable the Multipath Extension for QUIC,
+398    /// <https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/>.
+399    ///
+400    /// The value provided specifies the number maximum number of paths this endpoint may open
+401    /// concurrently when multipath is negotiated. For any path to be opened, the remote must
+402    /// enable multipath as well.
+403    pub fn max_concurrent_multipath_paths(&mut self, max_concurrent: u32) -> &mut Self {
+404        self.max_concurrent_multipath_paths = NonZeroU32::new(max_concurrent);
+405        self
+406    }
+407
+408    /// Sets a default per-path maximum idle timeout
+409    ///
+410    /// If the path is idle for this long the path will be abandoned. Bear in mind this will
+411    /// interact with the [`TransportConfig::max_idle_timeout`], if the last path is
+412    /// abandoned the entire connection will be closed.
+413    ///
+414    /// You can also change this using [`Connection::set_path_max_idle_timeout`] for
+415    /// existing paths.
+416    ///
+417    /// [`Connection::set_path_max_idle_timeout`]: crate::Connection::set_path_max_idle_timeout
+418    pub fn default_path_max_idle_timeout(&mut self, timeout: Option<Duration>) -> &mut Self {
+419        self.default_path_max_idle_timeout = timeout;
+420        self
+421    }
+422
+423    /// Sets a default per-path keep alive interval
+424    ///
+425    /// Note that this does not interact with the connection-wide
+426    /// [`TransportConfig::keep_alive_interval`].  This setting will keep this path active,
+427    /// [`TransportConfig::keep_alive_interval`] will keep the connection active, with no
+428    /// control over which path is used for this.
+429    ///
+430    /// You can also change this using [`Connection::set_path_keep_alive_interval`] for
+431    /// existing path.
+432    ///
+433    /// [`Connection::set_path_keep_alive_interval`]: crate::Connection::set_path_keep_alive_interval
+434    pub fn default_path_keep_alive_interval(&mut self, interval: Option<Duration>) -> &mut Self {
+435        self.default_path_keep_alive_interval = interval;
+436        self
+437    }
+438
+439    /// Get the initial max [`crate::PathId`] this endpoint allows.
+440    ///
+441    /// Returns `None` if multipath is disabled.
+442    pub(crate) fn get_initial_max_path_id(&self) -> Option<crate::PathId> {
+443        self.max_concurrent_multipath_paths
+444            // a max_concurrent_multipath_paths value of 1 only allows the first path, which
+445            // has id 0
+446            .map(|nonzero_concurrent| nonzero_concurrent.get() - 1)
+447            .map(Into::into)
+448    }
+449
+450    /// Sets the maximum number of nat traversal addresses this endpoint allows the remote to
+451    /// advertise
+452    ///
+453    /// Setting this to any nonzero value will enable Iroh's holepunching, loosely based in the Nat
+454    /// Traversal Extension for QUIC, see
+455    /// <https://www.ietf.org/archive/id/draft-seemann-quic-nat-traversal-02.html>
+456    ///
+457    /// This implementation expects the multipath extension to be enabled as well. if not yet
+458    /// enabled via [`Self::max_concurrent_multipath_paths`], a default value of
+459    /// [`DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED`] will be used.
+460    pub fn set_max_remote_nat_traversal_addresses(&mut self, max_addresses: u8) -> &mut Self {
+461        self.max_remote_nat_traversal_addresses = NonZeroU8::new(max_addresses);
+462        if max_addresses != 0 && self.max_concurrent_multipath_paths.is_none() {
+463            self.max_concurrent_multipath_paths(
+464                DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED_.get(),
+465            );
+466        }
+467        self
+468    }
+469
+470    /// Configures qlog capturing by setting a [`QlogFactory`].
+471    ///
+472    /// This assigns a [`QlogFactory`] that produces qlog capture configurations for
+473    /// individual connections.
+474    #[cfg(feature = "qlog")]
+475    pub fn qlog_factory(&mut self, factory: Arc<dyn QlogFactory>) -> &mut Self {
+476        self.qlog_factory = Some(factory);
+477        self
+478    }
+479
+480    /// Configures qlog capturing through the `QLOGDIR` environment variable.
+481    ///
+482    /// This uses [`QlogFileFactory::from_env`] to create a factory to write qlog traces
+483    /// into the directory set through the `QLOGDIR` environment variable.
+484    ///
+485    /// If `QLOGDIR` is not set, no traces will be written. If `QLOGDIR` is set to a path
+486    /// that does not exist, it will be created.
+487    ///
+488    /// The files will be prefixed with `prefix`.
+489    #[cfg(feature = "qlog")]
+490    pub fn qlog_from_env(&mut self, prefix: &str) -> &mut Self {
+491        self.qlog_factory(Arc::new(QlogFileFactory::from_env().with_prefix(prefix)))
+492    }
+493
+494    /// Configures qlog capturing into a directory.
+495    ///
+496    /// This uses [`QlogFileFactory`] to create a factory to write qlog traces into
+497    /// the specified directory.  The files will be prefixed with `prefix`.
+498    #[cfg(feature = "qlog")]
+499    pub fn qlog_from_path(&mut self, path: impl AsRef<Path>, prefix: &str) -> &mut Self {
+500        self.qlog_factory(Arc::new(
+501            QlogFileFactory::new(path.as_ref().to_owned()).with_prefix(prefix),
+502        ))
+503    }
+504
+505    pub(crate) fn create_qlog_sink(
+506        &self,
+507        side: Side,
+508        remote: SocketAddr,
+509        initial_dst_cid: ConnectionId,
+510        now: Instant,
+511    ) -> QlogSink {
+512        #[cfg(not(feature = "qlog"))]
+513        let sink = {
+514            let _ = (side, remote, initial_dst_cid, now);
+515            QlogSink::default()
+516        };
+517
+518        #[cfg(feature = "qlog")]
+519        let sink = {
+520            if let Some(config) = self
+521                .qlog_factory
+522                .as_ref()
+523                .and_then(|factory| factory.for_connection(side, remote, initial_dst_cid, now))
+524            {
+525                QlogSink::new(config, initial_dst_cid, side, now)
+526            } else {
+527                QlogSink::default()
+528            }
+529        };
+530
+531        sink
+532    }
+533}
+534
+535impl Default for TransportConfig {
+536    fn default() -> Self {
+537        const EXPECTED_RTT: u32 = 100; // ms
+538        const MAX_STREAM_BANDWIDTH: u32 = 12500 * 1000; // bytes/s
+539        // Window size needed to avoid pipeline
+540        // stalls
+541        const STREAM_RWND: u32 = MAX_STREAM_BANDWIDTH / 1000 * EXPECTED_RTT;
+542
+543        Self {
+544            max_concurrent_bidi_streams: 100u32.into(),
+545            max_concurrent_uni_streams: 100u32.into(),
+546            // 30 second default recommended by RFC 9308 § 3.2
+547            max_idle_timeout: Some(VarInt(30_000)),
+548            stream_receive_window: STREAM_RWND.into(),
+549            receive_window: VarInt::MAX,
+550            send_window: (8 * STREAM_RWND).into(),
+551            send_fairness: true,
+552
+553            packet_threshold: 3,
+554            time_threshold: 9.0 / 8.0,
+555            initial_rtt: Duration::from_millis(333), // per spec, intentionally distinct from EXPECTED_RTT
+556            initial_mtu: INITIAL_MTU,
+557            min_mtu: INITIAL_MTU,
+558            mtu_discovery_config: Some(MtuDiscoveryConfig::default()),
+559            pad_to_mtu: false,
+560            ack_frequency_config: None,
+561
+562            persistent_congestion_threshold: 3,
+563            keep_alive_interval: None,
+564            crypto_buffer_size: 16 * 1024,
+565            allow_spin: true,
+566            datagram_receive_buffer_size: Some(STREAM_RWND as usize),
+567            datagram_send_buffer_size: 1024 * 1024,
+568            #[cfg(test)]
+569            deterministic_packet_numbers: false,
+570
+571            congestion_controller_factory: Arc::new(congestion::CubicConfig::default()),
+572
+573            enable_segmentation_offload: true,
+574
+575            address_discovery_role: address_discovery::Role::default(),
+576
+577            // disabled multipath by default
+578            max_concurrent_multipath_paths: None,
+579            default_path_max_idle_timeout: None,
+580            default_path_keep_alive_interval: None,
+581
+582            // nat traversal disabled by default
+583            max_remote_nat_traversal_addresses: None,
+584
+585            #[cfg(feature = "qlog")]
+586            qlog_factory: None,
+587        }
+588    }
+589}
+590
+591impl fmt::Debug for TransportConfig {
+592    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+593        let Self {
+594            max_concurrent_bidi_streams,
+595            max_concurrent_uni_streams,
+596            max_idle_timeout,
+597            stream_receive_window,
+598            receive_window,
+599            send_window,
+600            send_fairness,
+601            packet_threshold,
+602            time_threshold,
+603            initial_rtt,
+604            initial_mtu,
+605            min_mtu,
+606            mtu_discovery_config,
+607            pad_to_mtu,
+608            ack_frequency_config,
+609            persistent_congestion_threshold,
+610            keep_alive_interval,
+611            crypto_buffer_size,
+612            allow_spin,
+613            datagram_receive_buffer_size,
+614            datagram_send_buffer_size,
+615            #[cfg(test)]
+616                deterministic_packet_numbers: _,
+617            congestion_controller_factory: _,
+618            enable_segmentation_offload,
+619            address_discovery_role,
+620            max_concurrent_multipath_paths,
+621            default_path_max_idle_timeout,
+622            default_path_keep_alive_interval,
+623            max_remote_nat_traversal_addresses,
+624            #[cfg(feature = "qlog")]
+625            qlog_factory,
+626        } = self;
+627        let mut s = fmt.debug_struct("TransportConfig");
+628
+629        s.field("max_concurrent_bidi_streams", max_concurrent_bidi_streams)
+630            .field("max_concurrent_uni_streams", max_concurrent_uni_streams)
+631            .field("max_idle_timeout", max_idle_timeout)
+632            .field("stream_receive_window", stream_receive_window)
+633            .field("receive_window", receive_window)
+634            .field("send_window", send_window)
+635            .field("send_fairness", send_fairness)
+636            .field("packet_threshold", packet_threshold)
+637            .field("time_threshold", time_threshold)
+638            .field("initial_rtt", initial_rtt)
+639            .field("initial_mtu", initial_mtu)
+640            .field("min_mtu", min_mtu)
+641            .field("mtu_discovery_config", mtu_discovery_config)
+642            .field("pad_to_mtu", pad_to_mtu)
+643            .field("ack_frequency_config", ack_frequency_config)
+644            .field(
+645                "persistent_congestion_threshold",
+646                persistent_congestion_threshold,
+647            )
+648            .field("keep_alive_interval", keep_alive_interval)
+649            .field("crypto_buffer_size", crypto_buffer_size)
+650            .field("allow_spin", allow_spin)
+651            .field("datagram_receive_buffer_size", datagram_receive_buffer_size)
+652            .field("datagram_send_buffer_size", datagram_send_buffer_size)
+653            // congestion_controller_factory not debug
+654            .field("enable_segmentation_offload", enable_segmentation_offload)
+655            .field("address_discovery_role", address_discovery_role)
+656            .field(
+657                "max_concurrent_multipath_paths",
+658                max_concurrent_multipath_paths,
+659            )
+660            .field(
+661                "default_path_max_idle_timeout",
+662                default_path_max_idle_timeout,
+663            )
+664            .field(
+665                "default_path_keep_alive_interval",
+666                default_path_keep_alive_interval,
+667            )
+668            .field(
+669                "max_remote_nat_traversal_addresses",
+670                max_remote_nat_traversal_addresses,
+671            );
+672        #[cfg(feature = "qlog")]
+673        s.field("qlog_factory", &qlog_factory.is_some());
+674
+675        s.finish_non_exhaustive()
+676    }
+677}
+678
+679/// Parameters for controlling the peer's acknowledgement frequency
+680///
+681/// The parameters provided in this config will be sent to the peer at the beginning of the
+682/// connection, so it can take them into account when sending acknowledgements (see each parameter's
+683/// description for details on how it influences acknowledgement frequency).
+684///
+685/// Quinn's implementation follows the fourth draft of the
+686/// [QUIC Acknowledgement Frequency extension](https://datatracker.ietf.org/doc/html/draft-ietf-quic-ack-frequency-04).
+687/// The defaults produce behavior slightly different than the behavior without this extension,
+688/// because they change the way reordered packets are handled (see
+689/// [`AckFrequencyConfig::reordering_threshold`] for details).
+690#[derive(Clone, Debug)]
+691pub struct AckFrequencyConfig {
+692    pub(crate) ack_eliciting_threshold: VarInt,
+693    pub(crate) max_ack_delay: Option<Duration>,
+694    pub(crate) reordering_threshold: VarInt,
+695}
+696
+697impl AckFrequencyConfig {
+698    /// The ack-eliciting threshold we will request the peer to use
+699    ///
+700    /// This threshold represents the number of ack-eliciting packets an endpoint may receive
+701    /// without immediately sending an ACK.
+702    ///
+703    /// The remote peer should send at least one ACK frame when more than this number of
+704    /// ack-eliciting packets have been received. A value of 0 results in a receiver immediately
+705    /// acknowledging every ack-eliciting packet.
+706    ///
+707    /// Defaults to 1, which sends ACK frames for every other ack-eliciting packet.
+708    pub fn ack_eliciting_threshold(&mut self, value: VarInt) -> &mut Self {
+709        self.ack_eliciting_threshold = value;
+710        self
+711    }
+712
+713    /// The `max_ack_delay` we will request the peer to use
+714    ///
+715    /// This parameter represents the maximum amount of time that an endpoint waits before sending
+716    /// an ACK when the ack-eliciting threshold hasn't been reached.
+717    ///
+718    /// The effective `max_ack_delay` will be clamped to be at least the peer's `min_ack_delay`
+719    /// transport parameter, and at most the greater of the current path RTT or 25ms.
+720    ///
+721    /// Defaults to `None`, in which case the peer's original `max_ack_delay` will be used, as
+722    /// obtained from its transport parameters.
+723    pub fn max_ack_delay(&mut self, value: Option<Duration>) -> &mut Self {
+724        self.max_ack_delay = value;
+725        self
+726    }
+727
+728    /// The reordering threshold we will request the peer to use
+729    ///
+730    /// This threshold represents the amount of out-of-order packets that will trigger an endpoint
+731    /// to send an ACK, without waiting for `ack_eliciting_threshold` to be exceeded or for
+732    /// `max_ack_delay` to be elapsed.
+733    ///
+734    /// A value of 0 indicates out-of-order packets do not elicit an immediate ACK. A value of 1
+735    /// immediately acknowledges any packets that are received out of order (this is also the
+736    /// behavior when the extension is disabled).
+737    ///
+738    /// It is recommended to set this value to [`TransportConfig::packet_threshold`] minus one.
+739    /// Since the default value for [`TransportConfig::packet_threshold`] is 3, this value defaults
+740    /// to 2.
+741    pub fn reordering_threshold(&mut self, value: VarInt) -> &mut Self {
+742        self.reordering_threshold = value;
+743        self
+744    }
+745}
+746
+747impl Default for AckFrequencyConfig {
+748    fn default() -> Self {
+749        Self {
+750            ack_eliciting_threshold: VarInt(1),
+751            max_ack_delay: None,
+752            reordering_threshold: VarInt(2),
+753        }
+754    }
+755}
+756
+757/// Parameters governing MTU discovery.
+758///
+759/// # The why of MTU discovery
+760///
+761/// By design, QUIC ensures during the handshake that the network path between the client and the
+762/// server is able to transmit unfragmented UDP packets with a body of 1200 bytes. In other words,
+763/// once the connection is established, we know that the network path's maximum transmission unit
+764/// (MTU) is of at least 1200 bytes (plus IP and UDP headers). Because of this, a QUIC endpoint can
+765/// split outgoing data in packets of 1200 bytes, with confidence that the network will be able to
+766/// deliver them (if the endpoint were to send bigger packets, they could prove too big and end up
+767/// being dropped).
+768///
+769/// There is, however, a significant overhead associated to sending a packet. If the same
+770/// information can be sent in fewer packets, that results in higher throughput. The amount of
+771/// packets that need to be sent is inversely proportional to the MTU: the higher the MTU, the
+772/// bigger the packets that can be sent, and the fewer packets that are needed to transmit a given
+773/// amount of bytes.
+774///
+775/// Most networks have an MTU higher than 1200. Through MTU discovery, endpoints can detect the
+776/// path's MTU and, if it turns out to be higher, start sending bigger packets.
+777///
+778/// # MTU discovery internals
+779///
+780/// Quinn implements MTU discovery through DPLPMTUD (Datagram Packetization Layer Path MTU
+781/// Discovery), described in [section 14.3 of RFC
+782/// 9000](https://www.rfc-editor.org/rfc/rfc9000.html#section-14.3). This method consists of sending
+783/// QUIC packets padded to a particular size (called PMTU probes), and waiting to see if the remote
+784/// peer responds with an ACK. If an ACK is received, that means the probe arrived at the remote
+785/// peer, which in turn means that the network path's MTU is of at least the packet's size. If the
+786/// probe is lost, it is sent another 2 times before concluding that the MTU is lower than the
+787/// packet's size.
+788///
+789/// MTU discovery runs on a schedule (e.g. every 600 seconds) specified through
+790/// [`MtuDiscoveryConfig::interval`]. The first run happens right after the handshake, and
+791/// subsequent discoveries are scheduled to run when the interval has elapsed, starting from the
+792/// last time when MTU discovery completed.
+793///
+794/// Since the search space for MTUs is quite big (the smallest possible MTU is 1200, and the highest
+795/// is 65527), Quinn performs a binary search to keep the number of probes as low as possible. The
+796/// lower bound of the search is equal to [`TransportConfig::initial_mtu`] in the
+797/// initial MTU discovery run, and is equal to the currently discovered MTU in subsequent runs. The
+798/// upper bound is determined by the minimum of [`MtuDiscoveryConfig::upper_bound`] and the
+799/// `max_udp_payload_size` transport parameter received from the peer during the handshake.
+800///
+801/// # Black hole detection
+802///
+803/// If, at some point, the network path no longer accepts packets of the detected size, packet loss
+804/// will eventually trigger black hole detection and reset the detected MTU to 1200. In that case,
+805/// MTU discovery will be triggered after [`MtuDiscoveryConfig::black_hole_cooldown`] (ignoring the
+806/// timer that was set based on [`MtuDiscoveryConfig::interval`]).
+807///
+808/// # Interaction between peers
+809///
+810/// There is no guarantee that the MTU on the path between A and B is the same as the MTU of the
+811/// path between B and A. Therefore, each peer in the connection needs to run MTU discovery
+812/// independently in order to discover the path's MTU.
+813#[derive(Clone, Debug)]
+814pub struct MtuDiscoveryConfig {
+815    pub(crate) interval: Duration,
+816    pub(crate) upper_bound: u16,
+817    pub(crate) minimum_change: u16,
+818    pub(crate) black_hole_cooldown: Duration,
+819}
+820
+821impl MtuDiscoveryConfig {
+822    /// Specifies the time to wait after completing MTU discovery before starting a new MTU
+823    /// discovery run.
+824    ///
+825    /// Defaults to 600 seconds, as recommended by [RFC
+826    /// 8899](https://www.rfc-editor.org/rfc/rfc8899).
+827    pub fn interval(&mut self, value: Duration) -> &mut Self {
+828        self.interval = value;
+829        self
+830    }
+831
+832    /// Specifies the upper bound to the max UDP payload size that MTU discovery will search for.
+833    ///
+834    /// Defaults to 1452, to stay within Ethernet's MTU when using IPv4 and IPv6. The highest
+835    /// allowed value is 65527, which corresponds to the maximum permitted UDP payload on IPv6.
+836    ///
+837    /// It is safe to use an arbitrarily high upper bound, regardless of the network path's MTU. The
+838    /// only drawback is that MTU discovery might take more time to finish.
+839    pub fn upper_bound(&mut self, value: u16) -> &mut Self {
+840        self.upper_bound = value.min(MAX_UDP_PAYLOAD);
+841        self
+842    }
+843
+844    /// Specifies the amount of time that MTU discovery should wait after a black hole was detected
+845    /// before running again. Defaults to one minute.
+846    ///
+847    /// Black hole detection can be spuriously triggered in case of congestion, so it makes sense to
+848    /// try MTU discovery again after a short period of time.
+849    pub fn black_hole_cooldown(&mut self, value: Duration) -> &mut Self {
+850        self.black_hole_cooldown = value;
+851        self
+852    }
+853
+854    /// Specifies the minimum MTU change to stop the MTU discovery phase.
+855    /// Defaults to 20.
+856    pub fn minimum_change(&mut self, value: u16) -> &mut Self {
+857        self.minimum_change = value;
+858        self
+859    }
+860}
+861
+862impl Default for MtuDiscoveryConfig {
+863    fn default() -> Self {
+864        Self {
+865            interval: Duration::from_secs(600),
+866            upper_bound: 1452,
+867            black_hole_cooldown: Duration::from_secs(60),
+868            minimum_change: 20,
+869        }
+870    }
+871}
+872
+873/// Maximum duration of inactivity to accept before timing out the connection
+874///
+875/// This wraps an underlying [`VarInt`], representing the duration in milliseconds. Values can be
+876/// constructed by converting directly from `VarInt`, or using `TryFrom<Duration>`.
+877///
+878/// ```
+879/// # use std::{convert::TryFrom, time::Duration};
+880/// # use iroh_quinn_proto::{IdleTimeout, VarIntBoundsExceeded, VarInt};
+881/// # fn main() -> Result<(), VarIntBoundsExceeded> {
+882/// // A `VarInt`-encoded value in milliseconds
+883/// let timeout = IdleTimeout::from(VarInt::from_u32(10_000));
+884///
+885/// // Try to convert a `Duration` into a `VarInt`-encoded timeout
+886/// let timeout = IdleTimeout::try_from(Duration::from_secs(10))?;
+887/// # Ok(())
+888/// # }
+889/// ```
+890#[derive(Default, Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
+891pub struct IdleTimeout(VarInt);
+892
+893impl From<VarInt> for IdleTimeout {
+894    fn from(inner: VarInt) -> Self {
+895        Self(inner)
+896    }
+897}
+898
+899impl std::convert::TryFrom<Duration> for IdleTimeout {
+900    type Error = VarIntBoundsExceeded;
+901
+902    fn try_from(timeout: Duration) -> Result<Self, Self::Error> {
+903        let inner = VarInt::try_from(timeout.as_millis())?;
+904        Ok(Self(inner))
+905    }
+906}
+907
+908impl fmt::Debug for IdleTimeout {
+909    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+910        self.0.fmt(f)
+911    }
+912}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/congestion.rs.html b/pr/395/docs/src/iroh_quinn_proto/congestion.rs.html new file mode 100644 index 000000000..4c4feb6bf --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/congestion.rs.html @@ -0,0 +1,112 @@ +congestion.rs - source

iroh_quinn_proto/
congestion.rs

1//! Logic for controlling the rate at which data is sent
+2
+3use crate::Instant;
+4use crate::connection::RttEstimator;
+5use std::any::Any;
+6use std::sync::Arc;
+7
+8mod bbr;
+9mod cubic;
+10mod new_reno;
+11
+12pub use bbr::{Bbr, BbrConfig};
+13pub use cubic::{Cubic, CubicConfig};
+14pub use new_reno::{NewReno, NewRenoConfig};
+15
+16/// Common interface for different congestion controllers
+17pub trait Controller: Send + Sync + std::fmt::Debug {
+18    /// One or more packets were just sent
+19    #[allow(unused_variables)]
+20    fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64) {}
+21
+22    /// Packet deliveries were confirmed
+23    ///
+24    /// `app_limited` indicates whether the connection was blocked on outgoing
+25    /// application data prior to receiving these acknowledgements.
+26    #[allow(unused_variables)]
+27    fn on_ack(
+28        &mut self,
+29        now: Instant,
+30        sent: Instant,
+31        bytes: u64,
+32        app_limited: bool,
+33        rtt: &RttEstimator,
+34    ) {
+35    }
+36
+37    /// Packets are acked in batches, all with the same `now` argument. This indicates one of those batches has completed.
+38    #[allow(unused_variables)]
+39    fn on_end_acks(
+40        &mut self,
+41        now: Instant,
+42        in_flight: u64,
+43        app_limited: bool,
+44        largest_packet_num_acked: Option<u64>,
+45    ) {
+46    }
+47
+48    /// Packets were deemed lost or marked congested
+49    ///
+50    /// `in_persistent_congestion` indicates whether all packets sent within the persistent
+51    /// congestion threshold period ending when the most recent packet in this batch was sent were
+52    /// lost.
+53    /// `lost_bytes` indicates how many bytes were lost. This value will be 0 for ECN triggers.
+54    fn on_congestion_event(
+55        &mut self,
+56        now: Instant,
+57        sent: Instant,
+58        is_persistent_congestion: bool,
+59        is_ecn: bool,
+60        lost_bytes: u64,
+61    );
+62
+63    /// Packets were incorrectly deemed lost
+64    ///
+65    /// This function is called when all packets that were deemed lost (for instance because
+66    /// of packet reordering) are acknowledged after the congestion event was raised.
+67    fn on_spurious_congestion_event(&mut self) {}
+68
+69    /// The known MTU for the current network path has been updated
+70    fn on_mtu_update(&mut self, new_mtu: u16);
+71
+72    /// Number of ack-eliciting bytes that may be in flight
+73    fn window(&self) -> u64;
+74
+75    /// Retrieve implementation-specific metrics used to populate `qlog` traces when they are enabled
+76    fn metrics(&self) -> ControllerMetrics {
+77        ControllerMetrics {
+78            congestion_window: self.window(),
+79            ssthresh: None,
+80            pacing_rate: None,
+81        }
+82    }
+83
+84    /// Duplicate the controller's state
+85    fn clone_box(&self) -> Box<dyn Controller>;
+86
+87    /// Initial congestion window
+88    fn initial_window(&self) -> u64;
+89
+90    /// Returns Self for use in down-casting to extract implementation details
+91    fn into_any(self: Box<Self>) -> Box<dyn Any>;
+92}
+93
+94/// Common congestion controller metrics
+95#[derive(Default, Debug)]
+96#[non_exhaustive]
+97pub struct ControllerMetrics {
+98    /// Congestion window (bytes)
+99    pub congestion_window: u64,
+100    /// Slow start threshold (bytes)
+101    pub ssthresh: Option<u64>,
+102    /// Pacing rate (bits/s)
+103    pub pacing_rate: Option<u64>,
+104}
+105
+106/// Constructs controllers on demand
+107pub trait ControllerFactory {
+108    /// Construct a fresh `Controller`
+109    fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller>;
+110}
+111
+112const BASE_DATAGRAM_SIZE: u64 = 1200;
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/congestion/bbr/bw_estimation.rs.html b/pr/395/docs/src/iroh_quinn_proto/congestion/bbr/bw_estimation.rs.html new file mode 100644 index 000000000..1c23f5cd5 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/congestion/bbr/bw_estimation.rs.html @@ -0,0 +1,101 @@ +bw_estimation.rs - source

iroh_quinn_proto/congestion/bbr/
bw_estimation.rs

1use std::fmt::{Debug, Display, Formatter};
+2
+3use super::min_max::MinMax;
+4use crate::{Duration, Instant};
+5
+6#[derive(Clone, Debug, Default)]
+7pub(crate) struct BandwidthEstimation {
+8    total_acked: u64,
+9    prev_total_acked: u64,
+10    acked_time: Option<Instant>,
+11    prev_acked_time: Option<Instant>,
+12    total_sent: u64,
+13    prev_total_sent: u64,
+14    sent_time: Option<Instant>,
+15    prev_sent_time: Option<Instant>,
+16    max_filter: MinMax,
+17    acked_at_last_window: u64,
+18}
+19
+20impl BandwidthEstimation {
+21    pub(crate) fn on_sent(&mut self, now: Instant, bytes: u64) {
+22        self.prev_total_sent = self.total_sent;
+23        self.total_sent += bytes;
+24        self.prev_sent_time = self.sent_time;
+25        self.sent_time = Some(now);
+26    }
+27
+28    pub(crate) fn on_ack(
+29        &mut self,
+30        now: Instant,
+31        _sent: Instant,
+32        bytes: u64,
+33        round: u64,
+34        app_limited: bool,
+35    ) {
+36        self.prev_total_acked = self.total_acked;
+37        self.total_acked += bytes;
+38        self.prev_acked_time = self.acked_time;
+39        self.acked_time = Some(now);
+40
+41        let prev_sent_time = match self.prev_sent_time {
+42            Some(prev_sent_time) => prev_sent_time,
+43            None => return,
+44        };
+45
+46        let send_rate = match self.sent_time {
+47            Some(sent_time) if sent_time > prev_sent_time => Self::bw_from_delta(
+48                self.total_sent - self.prev_total_sent,
+49                sent_time - prev_sent_time,
+50            )
+51            .unwrap_or(0),
+52            _ => u64::MAX, // will take the min of send and ack, so this is just a skip
+53        };
+54
+55        let ack_rate = match self.prev_acked_time {
+56            Some(prev_acked_time) => Self::bw_from_delta(
+57                self.total_acked - self.prev_total_acked,
+58                now - prev_acked_time,
+59            )
+60            .unwrap_or(0),
+61            None => 0,
+62        };
+63
+64        let bandwidth = send_rate.min(ack_rate);
+65        if !app_limited && self.max_filter.get() < bandwidth {
+66            self.max_filter.update_max(round, bandwidth);
+67        }
+68    }
+69
+70    pub(crate) fn bytes_acked_this_window(&self) -> u64 {
+71        self.total_acked - self.acked_at_last_window
+72    }
+73
+74    pub(crate) fn end_acks(&mut self, _current_round: u64, _app_limited: bool) {
+75        self.acked_at_last_window = self.total_acked;
+76    }
+77
+78    pub(crate) fn get_estimate(&self) -> u64 {
+79        self.max_filter.get()
+80    }
+81
+82    pub(crate) const fn bw_from_delta(bytes: u64, delta: Duration) -> Option<u64> {
+83        let window_duration_ns = delta.as_nanos();
+84        if window_duration_ns == 0 {
+85            return None;
+86        }
+87        let b_ns = bytes * 1_000_000_000;
+88        let bytes_per_second = b_ns / (window_duration_ns as u64);
+89        Some(bytes_per_second)
+90    }
+91}
+92
+93impl Display for BandwidthEstimation {
+94    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+95        write!(
+96            f,
+97            "{:.3} MB/s",
+98            self.get_estimate() as f32 / (1024 * 1024) as f32
+99        )
+100    }
+101}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/congestion/bbr/min_max.rs.html b/pr/395/docs/src/iroh_quinn_proto/congestion/bbr/min_max.rs.html new file mode 100644 index 000000000..a64b972e4 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/congestion/bbr/min_max.rs.html @@ -0,0 +1,152 @@ +min_max.rs - source

iroh_quinn_proto/congestion/bbr/
min_max.rs

1/*
+2 * Based on Google code released under BSD license here:
+3 * https://groups.google.com/forum/#!topic/bbr-dev/3RTgkzi5ZD8
+4 */
+5
+6/*
+7 * Kathleen Nichols' algorithm for tracking the minimum (or maximum)
+8 * value of a data stream over some fixed time interval.  (E.g.,
+9 * the minimum RTT over the past five minutes.) It uses constant
+10 * space and constant time per update yet almost always delivers
+11 * the same minimum as an implementation that has to keep all the
+12 * data in the window.
+13 *
+14 * The algorithm keeps track of the best, 2nd best & 3rd best min
+15 * values, maintaining an invariant that the measurement time of
+16 * the n'th best >= n-1'th best. It also makes sure that the three
+17 * values are widely separated in the time window since that bounds
+18 * the worse case error when that data is monotonically increasing
+19 * over the window.
+20 *
+21 * Upon getting a new min, we can forget everything earlier because
+22 * it has no value - the new min is <= everything else in the window
+23 * by definition and it samples the most recent. So we restart fresh on
+24 * every new min and overwrites 2nd & 3rd choices. The same property
+25 * holds for 2nd & 3rd best.
+26 */
+27
+28use std::fmt::Debug;
+29
+30#[derive(Copy, Clone, Debug)]
+31pub(super) struct MinMax {
+32    /// round count, not a timestamp
+33    window: u64,
+34    samples: [MinMaxSample; 3],
+35}
+36
+37impl MinMax {
+38    pub(super) fn get(&self) -> u64 {
+39        self.samples[0].value
+40    }
+41
+42    fn fill(&mut self, sample: MinMaxSample) {
+43        self.samples.fill(sample);
+44    }
+45
+46    pub(super) fn reset(&mut self) {
+47        self.fill(Default::default())
+48    }
+49
+50    /// update_min is also defined in the original source, but removed here since it is not used.
+51    pub(super) fn update_max(&mut self, current_round: u64, measurement: u64) {
+52        let sample = MinMaxSample {
+53            time: current_round,
+54            value: measurement,
+55        };
+56
+57        if self.samples[0].value == 0  /* uninitialised */
+58            || /* found new max? */ sample.value >= self.samples[0].value
+59            || /* nothing left in window? */ sample.time - self.samples[2].time > self.window
+60        {
+61            self.fill(sample); /* forget earlier samples */
+62            return;
+63        }
+64
+65        if sample.value >= self.samples[1].value {
+66            self.samples[2] = sample;
+67            self.samples[1] = sample;
+68        } else if sample.value >= self.samples[2].value {
+69            self.samples[2] = sample;
+70        }
+71
+72        self.subwin_update(sample);
+73    }
+74
+75    /* As time advances, update the 1st, 2nd, and 3rd choices. */
+76    fn subwin_update(&mut self, sample: MinMaxSample) {
+77        let dt = sample.time - self.samples[0].time;
+78        if dt > self.window {
+79            /*
+80             * Passed entire window without a new sample so make 2nd
+81             * choice the new sample & 3rd choice the new 2nd choice.
+82             * we may have to iterate this since our 2nd choice
+83             * may also be outside the window (we checked on entry
+84             * that the third choice was in the window).
+85             */
+86            self.samples[0] = self.samples[1];
+87            self.samples[1] = self.samples[2];
+88            self.samples[2] = sample;
+89            if sample.time - self.samples[0].time > self.window {
+90                self.samples[0] = self.samples[1];
+91                self.samples[1] = self.samples[2];
+92                self.samples[2] = sample;
+93            }
+94        } else if self.samples[1].time == self.samples[0].time && dt > self.window / 4 {
+95            /*
+96             * We've passed a quarter of the window without a new sample
+97             * so take a 2nd choice from the 2nd quarter of the window.
+98             */
+99            self.samples[2] = sample;
+100            self.samples[1] = sample;
+101        } else if self.samples[2].time == self.samples[1].time && dt > self.window / 2 {
+102            /*
+103             * We've passed half the window without finding a new sample
+104             * so take a 3rd choice from the last half of the window
+105             */
+106            self.samples[2] = sample;
+107        }
+108    }
+109}
+110
+111impl Default for MinMax {
+112    fn default() -> Self {
+113        Self {
+114            window: 10,
+115            samples: [Default::default(); 3],
+116        }
+117    }
+118}
+119
+120#[derive(Debug, Copy, Clone, Default)]
+121struct MinMaxSample {
+122    /// round number, not a timestamp
+123    time: u64,
+124    value: u64,
+125}
+126
+127#[cfg(test)]
+128mod test {
+129    use super::*;
+130
+131    #[test]
+132    fn test() {
+133        let round = 25;
+134        let mut min_max = MinMax::default();
+135        min_max.update_max(round + 1, 100);
+136        assert_eq!(100, min_max.get());
+137        min_max.update_max(round + 3, 120);
+138        assert_eq!(120, min_max.get());
+139        min_max.update_max(round + 5, 160);
+140        assert_eq!(160, min_max.get());
+141        min_max.update_max(round + 7, 100);
+142        assert_eq!(160, min_max.get());
+143        min_max.update_max(round + 10, 100);
+144        assert_eq!(160, min_max.get());
+145        min_max.update_max(round + 14, 100);
+146        assert_eq!(160, min_max.get());
+147        min_max.update_max(round + 16, 100);
+148        assert_eq!(100, min_max.get());
+149        min_max.update_max(round + 18, 130);
+150        assert_eq!(130, min_max.get());
+151    }
+152}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/congestion/bbr/mod.rs.html b/pr/395/docs/src/iroh_quinn_proto/congestion/bbr/mod.rs.html new file mode 100644 index 000000000..fb51be2b0 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/congestion/bbr/mod.rs.html @@ -0,0 +1,651 @@ +mod.rs - source

iroh_quinn_proto/congestion/bbr/
mod.rs

1use std::any::Any;
+2use std::fmt::Debug;
+3use std::sync::Arc;
+4
+5use rand::{Rng, SeedableRng};
+6
+7use crate::congestion::ControllerMetrics;
+8use crate::congestion::bbr::bw_estimation::BandwidthEstimation;
+9use crate::congestion::bbr::min_max::MinMax;
+10use crate::connection::RttEstimator;
+11use crate::{Duration, Instant};
+12
+13use super::{BASE_DATAGRAM_SIZE, Controller, ControllerFactory};
+14
+15mod bw_estimation;
+16mod min_max;
+17
+18/// Experimental! Use at your own risk.
+19///
+20/// Aims for reduced buffer bloat and improved performance over high bandwidth-delay product networks.
+21/// Based on google's quiche implementation <https://source.chromium.org/chromium/chromium/src/+/master:net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc>
+22/// of BBR <https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control>.
+23/// More discussion and links at <https://groups.google.com/g/bbr-dev>.
+24#[derive(Debug, Clone)]
+25pub struct Bbr {
+26    config: Arc<BbrConfig>,
+27    current_mtu: u64,
+28    max_bandwidth: BandwidthEstimation,
+29    acked_bytes: u64,
+30    mode: Mode,
+31    loss_state: LossState,
+32    recovery_state: RecoveryState,
+33    recovery_window: u64,
+34    is_at_full_bandwidth: bool,
+35    pacing_gain: f32,
+36    high_gain: f32,
+37    drain_gain: f32,
+38    cwnd_gain: f32,
+39    high_cwnd_gain: f32,
+40    last_cycle_start: Option<Instant>,
+41    current_cycle_offset: u8,
+42    init_cwnd: u64,
+43    min_cwnd: u64,
+44    prev_in_flight_count: u64,
+45    exit_probe_rtt_at: Option<Instant>,
+46    probe_rtt_last_started_at: Option<Instant>,
+47    min_rtt: Duration,
+48    exiting_quiescence: bool,
+49    pacing_rate: u64,
+50    max_acked_packet_number: u64,
+51    max_sent_packet_number: u64,
+52    end_recovery_at_packet_number: u64,
+53    cwnd: u64,
+54    current_round_trip_end_packet_number: u64,
+55    round_count: u64,
+56    bw_at_last_round: u64,
+57    round_wo_bw_gain: u64,
+58    ack_aggregation: AckAggregationState,
+59    random_number_generator: rand::rngs::StdRng,
+60}
+61
+62impl Bbr {
+63    /// Construct a state using the given `config` and current time `now`
+64    pub fn new(config: Arc<BbrConfig>, current_mtu: u16) -> Self {
+65        let initial_window = config.initial_window;
+66        Self {
+67            config,
+68            current_mtu: current_mtu as u64,
+69            max_bandwidth: BandwidthEstimation::default(),
+70            acked_bytes: 0,
+71            mode: Mode::Startup,
+72            loss_state: Default::default(),
+73            recovery_state: RecoveryState::NotInRecovery,
+74            recovery_window: 0,
+75            is_at_full_bandwidth: false,
+76            pacing_gain: K_DEFAULT_HIGH_GAIN,
+77            high_gain: K_DEFAULT_HIGH_GAIN,
+78            drain_gain: 1.0 / K_DEFAULT_HIGH_GAIN,
+79            cwnd_gain: K_DEFAULT_HIGH_GAIN,
+80            high_cwnd_gain: K_DEFAULT_HIGH_GAIN,
+81            last_cycle_start: None,
+82            current_cycle_offset: 0,
+83            init_cwnd: initial_window,
+84            min_cwnd: calculate_min_window(current_mtu as u64),
+85            prev_in_flight_count: 0,
+86            exit_probe_rtt_at: None,
+87            probe_rtt_last_started_at: None,
+88            min_rtt: Default::default(),
+89            exiting_quiescence: false,
+90            pacing_rate: 0,
+91            max_acked_packet_number: 0,
+92            max_sent_packet_number: 0,
+93            end_recovery_at_packet_number: 0,
+94            cwnd: initial_window,
+95            current_round_trip_end_packet_number: 0,
+96            round_count: 0,
+97            bw_at_last_round: 0,
+98            round_wo_bw_gain: 0,
+99            ack_aggregation: AckAggregationState::default(),
+100            random_number_generator: rand::rngs::StdRng::from_os_rng(),
+101        }
+102    }
+103
+104    fn enter_startup_mode(&mut self) {
+105        self.mode = Mode::Startup;
+106        self.pacing_gain = self.high_gain;
+107        self.cwnd_gain = self.high_cwnd_gain;
+108    }
+109
+110    fn enter_probe_bandwidth_mode(&mut self, now: Instant) {
+111        self.mode = Mode::ProbeBw;
+112        self.cwnd_gain = K_DERIVED_HIGH_CWNDGAIN;
+113        self.last_cycle_start = Some(now);
+114        // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is
+115        // excluded because in that case increased gain and decreased gain would not
+116        // follow each other.
+117        let mut rand_index = self
+118            .random_number_generator
+119            .random_range(0..K_PACING_GAIN.len() as u8 - 1);
+120        if rand_index >= 1 {
+121            rand_index += 1;
+122        }
+123        self.current_cycle_offset = rand_index;
+124        self.pacing_gain = K_PACING_GAIN[rand_index as usize];
+125    }
+126
+127    fn update_recovery_state(&mut self, is_round_start: bool) {
+128        // Exit recovery when there are no losses for a round.
+129        if self.loss_state.has_losses() {
+130            self.end_recovery_at_packet_number = self.max_sent_packet_number;
+131        }
+132        match self.recovery_state {
+133            // Enter conservation on the first loss.
+134            RecoveryState::NotInRecovery if self.loss_state.has_losses() => {
+135                self.recovery_state = RecoveryState::Conservation;
+136                // This will cause the |recovery_window| to be set to the
+137                // correct value in CalculateRecoveryWindow().
+138                self.recovery_window = 0;
+139                // Since the conservation phase is meant to be lasting for a whole
+140                // round, extend the current round as if it were started right now.
+141                self.current_round_trip_end_packet_number = self.max_sent_packet_number;
+142            }
+143            RecoveryState::Growth | RecoveryState::Conservation => {
+144                if self.recovery_state == RecoveryState::Conservation && is_round_start {
+145                    self.recovery_state = RecoveryState::Growth;
+146                }
+147                // Exit recovery if appropriate.
+148                if !self.loss_state.has_losses()
+149                    && self.max_acked_packet_number > self.end_recovery_at_packet_number
+150                {
+151                    self.recovery_state = RecoveryState::NotInRecovery;
+152                }
+153            }
+154            _ => {}
+155        }
+156    }
+157
+158    fn update_gain_cycle_phase(&mut self, now: Instant, in_flight: u64) {
+159        // In most cases, the cycle is advanced after an RTT passes.
+160        let mut should_advance_gain_cycling = self
+161            .last_cycle_start
+162            .map(|last_cycle_start| now.duration_since(last_cycle_start) > self.min_rtt)
+163            .unwrap_or(false);
+164        // If the pacing gain is above 1.0, the connection is trying to probe the
+165        // bandwidth by increasing the number of bytes in flight to at least
+166        // pacing_gain * BDP.  Make sure that it actually reaches the target, as
+167        // long as there are no losses suggesting that the buffers are not able to
+168        // hold that much.
+169        if self.pacing_gain > 1.0
+170            && !self.loss_state.has_losses()
+171            && self.prev_in_flight_count < self.get_target_cwnd(self.pacing_gain)
+172        {
+173            should_advance_gain_cycling = false;
+174        }
+175
+176        // If pacing gain is below 1.0, the connection is trying to drain the extra
+177        // queue which could have been incurred by probing prior to it.  If the
+178        // number of bytes in flight falls down to the estimated BDP value earlier,
+179        // conclude that the queue has been successfully drained and exit this cycle
+180        // early.
+181        if self.pacing_gain < 1.0 && in_flight <= self.get_target_cwnd(1.0) {
+182            should_advance_gain_cycling = true;
+183        }
+184
+185        if should_advance_gain_cycling {
+186            self.current_cycle_offset = (self.current_cycle_offset + 1) % K_PACING_GAIN.len() as u8;
+187            self.last_cycle_start = Some(now);
+188            // Stay in low gain mode until the target BDP is hit.  Low gain mode
+189            // will be exited immediately when the target BDP is achieved.
+190            if DRAIN_TO_TARGET
+191                && self.pacing_gain < 1.0
+192                && (K_PACING_GAIN[self.current_cycle_offset as usize] - 1.0).abs() < f32::EPSILON
+193                && in_flight > self.get_target_cwnd(1.0)
+194            {
+195                return;
+196            }
+197            self.pacing_gain = K_PACING_GAIN[self.current_cycle_offset as usize];
+198        }
+199    }
+200
+201    fn maybe_exit_startup_or_drain(&mut self, now: Instant, in_flight: u64) {
+202        if self.mode == Mode::Startup && self.is_at_full_bandwidth {
+203            self.mode = Mode::Drain;
+204            self.pacing_gain = self.drain_gain;
+205            self.cwnd_gain = self.high_cwnd_gain;
+206        }
+207        if self.mode == Mode::Drain && in_flight <= self.get_target_cwnd(1.0) {
+208            self.enter_probe_bandwidth_mode(now);
+209        }
+210    }
+211
+212    fn is_min_rtt_expired(&self, now: Instant, app_limited: bool) -> bool {
+213        !app_limited
+214            && self
+215                .probe_rtt_last_started_at
+216                .map(|last| now.saturating_duration_since(last) > Duration::from_secs(10))
+217                .unwrap_or(true)
+218    }
+219
+220    fn maybe_enter_or_exit_probe_rtt(
+221        &mut self,
+222        now: Instant,
+223        is_round_start: bool,
+224        bytes_in_flight: u64,
+225        app_limited: bool,
+226    ) {
+227        let min_rtt_expired = self.is_min_rtt_expired(now, app_limited);
+228        if min_rtt_expired && !self.exiting_quiescence && self.mode != Mode::ProbeRtt {
+229            self.mode = Mode::ProbeRtt;
+230            self.pacing_gain = 1.0;
+231            // Do not decide on the time to exit ProbeRtt until the
+232            // |bytes_in_flight| is at the target small value.
+233            self.exit_probe_rtt_at = None;
+234            self.probe_rtt_last_started_at = Some(now);
+235        }
+236
+237        if self.mode == Mode::ProbeRtt {
+238            match self.exit_probe_rtt_at {
+239                None => {
+240                    // If the window has reached the appropriate size, schedule exiting
+241                    // ProbeRtt.  The CWND during ProbeRtt is
+242                    // kMinimumCongestionWindow, but we allow an extra packet since QUIC
+243                    // checks CWND before sending a packet.
+244                    if bytes_in_flight < self.get_probe_rtt_cwnd() + self.current_mtu {
+245                        const K_PROBE_RTT_TIME: Duration = Duration::from_millis(200);
+246                        self.exit_probe_rtt_at = Some(now + K_PROBE_RTT_TIME);
+247                    }
+248                }
+249                Some(exit_time) if is_round_start && now >= exit_time => {
+250                    if !self.is_at_full_bandwidth {
+251                        self.enter_startup_mode();
+252                    } else {
+253                        self.enter_probe_bandwidth_mode(now);
+254                    }
+255                }
+256                Some(_) => {}
+257            }
+258        }
+259
+260        self.exiting_quiescence = false;
+261    }
+262
+263    fn get_target_cwnd(&self, gain: f32) -> u64 {
+264        let bw = self.max_bandwidth.get_estimate();
+265        let bdp = self.min_rtt.as_micros() as u64 * bw;
+266        let bdpf = bdp as f64;
+267        let cwnd = ((gain as f64 * bdpf) / 1_000_000f64) as u64;
+268        // BDP estimate will be zero if no bandwidth samples are available yet.
+269        if cwnd == 0 {
+270            return self.init_cwnd;
+271        }
+272        cwnd.max(self.min_cwnd)
+273    }
+274
+275    fn get_probe_rtt_cwnd(&self) -> u64 {
+276        const K_MODERATE_PROBE_RTT_MULTIPLIER: f32 = 0.75;
+277        if PROBE_RTT_BASED_ON_BDP {
+278            return self.get_target_cwnd(K_MODERATE_PROBE_RTT_MULTIPLIER);
+279        }
+280        self.min_cwnd
+281    }
+282
+283    fn calculate_pacing_rate(&mut self) {
+284        let bw = self.max_bandwidth.get_estimate();
+285        if bw == 0 {
+286            return;
+287        }
+288        let target_rate = (bw as f64 * self.pacing_gain as f64) as u64;
+289        if self.is_at_full_bandwidth {
+290            self.pacing_rate = target_rate;
+291            return;
+292        }
+293
+294        // Pace at the rate of initial_window / RTT as soon as RTT measurements are
+295        // available.
+296        if self.pacing_rate == 0 && self.min_rtt.as_nanos() != 0 {
+297            self.pacing_rate =
+298                BandwidthEstimation::bw_from_delta(self.init_cwnd, self.min_rtt).unwrap();
+299            return;
+300        }
+301
+302        // Do not decrease the pacing rate during startup.
+303        if self.pacing_rate < target_rate {
+304            self.pacing_rate = target_rate;
+305        }
+306    }
+307
+308    fn calculate_cwnd(&mut self, bytes_acked: u64, excess_acked: u64) {
+309        if self.mode == Mode::ProbeRtt {
+310            return;
+311        }
+312        let mut target_window = self.get_target_cwnd(self.cwnd_gain);
+313        if self.is_at_full_bandwidth {
+314            // Add the max recently measured ack aggregation to CWND.
+315            target_window += self.ack_aggregation.max_ack_height.get();
+316        } else {
+317            // Add the most recent excess acked.  Because CWND never decreases in
+318            // STARTUP, this will automatically create a very localized max filter.
+319            target_window += excess_acked;
+320        }
+321        // Instead of immediately setting the target CWND as the new one, BBR grows
+322        // the CWND towards |target_window| by only increasing it |bytes_acked| at a
+323        // time.
+324        if self.is_at_full_bandwidth {
+325            self.cwnd = target_window.min(self.cwnd + bytes_acked);
+326        } else if (self.cwnd_gain < target_window as f32) || (self.acked_bytes < self.init_cwnd) {
+327            // If the connection is not yet out of startup phase, do not decrease
+328            // the window.
+329            self.cwnd += bytes_acked;
+330        }
+331
+332        // Enforce the limits on the congestion window.
+333        if self.cwnd < self.min_cwnd {
+334            self.cwnd = self.min_cwnd;
+335        }
+336    }
+337
+338    fn calculate_recovery_window(&mut self, bytes_acked: u64, bytes_lost: u64, in_flight: u64) {
+339        if !self.recovery_state.in_recovery() {
+340            return;
+341        }
+342        // Set up the initial recovery window.
+343        if self.recovery_window == 0 {
+344            self.recovery_window = self.min_cwnd.max(in_flight + bytes_acked);
+345            return;
+346        }
+347
+348        // Remove losses from the recovery window, while accounting for a potential
+349        // integer underflow.
+350        if self.recovery_window >= bytes_lost {
+351            self.recovery_window -= bytes_lost;
+352        } else {
+353            // k_max_segment_size = current_mtu
+354            self.recovery_window = self.current_mtu;
+355        }
+356        // In CONSERVATION mode, just subtracting losses is sufficient.  In GROWTH,
+357        // release additional |bytes_acked| to achieve a slow-start-like behavior.
+358        if self.recovery_state == RecoveryState::Growth {
+359            self.recovery_window += bytes_acked;
+360        }
+361
+362        // Sanity checks.  Ensure that we always allow to send at least an MSS or
+363        // |bytes_acked| in response, whichever is larger.
+364        self.recovery_window = self
+365            .recovery_window
+366            .max(in_flight + bytes_acked)
+367            .max(self.min_cwnd);
+368    }
+369
+370    /// <https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control#section-4.3.2.2>
+371    fn check_if_full_bw_reached(&mut self, app_limited: bool) {
+372        if app_limited {
+373            return;
+374        }
+375        let target = (self.bw_at_last_round as f64 * K_STARTUP_GROWTH_TARGET as f64) as u64;
+376        let bw = self.max_bandwidth.get_estimate();
+377        if bw >= target {
+378            self.bw_at_last_round = bw;
+379            self.round_wo_bw_gain = 0;
+380            self.ack_aggregation.max_ack_height.reset();
+381            return;
+382        }
+383
+384        self.round_wo_bw_gain += 1;
+385        if self.round_wo_bw_gain >= K_ROUND_TRIPS_WITHOUT_GROWTH_BEFORE_EXITING_STARTUP as u64
+386            || (self.recovery_state.in_recovery())
+387        {
+388            self.is_at_full_bandwidth = true;
+389        }
+390    }
+391}
+392
+393impl Controller for Bbr {
+394    fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64) {
+395        self.max_sent_packet_number = last_packet_number;
+396        self.max_bandwidth.on_sent(now, bytes);
+397    }
+398
+399    fn on_ack(
+400        &mut self,
+401        now: Instant,
+402        sent: Instant,
+403        bytes: u64,
+404        app_limited: bool,
+405        rtt: &RttEstimator,
+406    ) {
+407        self.max_bandwidth
+408            .on_ack(now, sent, bytes, self.round_count, app_limited);
+409        self.acked_bytes += bytes;
+410        if self.is_min_rtt_expired(now, app_limited) || self.min_rtt > rtt.min() {
+411            self.min_rtt = rtt.min();
+412        }
+413    }
+414
+415    fn on_end_acks(
+416        &mut self,
+417        now: Instant,
+418        in_flight: u64,
+419        app_limited: bool,
+420        largest_packet_num_acked: Option<u64>,
+421    ) {
+422        let bytes_acked = self.max_bandwidth.bytes_acked_this_window();
+423        let excess_acked = self.ack_aggregation.update_ack_aggregation_bytes(
+424            bytes_acked,
+425            now,
+426            self.round_count,
+427            self.max_bandwidth.get_estimate(),
+428        );
+429        self.max_bandwidth.end_acks(self.round_count, app_limited);
+430        if let Some(largest_acked_packet) = largest_packet_num_acked {
+431            self.max_acked_packet_number = largest_acked_packet;
+432        }
+433
+434        let mut is_round_start = false;
+435        if bytes_acked > 0 {
+436            is_round_start =
+437                self.max_acked_packet_number > self.current_round_trip_end_packet_number;
+438            if is_round_start {
+439                self.current_round_trip_end_packet_number = self.max_sent_packet_number;
+440                self.round_count += 1;
+441            }
+442        }
+443
+444        self.update_recovery_state(is_round_start);
+445
+446        if self.mode == Mode::ProbeBw {
+447            self.update_gain_cycle_phase(now, in_flight);
+448        }
+449
+450        if is_round_start && !self.is_at_full_bandwidth {
+451            self.check_if_full_bw_reached(app_limited);
+452        }
+453
+454        self.maybe_exit_startup_or_drain(now, in_flight);
+455
+456        self.maybe_enter_or_exit_probe_rtt(now, is_round_start, in_flight, app_limited);
+457
+458        // After the model is updated, recalculate the pacing rate and congestion window.
+459        self.calculate_pacing_rate();
+460        self.calculate_cwnd(bytes_acked, excess_acked);
+461        self.calculate_recovery_window(bytes_acked, self.loss_state.lost_bytes, in_flight);
+462
+463        self.prev_in_flight_count = in_flight;
+464        self.loss_state.reset();
+465    }
+466
+467    fn on_congestion_event(
+468        &mut self,
+469        _now: Instant,
+470        _sent: Instant,
+471        _is_persistent_congestion: bool,
+472        _is_ecn: bool,
+473        lost_bytes: u64,
+474    ) {
+475        self.loss_state.lost_bytes += lost_bytes;
+476    }
+477
+478    fn on_mtu_update(&mut self, new_mtu: u16) {
+479        self.current_mtu = new_mtu as u64;
+480        self.min_cwnd = calculate_min_window(self.current_mtu);
+481        self.init_cwnd = self.config.initial_window.max(self.min_cwnd);
+482        self.cwnd = self.cwnd.max(self.min_cwnd);
+483    }
+484
+485    fn window(&self) -> u64 {
+486        if self.mode == Mode::ProbeRtt {
+487            return self.get_probe_rtt_cwnd();
+488        } else if self.recovery_state.in_recovery() && self.mode != Mode::Startup {
+489            return self.cwnd.min(self.recovery_window);
+490        }
+491        self.cwnd
+492    }
+493
+494    fn metrics(&self) -> ControllerMetrics {
+495        ControllerMetrics {
+496            congestion_window: self.window(),
+497            ssthresh: None,
+498            pacing_rate: Some(self.pacing_rate * 8),
+499        }
+500    }
+501
+502    fn clone_box(&self) -> Box<dyn Controller> {
+503        Box::new(self.clone())
+504    }
+505
+506    fn initial_window(&self) -> u64 {
+507        self.config.initial_window
+508    }
+509
+510    fn into_any(self: Box<Self>) -> Box<dyn Any> {
+511        self
+512    }
+513}
+514
+515/// Configuration for the [`Bbr`] congestion controller
+516#[derive(Debug, Clone)]
+517pub struct BbrConfig {
+518    initial_window: u64,
+519}
+520
+521impl BbrConfig {
+522    /// Default limit on the amount of outstanding data in bytes.
+523    ///
+524    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
+525    pub fn initial_window(&mut self, value: u64) -> &mut Self {
+526        self.initial_window = value;
+527        self
+528    }
+529}
+530
+531impl Default for BbrConfig {
+532    fn default() -> Self {
+533        Self {
+534            initial_window: K_MAX_INITIAL_CONGESTION_WINDOW * BASE_DATAGRAM_SIZE,
+535        }
+536    }
+537}
+538
+539impl ControllerFactory for BbrConfig {
+540    fn build(self: Arc<Self>, _now: Instant, current_mtu: u16) -> Box<dyn Controller> {
+541        Box::new(Bbr::new(self, current_mtu))
+542    }
+543}
+544
+545#[derive(Debug, Default, Copy, Clone)]
+546struct AckAggregationState {
+547    max_ack_height: MinMax,
+548    aggregation_epoch_start_time: Option<Instant>,
+549    aggregation_epoch_bytes: u64,
+550}
+551
+552impl AckAggregationState {
+553    fn update_ack_aggregation_bytes(
+554        &mut self,
+555        newly_acked_bytes: u64,
+556        now: Instant,
+557        round: u64,
+558        max_bandwidth: u64,
+559    ) -> u64 {
+560        // Compute how many bytes are expected to be delivered, assuming max
+561        // bandwidth is correct.
+562        let expected_bytes_acked = max_bandwidth
+563            * now
+564                .saturating_duration_since(self.aggregation_epoch_start_time.unwrap_or(now))
+565                .as_micros() as u64
+566            / 1_000_000;
+567
+568        // Reset the current aggregation epoch as soon as the ack arrival rate is
+569        // less than or equal to the max bandwidth.
+570        if self.aggregation_epoch_bytes <= expected_bytes_acked {
+571            // Reset to start measuring a new aggregation epoch.
+572            self.aggregation_epoch_bytes = newly_acked_bytes;
+573            self.aggregation_epoch_start_time = Some(now);
+574            return 0;
+575        }
+576
+577        // Compute how many extra bytes were delivered vs max bandwidth.
+578        // Include the bytes most recently acknowledged to account for stretch acks.
+579        self.aggregation_epoch_bytes += newly_acked_bytes;
+580        let diff = self.aggregation_epoch_bytes - expected_bytes_acked;
+581        self.max_ack_height.update_max(round, diff);
+582        diff
+583    }
+584}
+585
+586#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+587enum Mode {
+588    // Startup phase of the connection.
+589    Startup,
+590    // After achieving the highest possible bandwidth during the startup, lower
+591    // the pacing rate in order to drain the queue.
+592    Drain,
+593    // Cruising mode.
+594    ProbeBw,
+595    // Temporarily slow down sending in order to empty the buffer and measure
+596    // the real minimum RTT.
+597    ProbeRtt,
+598}
+599
+600// Indicates how the congestion control limits the amount of bytes in flight.
+601#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+602enum RecoveryState {
+603    // Do not limit.
+604    NotInRecovery,
+605    // Allow an extra outstanding byte for each byte acknowledged.
+606    Conservation,
+607    // Allow two extra outstanding bytes for each byte acknowledged (slow
+608    // start).
+609    Growth,
+610}
+611
+612impl RecoveryState {
+613    pub(super) fn in_recovery(&self) -> bool {
+614        !matches!(self, Self::NotInRecovery)
+615    }
+616}
+617
+618#[derive(Debug, Clone, Default)]
+619struct LossState {
+620    lost_bytes: u64,
+621}
+622
+623impl LossState {
+624    pub(super) fn reset(&mut self) {
+625        self.lost_bytes = 0;
+626    }
+627
+628    pub(super) fn has_losses(&self) -> bool {
+629        self.lost_bytes != 0
+630    }
+631}
+632
+633fn calculate_min_window(current_mtu: u64) -> u64 {
+634    4 * current_mtu
+635}
+636
+637// The gain used for the STARTUP, equal to 2/ln(2).
+638const K_DEFAULT_HIGH_GAIN: f32 = 2.885;
+639// The newly derived CWND gain for STARTUP, 2.
+640const K_DERIVED_HIGH_CWNDGAIN: f32 = 2.0;
+641// The cycle of gains used during the ProbeBw stage.
+642const K_PACING_GAIN: [f32; 8] = [1.25, 0.75, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
+643
+644const K_STARTUP_GROWTH_TARGET: f32 = 1.25;
+645const K_ROUND_TRIPS_WITHOUT_GROWTH_BEFORE_EXITING_STARTUP: u8 = 3;
+646
+647// Do not allow initial congestion window to be greater than 200 packets.
+648const K_MAX_INITIAL_CONGESTION_WINDOW: u64 = 200;
+649
+650const PROBE_RTT_BASED_ON_BDP: bool = true;
+651const DRAIN_TO_TARGET: bool = true;
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/congestion/cubic.rs.html b/pr/395/docs/src/iroh_quinn_proto/congestion/cubic.rs.html new file mode 100644 index 000000000..c337a95cb --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/congestion/cubic.rs.html @@ -0,0 +1,299 @@ +cubic.rs - source

iroh_quinn_proto/congestion/
cubic.rs

1use std::any::Any;
+2use std::cmp;
+3use std::sync::Arc;
+4
+5use super::{BASE_DATAGRAM_SIZE, Controller, ControllerFactory};
+6use crate::connection::RttEstimator;
+7use crate::{Duration, Instant};
+8
+9/// CUBIC Constants.
+10///
+11/// These are recommended value in RFC8312.
+12const BETA_CUBIC: f64 = 0.7;
+13
+14const C: f64 = 0.4;
+15
+16/// CUBIC State Variables.
+17///
+18/// We need to keep those variables across the connection.
+19/// k, w_max are described in the RFC.
+20#[derive(Debug, Default, Clone)]
+21pub(super) struct State {
+22    /// Time period that the cubic function takes to increase the window size to W_max.
+23    k: f64,
+24
+25    /// Congestion window size when the last congestion event occurred.
+26    w_max: f64,
+27
+28    /// Congestion window increment stored during congestion avoidance.
+29    cwnd_inc: u64,
+30
+31    /// Maximum number of bytes in flight that may be sent.
+32    window: u64,
+33
+34    /// Slow start threshold in bytes.
+35    ///
+36    /// When the congestion window is below ssthresh, the mode is slow start
+37    /// and the window grows by the number of bytes acknowledged.
+38    ssthresh: u64,
+39
+40    /// The time when QUIC first detects a loss, causing it to enter recovery. When a packet sent
+41    /// after this time is acknowledged, QUIC exits recovery.
+42    recovery_start_time: Option<Instant>,
+43}
+44
+45/// CUBIC Functions.
+46///
+47/// Note that these calculations are based on a count of cwnd as bytes,
+48/// not packets.
+49/// Unit of t (duration) and RTT are based on seconds (f64).
+50impl State {
+51    // K = cbrt(w_max * (1 - beta_cubic) / C) (Eq. 2)
+52    fn cubic_k(&self, max_datagram_size: u64) -> f64 {
+53        let w_max = self.w_max / max_datagram_size as f64;
+54        (w_max * (1.0 - BETA_CUBIC) / C).cbrt()
+55    }
+56
+57    // W_cubic(t) = C * (t - K)^3 + w_max (Eq. 1)
+58    fn w_cubic(&self, t: Duration, max_datagram_size: u64) -> f64 {
+59        let w_max = self.w_max / max_datagram_size as f64;
+60
+61        (C * (t.as_secs_f64() - self.k).powi(3) + w_max) * max_datagram_size as f64
+62    }
+63
+64    // W_est(t) = w_max * beta_cubic + 3 * (1 - beta_cubic) / (1 + beta_cubic) *
+65    // (t / RTT) (Eq. 4)
+66    fn w_est(&self, t: Duration, rtt: Duration, max_datagram_size: u64) -> f64 {
+67        let w_max = self.w_max / max_datagram_size as f64;
+68        (w_max * BETA_CUBIC
+69            + 3.0 * (1.0 - BETA_CUBIC) / (1.0 + BETA_CUBIC) * t.as_secs_f64() / rtt.as_secs_f64())
+70            * max_datagram_size as f64
+71    }
+72}
+73
+74/// The RFC8312 congestion controller, as widely used for TCP
+75#[derive(Debug, Clone)]
+76pub struct Cubic {
+77    config: Arc<CubicConfig>,
+78    current_mtu: u64,
+79    state: State,
+80    /// Copy of the controller state to restore when a spurious congestion event is detected.
+81    pre_congestion_state: Option<State>,
+82}
+83
+84impl Cubic {
+85    /// Construct a state using the given `config` and current time `now`
+86    pub fn new(config: Arc<CubicConfig>, _now: Instant, current_mtu: u16) -> Self {
+87        Self {
+88            state: State {
+89                window: config.initial_window,
+90                ssthresh: u64::MAX,
+91                ..Default::default()
+92            },
+93            current_mtu: current_mtu as u64,
+94            pre_congestion_state: None,
+95            config,
+96        }
+97    }
+98
+99    fn minimum_window(&self) -> u64 {
+100        2 * self.current_mtu
+101    }
+102}
+103
+104impl Controller for Cubic {
+105    fn on_ack(
+106        &mut self,
+107        now: Instant,
+108        sent: Instant,
+109        bytes: u64,
+110        app_limited: bool,
+111        rtt: &RttEstimator,
+112    ) {
+113        if app_limited
+114            || self
+115                .state
+116                .recovery_start_time
+117                .map(|recovery_start_time| sent <= recovery_start_time)
+118                .unwrap_or(false)
+119        {
+120            return;
+121        }
+122
+123        if self.state.window < self.state.ssthresh {
+124            // Slow start
+125            self.state.window += bytes;
+126        } else {
+127            // Congestion avoidance.
+128            let ca_start_time;
+129
+130            match self.state.recovery_start_time {
+131                Some(t) => ca_start_time = t,
+132                None => {
+133                    // When we come here without congestion_event() triggered,
+134                    // initialize congestion_recovery_start_time, w_max and k.
+135                    ca_start_time = now;
+136                    self.state.recovery_start_time = Some(now);
+137
+138                    self.state.w_max = self.state.window as f64;
+139                    self.state.k = 0.0;
+140                }
+141            }
+142
+143            let t = now - ca_start_time;
+144
+145            // w_cubic(t + rtt)
+146            let w_cubic = self.state.w_cubic(t + rtt.get(), self.current_mtu);
+147
+148            // w_est(t)
+149            let w_est = self.state.w_est(t, rtt.get(), self.current_mtu);
+150
+151            let mut cubic_cwnd = self.state.window;
+152
+153            if w_cubic < w_est {
+154                // TCP friendly region.
+155                cubic_cwnd = cmp::max(cubic_cwnd, w_est as u64);
+156            } else if cubic_cwnd < w_cubic as u64 {
+157                // Concave region or convex region use same increment.
+158                let cubic_inc =
+159                    (w_cubic - cubic_cwnd as f64) / cubic_cwnd as f64 * self.current_mtu as f64;
+160
+161                cubic_cwnd += cubic_inc as u64;
+162            }
+163
+164            // Update the increment and increase cwnd by MSS.
+165            self.state.cwnd_inc += cubic_cwnd - self.state.window;
+166
+167            // cwnd_inc can be more than 1 MSS in the late stage of max probing.
+168            // however RFC9002 §7.3.3 (Congestion Avoidance) limits
+169            // the increase of cwnd to 1 max_datagram_size per cwnd acknowledged.
+170            if self.state.cwnd_inc >= self.current_mtu {
+171                self.state.window += self.current_mtu;
+172                self.state.cwnd_inc = 0;
+173            }
+174        }
+175    }
+176
+177    fn on_congestion_event(
+178        &mut self,
+179        now: Instant,
+180        sent: Instant,
+181        is_persistent_congestion: bool,
+182        is_ecn: bool,
+183        _lost_bytes: u64,
+184    ) {
+185        if self
+186            .state
+187            .recovery_start_time
+188            .map(|recovery_start_time| sent <= recovery_start_time)
+189            .unwrap_or(false)
+190        {
+191            return;
+192        }
+193
+194        // Save state in case this event ends up being spurious
+195        if !is_ecn {
+196            self.pre_congestion_state = Some(self.state.clone());
+197        }
+198
+199        self.state.recovery_start_time = Some(now);
+200
+201        // Fast convergence
+202        if (self.state.window as f64) < self.state.w_max {
+203            self.state.w_max = self.state.window as f64 * (1.0 + BETA_CUBIC) / 2.0;
+204        } else {
+205            self.state.w_max = self.state.window as f64;
+206        }
+207
+208        self.state.ssthresh = cmp::max(
+209            (self.state.w_max * BETA_CUBIC) as u64,
+210            self.minimum_window(),
+211        );
+212        self.state.window = self.state.ssthresh;
+213        self.state.k = self.state.cubic_k(self.current_mtu);
+214
+215        self.state.cwnd_inc = (self.state.cwnd_inc as f64 * BETA_CUBIC) as u64;
+216
+217        if is_persistent_congestion {
+218            self.state.recovery_start_time = None;
+219            self.state.w_max = self.state.window as f64;
+220
+221            // 4.7 Timeout - reduce ssthresh based on BETA_CUBIC
+222            self.state.ssthresh = cmp::max(
+223                (self.state.window as f64 * BETA_CUBIC) as u64,
+224                self.minimum_window(),
+225            );
+226
+227            self.state.cwnd_inc = 0;
+228
+229            self.state.window = self.minimum_window();
+230        }
+231    }
+232
+233    fn on_spurious_congestion_event(&mut self) {
+234        if let Some(prior_state) = self.pre_congestion_state.take()
+235            && self.state.window < prior_state.window
+236        {
+237            self.state = prior_state;
+238        }
+239    }
+240
+241    fn on_mtu_update(&mut self, new_mtu: u16) {
+242        self.current_mtu = new_mtu as u64;
+243        self.state.window = self.state.window.max(self.minimum_window());
+244    }
+245
+246    fn window(&self) -> u64 {
+247        self.state.window
+248    }
+249
+250    fn metrics(&self) -> super::ControllerMetrics {
+251        super::ControllerMetrics {
+252            congestion_window: self.window(),
+253            ssthresh: Some(self.state.ssthresh),
+254            pacing_rate: None,
+255        }
+256    }
+257
+258    fn clone_box(&self) -> Box<dyn Controller> {
+259        Box::new(self.clone())
+260    }
+261
+262    fn initial_window(&self) -> u64 {
+263        self.config.initial_window
+264    }
+265
+266    fn into_any(self: Box<Self>) -> Box<dyn Any> {
+267        self
+268    }
+269}
+270
+271/// Configuration for the `Cubic` congestion controller
+272#[derive(Debug, Clone)]
+273pub struct CubicConfig {
+274    initial_window: u64,
+275}
+276
+277impl CubicConfig {
+278    /// Default limit on the amount of outstanding data in bytes.
+279    ///
+280    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
+281    pub fn initial_window(&mut self, value: u64) -> &mut Self {
+282        self.initial_window = value;
+283        self
+284    }
+285}
+286
+287impl Default for CubicConfig {
+288    fn default() -> Self {
+289        Self {
+290            initial_window: 14720.clamp(2 * BASE_DATAGRAM_SIZE, 10 * BASE_DATAGRAM_SIZE),
+291        }
+292    }
+293}
+294
+295impl ControllerFactory for CubicConfig {
+296    fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller> {
+297        Box::new(Cubic::new(self, now, current_mtu))
+298    }
+299}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/congestion/new_reno.rs.html b/pr/395/docs/src/iroh_quinn_proto/congestion/new_reno.rs.html new file mode 100644 index 000000000..c6b1f772b --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/congestion/new_reno.rs.html @@ -0,0 +1,173 @@ +new_reno.rs - source

iroh_quinn_proto/congestion/
new_reno.rs

1use std::any::Any;
+2use std::sync::Arc;
+3
+4use super::{BASE_DATAGRAM_SIZE, Controller, ControllerFactory};
+5use crate::Instant;
+6use crate::connection::RttEstimator;
+7
+8/// A simple, standard congestion controller
+9#[derive(Debug, Clone)]
+10pub struct NewReno {
+11    config: Arc<NewRenoConfig>,
+12    current_mtu: u64,
+13    /// Maximum number of bytes in flight that may be sent.
+14    window: u64,
+15    /// Slow start threshold in bytes. When the congestion window is below ssthresh, the mode is
+16    /// slow start and the window grows by the number of bytes acknowledged.
+17    ssthresh: u64,
+18    /// The time when QUIC first detects a loss, causing it to enter recovery. When a packet sent
+19    /// after this time is acknowledged, QUIC exits recovery.
+20    recovery_start_time: Instant,
+21    /// Bytes which had been acked by the peer since leaving slow start
+22    bytes_acked: u64,
+23}
+24
+25impl NewReno {
+26    /// Construct a state using the given `config` and current time `now`
+27    pub fn new(config: Arc<NewRenoConfig>, now: Instant, current_mtu: u16) -> Self {
+28        Self {
+29            window: config.initial_window,
+30            ssthresh: u64::MAX,
+31            recovery_start_time: now,
+32            current_mtu: current_mtu as u64,
+33            config,
+34            bytes_acked: 0,
+35        }
+36    }
+37
+38    fn minimum_window(&self) -> u64 {
+39        2 * self.current_mtu
+40    }
+41}
+42
+43impl Controller for NewReno {
+44    fn on_ack(
+45        &mut self,
+46        _now: Instant,
+47        sent: Instant,
+48        bytes: u64,
+49        app_limited: bool,
+50        _rtt: &RttEstimator,
+51    ) {
+52        if app_limited || sent <= self.recovery_start_time {
+53            return;
+54        }
+55
+56        if self.window < self.ssthresh {
+57            // Slow start
+58            self.window += bytes;
+59
+60            if self.window >= self.ssthresh {
+61                // Exiting slow start
+62                // Initialize `bytes_acked` for congestion avoidance. The idea
+63                // here is that any bytes over `sshthresh` will already be counted
+64                // towards the congestion avoidance phase - independent of when
+65                // how close to `sshthresh` the `window` was when switching states,
+66                // and independent of datagram sizes.
+67                self.bytes_acked = self.window - self.ssthresh;
+68            }
+69        } else {
+70            // Congestion avoidance
+71            // This implementation uses the method which does not require
+72            // floating point math, which also increases the window by 1 datagram
+73            // for every round trip.
+74            // This mechanism is called Appropriate Byte Counting in
+75            // https://tools.ietf.org/html/rfc3465
+76            self.bytes_acked += bytes;
+77
+78            if self.bytes_acked >= self.window {
+79                self.bytes_acked -= self.window;
+80                self.window += self.current_mtu;
+81            }
+82        }
+83    }
+84
+85    fn on_congestion_event(
+86        &mut self,
+87        now: Instant,
+88        sent: Instant,
+89        is_persistent_congestion: bool,
+90        _is_ecn: bool,
+91        _lost_bytes: u64,
+92    ) {
+93        if sent <= self.recovery_start_time {
+94            return;
+95        }
+96
+97        self.recovery_start_time = now;
+98        self.window = (self.window as f32 * self.config.loss_reduction_factor) as u64;
+99        self.window = self.window.max(self.minimum_window());
+100        self.ssthresh = self.window;
+101
+102        if is_persistent_congestion {
+103            self.window = self.minimum_window();
+104        }
+105    }
+106
+107    fn on_mtu_update(&mut self, new_mtu: u16) {
+108        self.current_mtu = new_mtu as u64;
+109        self.window = self.window.max(self.minimum_window());
+110    }
+111
+112    fn window(&self) -> u64 {
+113        self.window
+114    }
+115
+116    fn metrics(&self) -> super::ControllerMetrics {
+117        super::ControllerMetrics {
+118            congestion_window: self.window(),
+119            ssthresh: Some(self.ssthresh),
+120            pacing_rate: None,
+121        }
+122    }
+123
+124    fn clone_box(&self) -> Box<dyn Controller> {
+125        Box::new(self.clone())
+126    }
+127
+128    fn initial_window(&self) -> u64 {
+129        self.config.initial_window
+130    }
+131
+132    fn into_any(self: Box<Self>) -> Box<dyn Any> {
+133        self
+134    }
+135}
+136
+137/// Configuration for the `NewReno` congestion controller
+138#[derive(Debug, Clone)]
+139pub struct NewRenoConfig {
+140    initial_window: u64,
+141    loss_reduction_factor: f32,
+142}
+143
+144impl NewRenoConfig {
+145    /// Default limit on the amount of outstanding data in bytes.
+146    ///
+147    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
+148    pub fn initial_window(&mut self, value: u64) -> &mut Self {
+149        self.initial_window = value;
+150        self
+151    }
+152
+153    /// Reduction in congestion window when a new loss event is detected.
+154    pub fn loss_reduction_factor(&mut self, value: f32) -> &mut Self {
+155        self.loss_reduction_factor = value;
+156        self
+157    }
+158}
+159
+160impl Default for NewRenoConfig {
+161    fn default() -> Self {
+162        Self {
+163            initial_window: 14720.clamp(2 * BASE_DATAGRAM_SIZE, 10 * BASE_DATAGRAM_SIZE),
+164            loss_reduction_factor: 0.5,
+165        }
+166    }
+167}
+168
+169impl ControllerFactory for NewRenoConfig {
+170    fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller> {
+171        Box::new(NewReno::new(self, now, current_mtu))
+172    }
+173}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/ack_frequency.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/ack_frequency.rs.html new file mode 100644 index 000000000..f6c1cd72b --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/ack_frequency.rs.html @@ -0,0 +1,159 @@ +ack_frequency.rs - source

iroh_quinn_proto/connection/
ack_frequency.rs

1use crate::Duration;
+2use crate::frame::AckFrequency;
+3use crate::transport_parameters::TransportParameters;
+4use crate::{AckFrequencyConfig, TIMER_GRANULARITY, TransportError, VarInt};
+5
+6use super::PathId;
+7
+8/// State associated to ACK frequency
+9pub(super) struct AckFrequencyState {
+10    //
+11    // Sending ACK_FREQUENCY frames
+12    //
+13    /// The path ID, packet number and value of the in-flight ACK_FREQUENCY frame
+14    in_flight_ack_frequency_frame: Option<(PathId, u64, Duration)>,
+15    next_outgoing_sequence_number: VarInt,
+16    pub(super) peer_max_ack_delay: Duration,
+17
+18    //
+19    // Receiving ACK_FREQUENCY frames
+20    //
+21    /// The sequence number of the most recently received ACK_FREQUENCY frame
+22    last_ack_frequency_frame: Option<u64>,
+23    pub(super) max_ack_delay: Duration,
+24}
+25
+26impl AckFrequencyState {
+27    pub(super) fn new(default_max_ack_delay: Duration) -> Self {
+28        Self {
+29            in_flight_ack_frequency_frame: None,
+30            next_outgoing_sequence_number: VarInt(0),
+31            peer_max_ack_delay: default_max_ack_delay,
+32
+33            last_ack_frequency_frame: None,
+34            max_ack_delay: default_max_ack_delay,
+35        }
+36    }
+37
+38    /// Returns the `max_ack_delay` that should be requested of the peer when sending an
+39    /// ACK_FREQUENCY frame
+40    pub(super) fn candidate_max_ack_delay(
+41        &self,
+42        rtt: Duration,
+43        config: &AckFrequencyConfig,
+44        peer_params: &TransportParameters,
+45    ) -> Duration {
+46        // Use the peer's max_ack_delay if no custom max_ack_delay was provided in the config
+47        let min_ack_delay =
+48            Duration::from_micros(peer_params.min_ack_delay.map_or(0, |x| x.into()));
+49        config
+50            .max_ack_delay
+51            .unwrap_or(self.peer_max_ack_delay)
+52            .clamp(min_ack_delay, rtt.max(MIN_AUTOMATIC_ACK_DELAY))
+53    }
+54
+55    /// Returns the `max_ack_delay` for the purposes of calculating the PTO
+56    ///
+57    /// This `max_ack_delay` is defined as the maximum of the peer's current `max_ack_delay` and all
+58    /// in-flight `max_ack_delay`s (i.e. proposed values that haven't been acknowledged yet, but
+59    /// might be already in use by the peer).
+60    pub(super) fn max_ack_delay_for_pto(&self) -> Duration {
+61        // Note: we have at most one in-flight ACK_FREQUENCY frame
+62        if let Some((_, _, max_ack_delay)) = self.in_flight_ack_frequency_frame {
+63            self.peer_max_ack_delay.max(max_ack_delay)
+64        } else {
+65            self.peer_max_ack_delay
+66        }
+67    }
+68
+69    /// Returns the next sequence number for an ACK_FREQUENCY frame
+70    pub(super) fn next_sequence_number(&mut self) -> VarInt {
+71        assert!(self.next_outgoing_sequence_number <= VarInt::MAX);
+72
+73        let seq = self.next_outgoing_sequence_number;
+74        self.next_outgoing_sequence_number.0 += 1;
+75        seq
+76    }
+77
+78    /// Returns true if we should send an ACK_FREQUENCY frame
+79    pub(super) fn should_send_ack_frequency(
+80        &self,
+81        rtt: Duration,
+82        config: &AckFrequencyConfig,
+83        peer_params: &TransportParameters,
+84    ) -> bool {
+85        if self.next_outgoing_sequence_number.0 == 0 {
+86            // Always send at startup
+87            return true;
+88        }
+89        let current = self
+90            .in_flight_ack_frequency_frame
+91            .map_or(self.peer_max_ack_delay, |(_, _, pending)| pending);
+92        let desired = self.candidate_max_ack_delay(rtt, config, peer_params);
+93        let error = (desired.as_secs_f32() / current.as_secs_f32()) - 1.0;
+94        error.abs() > MAX_RTT_ERROR
+95    }
+96
+97    /// Notifies the [`AckFrequencyState`] that a packet containing an ACK_FREQUENCY frame was sent
+98    pub(super) fn ack_frequency_sent(
+99        &mut self,
+100        path_id: PathId,
+101        pn: u64,
+102        requested_max_ack_delay: Duration,
+103    ) {
+104        self.in_flight_ack_frequency_frame = Some((path_id, pn, requested_max_ack_delay));
+105    }
+106
+107    /// Notifies the [`AckFrequencyState`] that a packet has been ACKed
+108    pub(super) fn on_acked(&mut self, path_id: PathId, pn: u64) {
+109        match self.in_flight_ack_frequency_frame {
+110            Some((path, number, requested_max_ack_delay)) if path == path_id && number == pn => {
+111                self.in_flight_ack_frequency_frame = None;
+112                self.peer_max_ack_delay = requested_max_ack_delay;
+113            }
+114            _ => {}
+115        }
+116    }
+117
+118    /// Notifies the [`AckFrequencyState`] that an ACK_FREQUENCY frame was received
+119    ///
+120    /// Updates the endpoint's params according to the payload of the ACK_FREQUENCY frame, or
+121    /// returns an error in case the requested `max_ack_delay` is invalid.
+122    ///
+123    /// Returns `true` if the frame was processed and `false` if it was ignored because of being
+124    /// stale.
+125    pub(super) fn ack_frequency_received(
+126        &mut self,
+127        frame: &AckFrequency,
+128    ) -> Result<bool, TransportError> {
+129        if self
+130            .last_ack_frequency_frame
+131            .is_some_and(|highest_sequence_nr| frame.sequence.into_inner() <= highest_sequence_nr)
+132        {
+133            return Ok(false);
+134        }
+135
+136        self.last_ack_frequency_frame = Some(frame.sequence.into_inner());
+137
+138        // Update max_ack_delay
+139        let max_ack_delay = Duration::from_micros(frame.request_max_ack_delay.into_inner());
+140        if max_ack_delay < TIMER_GRANULARITY {
+141            return Err(TransportError::PROTOCOL_VIOLATION(
+142                "Requested Max Ack Delay in ACK_FREQUENCY frame is less than min_ack_delay",
+143            ));
+144        }
+145        self.max_ack_delay = max_ack_delay;
+146
+147        Ok(true)
+148    }
+149}
+150
+151/// Maximum proportion difference between the most recently requested max ACK delay and the
+152/// currently desired one before a new request is sent, when the peer supports the ACK frequency
+153/// extension and an explicit max ACK delay is not configured.
+154const MAX_RTT_ERROR: f32 = 0.2;
+155
+156/// Minimum value to request the peer set max ACK delay to when the peer supports the ACK frequency
+157/// extension and an explicit max ACK delay is not configured.
+158// Keep in sync with `AckFrequencyConfig::max_ack_delay` documentation
+159const MIN_AUTOMATIC_ACK_DELAY: Duration = Duration::from_millis(25);
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/assembler.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/assembler.rs.html new file mode 100644 index 000000000..fd61c9cb1 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/assembler.rs.html @@ -0,0 +1,845 @@ +assembler.rs - source

iroh_quinn_proto/connection/
assembler.rs

1use std::{
+2    cmp::Ordering,
+3    collections::{BinaryHeap, binary_heap::PeekMut},
+4    mem,
+5};
+6
+7use bytes::{Buf, Bytes, BytesMut};
+8
+9use crate::range_set::ArrayRangeSet;
+10
+11/// Helper to assemble unordered stream frames into an ordered stream
+12#[derive(Debug, Default)]
+13pub(super) struct Assembler {
+14    state: State,
+15    data: BinaryHeap<Buffer>,
+16    /// Total number of buffered bytes, including duplicates in ordered mode.
+17    buffered: usize,
+18    /// Estimated number of allocated bytes, will never be less than `buffered`.
+19    allocated: usize,
+20    /// Number of bytes read by the application. When only ordered reads have been used, this is the
+21    /// length of the contiguous prefix of the stream which has been consumed by the application,
+22    /// aka the stream offset.
+23    bytes_read: u64,
+24    end: u64,
+25}
+26
+27impl Assembler {
+28    pub(super) fn new() -> Self {
+29        Self::default()
+30    }
+31
+32    /// Reset to the initial state
+33    pub(super) fn reinit(&mut self) {
+34        let old_data = mem::take(&mut self.data);
+35        *self = Self::default();
+36        self.data = old_data;
+37        self.data.clear();
+38    }
+39
+40    pub(super) fn ensure_ordering(&mut self, ordered: bool) -> Result<(), IllegalOrderedRead> {
+41        if ordered && !self.state.is_ordered() {
+42            return Err(IllegalOrderedRead);
+43        } else if !ordered && self.state.is_ordered() {
+44            // Enter unordered mode
+45            if !self.data.is_empty() {
+46                // Get rid of possible duplicates
+47                self.defragment();
+48            }
+49            let mut recvd = ArrayRangeSet::new();
+50            recvd.insert(0..self.bytes_read);
+51            for chunk in &self.data {
+52                recvd.insert(chunk.offset..chunk.offset + chunk.bytes.len() as u64);
+53            }
+54            self.state = State::Unordered { recvd };
+55        }
+56        Ok(())
+57    }
+58
+59    /// Get the the next chunk
+60    pub(super) fn read(&mut self, max_length: usize, ordered: bool) -> Option<Chunk> {
+61        loop {
+62            let mut chunk = self.data.peek_mut()?;
+63
+64            if ordered {
+65                if chunk.offset > self.bytes_read {
+66                    // Next chunk is after current read index
+67                    return None;
+68                } else if (chunk.offset + chunk.bytes.len() as u64) <= self.bytes_read {
+69                    // Next chunk is useless as the read index is beyond its end
+70                    self.buffered -= chunk.bytes.len();
+71                    self.allocated -= chunk.allocation_size;
+72                    PeekMut::pop(chunk);
+73                    continue;
+74                }
+75
+76                // Determine `start` and `len` of the slice of useful data in chunk
+77                let start = (self.bytes_read - chunk.offset) as usize;
+78                if start > 0 {
+79                    chunk.bytes.advance(start);
+80                    chunk.offset += start as u64;
+81                    self.buffered -= start;
+82                }
+83            }
+84
+85            return Some(if max_length < chunk.bytes.len() {
+86                self.bytes_read += max_length as u64;
+87                let offset = chunk.offset;
+88                chunk.offset += max_length as u64;
+89                self.buffered -= max_length;
+90                Chunk::new(offset, chunk.bytes.split_to(max_length))
+91            } else {
+92                self.bytes_read += chunk.bytes.len() as u64;
+93                self.buffered -= chunk.bytes.len();
+94                self.allocated -= chunk.allocation_size;
+95                let chunk = PeekMut::pop(chunk);
+96                Chunk::new(chunk.offset, chunk.bytes)
+97            });
+98        }
+99    }
+100
+101    /// Copy fragmented chunk data to new chunks backed by a single buffer
+102    ///
+103    /// This makes sure we're not unnecessarily holding on to many larger allocations.
+104    /// We merge contiguous chunks in the process of doing so.
+105    fn defragment(&mut self) {
+106        let new = BinaryHeap::with_capacity(self.data.len());
+107        let old = mem::replace(&mut self.data, new);
+108        let mut buffers = old.into_sorted_vec();
+109        self.buffered = 0;
+110        let mut fragmented_buffered = 0;
+111        let mut offset = self.bytes_read;
+112        for chunk in buffers.iter_mut().rev() {
+113            chunk.try_mark_defragment(offset);
+114            let size = chunk.bytes.len();
+115            offset = chunk.offset + size as u64;
+116            self.buffered += size;
+117            if !chunk.defragmented {
+118                fragmented_buffered += size;
+119            }
+120        }
+121        self.allocated = self.buffered;
+122        let mut buffer = BytesMut::with_capacity(fragmented_buffered);
+123        let mut offset = self.bytes_read;
+124        for chunk in buffers.into_iter().rev() {
+125            if chunk.defragmented {
+126                // bytes might be empty after try_mark_defragment
+127                if !chunk.bytes.is_empty() {
+128                    self.data.push(chunk);
+129                }
+130                continue;
+131            }
+132            // Overlap is resolved by try_mark_defragment
+133            if chunk.offset != offset + (buffer.len() as u64) {
+134                if !buffer.is_empty() {
+135                    self.data
+136                        .push(Buffer::new_defragmented(offset, buffer.split().freeze()));
+137                }
+138                offset = chunk.offset;
+139            }
+140            buffer.extend_from_slice(&chunk.bytes);
+141        }
+142        if !buffer.is_empty() {
+143            self.data
+144                .push(Buffer::new_defragmented(offset, buffer.split().freeze()));
+145        }
+146    }
+147
+148    // Note: If a packet contains many frames from the same stream, the estimated over-allocation
+149    // will be much higher because we are counting the same allocation multiple times.
+150    pub(super) fn insert(&mut self, mut offset: u64, mut bytes: Bytes, allocation_size: usize) {
+151        debug_assert!(
+152            bytes.len() <= allocation_size,
+153            "allocation_size less than bytes.len(): {:?} < {:?}",
+154            allocation_size,
+155            bytes.len()
+156        );
+157        self.end = self.end.max(offset + bytes.len() as u64);
+158        if let State::Unordered { ref mut recvd } = self.state {
+159            // Discard duplicate data
+160            let range = offset..offset + bytes.len() as u64;
+161            for duplicate in recvd.iter_range(range.clone()) {
+162                if duplicate.start > offset {
+163                    let buffer = Buffer::new(
+164                        offset,
+165                        bytes.split_to((duplicate.start - offset) as usize),
+166                        allocation_size,
+167                    );
+168                    self.buffered += buffer.bytes.len();
+169                    self.allocated += buffer.allocation_size;
+170                    self.data.push(buffer);
+171                    offset = duplicate.start;
+172                }
+173                bytes.advance((duplicate.end - offset) as usize);
+174                offset = duplicate.end;
+175            }
+176            recvd.insert(range);
+177        } else if offset < self.bytes_read {
+178            if (offset + bytes.len() as u64) <= self.bytes_read {
+179                return;
+180            } else {
+181                let diff = self.bytes_read - offset;
+182                offset += diff;
+183                bytes.advance(diff as usize);
+184            }
+185        }
+186
+187        if bytes.is_empty() {
+188            return;
+189        }
+190        let buffer = Buffer::new(offset, bytes, allocation_size);
+191        self.buffered += buffer.bytes.len();
+192        self.allocated += buffer.allocation_size;
+193        self.data.push(buffer);
+194        // `self.buffered` also counts duplicate bytes, therefore we use
+195        // `self.end - self.bytes_read` as an upper bound of buffered unique
+196        // bytes. This will cause a defragmentation if the amount of duplicate
+197        // bytes exceedes a proportion of the receive window size.
+198        let buffered = self.buffered.min((self.end - self.bytes_read) as usize);
+199        let over_allocation = self.allocated - buffered;
+200        // Rationale: on the one hand, we want to defragment rarely, ideally never
+201        // in non-pathological scenarios. However, a pathological or malicious
+202        // peer could send us one-byte frames, and since we use reference-counted
+203        // buffers in order to prevent copying, this could result in keeping a lot
+204        // of memory allocated. This limits over-allocation in proportion to the
+205        // buffered data. The constants are chosen somewhat arbitrarily and try to
+206        // balance between defragmentation overhead and over-allocation.
+207        let threshold = 32768.max(buffered * 3 / 2);
+208        if over_allocation > threshold {
+209            self.defragment()
+210        }
+211    }
+212
+213    /// Number of bytes consumed by the application
+214    pub(super) fn bytes_read(&self) -> u64 {
+215        self.bytes_read
+216    }
+217
+218    /// Discard all buffered data
+219    pub(super) fn clear(&mut self) {
+220        self.data.clear();
+221        self.buffered = 0;
+222        self.allocated = 0;
+223    }
+224}
+225
+226/// A chunk of data from the receive stream
+227#[derive(Debug, PartialEq, Eq)]
+228pub struct Chunk {
+229    /// The offset in the stream
+230    pub offset: u64,
+231    /// The contents of the chunk
+232    pub bytes: Bytes,
+233}
+234
+235impl Chunk {
+236    fn new(offset: u64, bytes: Bytes) -> Self {
+237        Self { offset, bytes }
+238    }
+239}
+240
+241#[derive(Debug, Eq)]
+242struct Buffer {
+243    offset: u64,
+244    bytes: Bytes,
+245    /// Size of the allocation behind `bytes`, if `defragmented == false`.
+246    /// Otherwise this will be set to `bytes.len()` by `try_mark_defragment`.
+247    /// Will never be less than `bytes.len()`.
+248    allocation_size: usize,
+249    defragmented: bool,
+250}
+251
+252impl Buffer {
+253    /// Constructs a new fragmented Buffer
+254    fn new(offset: u64, bytes: Bytes, allocation_size: usize) -> Self {
+255        Self {
+256            offset,
+257            bytes,
+258            allocation_size,
+259            defragmented: false,
+260        }
+261    }
+262
+263    /// Constructs a new defragmented Buffer
+264    fn new_defragmented(offset: u64, bytes: Bytes) -> Self {
+265        let allocation_size = bytes.len();
+266        Self {
+267            offset,
+268            bytes,
+269            allocation_size,
+270            defragmented: true,
+271        }
+272    }
+273
+274    /// Discards data before `offset` and flags `self` as defragmented if it has good utilization
+275    fn try_mark_defragment(&mut self, offset: u64) {
+276        let duplicate = offset.saturating_sub(self.offset) as usize;
+277        self.offset = self.offset.max(offset);
+278        if duplicate >= self.bytes.len() {
+279            // All bytes are duplicate
+280            self.bytes = Bytes::new();
+281            self.defragmented = true;
+282            self.allocation_size = 0;
+283            return;
+284        }
+285        self.bytes.advance(duplicate);
+286        // Make sure that fragmented buffers with high utilization become defragmented and
+287        // defragmented buffers remain defragmented
+288        self.defragmented = self.defragmented || self.bytes.len() * 6 / 5 >= self.allocation_size;
+289        if self.defragmented {
+290            // Make sure that defragmented buffers do not contribute to over-allocation
+291            self.allocation_size = self.bytes.len();
+292        }
+293    }
+294}
+295
+296impl Ord for Buffer {
+297    // Invert ordering based on offset (max-heap, min offset first),
+298    // prioritize longer chunks at the same offset.
+299    fn cmp(&self, other: &Self) -> Ordering {
+300        self.offset
+301            .cmp(&other.offset)
+302            .reverse()
+303            .then(self.bytes.len().cmp(&other.bytes.len()))
+304    }
+305}
+306
+307impl PartialOrd for Buffer {
+308    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+309        Some(self.cmp(other))
+310    }
+311}
+312
+313impl PartialEq for Buffer {
+314    fn eq(&self, other: &Self) -> bool {
+315        (self.offset, self.bytes.len()) == (other.offset, other.bytes.len())
+316    }
+317}
+318
+319#[derive(Debug, Default)]
+320enum State {
+321    #[default]
+322    Ordered,
+323    Unordered {
+324        /// The set of offsets that have been received from the peer, including portions not yet
+325        /// read by the application.
+326        recvd: ArrayRangeSet,
+327    },
+328}
+329
+330impl State {
+331    fn is_ordered(&self) -> bool {
+332        matches!(self, Self::Ordered)
+333    }
+334}
+335
+336/// Error indicating that an ordered read was performed on a stream after an unordered read
+337#[derive(Debug)]
+338pub(crate) struct IllegalOrderedRead;
+339
+340#[cfg(test)]
+341mod test {
+342    use super::*;
+343    use assert_matches::assert_matches;
+344
+345    #[test]
+346    fn assemble_ordered() {
+347        let mut x = Assembler::new();
+348        assert_matches!(next(&mut x, 32), None);
+349        x.insert(0, Bytes::from_static(b"123"), 3);
+350        assert_matches!(next(&mut x, 1), Some(ref y) if &y[..] == b"1");
+351        assert_matches!(next(&mut x, 3), Some(ref y) if &y[..] == b"23");
+352        x.insert(3, Bytes::from_static(b"456"), 3);
+353        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"456");
+354        x.insert(6, Bytes::from_static(b"789"), 3);
+355        x.insert(9, Bytes::from_static(b"10"), 2);
+356        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"789");
+357        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"10");
+358        assert_matches!(next(&mut x, 32), None);
+359    }
+360
+361    #[test]
+362    fn assemble_unordered() {
+363        let mut x = Assembler::new();
+364        x.ensure_ordering(false).unwrap();
+365        x.insert(3, Bytes::from_static(b"456"), 3);
+366        assert_matches!(next(&mut x, 32), None);
+367        x.insert(0, Bytes::from_static(b"123"), 3);
+368        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
+369        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"456");
+370        assert_matches!(next(&mut x, 32), None);
+371    }
+372
+373    #[test]
+374    fn assemble_duplicate() {
+375        let mut x = Assembler::new();
+376        x.insert(0, Bytes::from_static(b"123"), 3);
+377        x.insert(0, Bytes::from_static(b"123"), 3);
+378        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
+379        assert_matches!(next(&mut x, 32), None);
+380    }
+381
+382    #[test]
+383    fn assemble_duplicate_compact() {
+384        let mut x = Assembler::new();
+385        x.insert(0, Bytes::from_static(b"123"), 3);
+386        x.insert(0, Bytes::from_static(b"123"), 3);
+387        x.defragment();
+388        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
+389        assert_matches!(next(&mut x, 32), None);
+390    }
+391
+392    #[test]
+393    fn assemble_contained() {
+394        let mut x = Assembler::new();
+395        x.insert(0, Bytes::from_static(b"12345"), 5);
+396        x.insert(1, Bytes::from_static(b"234"), 3);
+397        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
+398        assert_matches!(next(&mut x, 32), None);
+399    }
+400
+401    #[test]
+402    fn assemble_contained_compact() {
+403        let mut x = Assembler::new();
+404        x.insert(0, Bytes::from_static(b"12345"), 5);
+405        x.insert(1, Bytes::from_static(b"234"), 3);
+406        x.defragment();
+407        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
+408        assert_matches!(next(&mut x, 32), None);
+409    }
+410
+411    #[test]
+412    fn assemble_contains() {
+413        let mut x = Assembler::new();
+414        x.insert(1, Bytes::from_static(b"234"), 3);
+415        x.insert(0, Bytes::from_static(b"12345"), 5);
+416        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
+417        assert_matches!(next(&mut x, 32), None);
+418    }
+419
+420    #[test]
+421    fn assemble_contains_compact() {
+422        let mut x = Assembler::new();
+423        x.insert(1, Bytes::from_static(b"234"), 3);
+424        x.insert(0, Bytes::from_static(b"12345"), 5);
+425        x.defragment();
+426        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
+427        assert_matches!(next(&mut x, 32), None);
+428    }
+429
+430    #[test]
+431    fn assemble_overlapping() {
+432        let mut x = Assembler::new();
+433        x.insert(0, Bytes::from_static(b"123"), 3);
+434        x.insert(1, Bytes::from_static(b"234"), 3);
+435        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
+436        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"4");
+437        assert_matches!(next(&mut x, 32), None);
+438    }
+439
+440    #[test]
+441    fn assemble_overlapping_compact() {
+442        let mut x = Assembler::new();
+443        x.insert(0, Bytes::from_static(b"123"), 4);
+444        x.insert(1, Bytes::from_static(b"234"), 4);
+445        x.defragment();
+446        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"1234");
+447        assert_matches!(next(&mut x, 32), None);
+448    }
+449
+450    #[test]
+451    fn assemble_complex() {
+452        let mut x = Assembler::new();
+453        x.insert(0, Bytes::from_static(b"1"), 1);
+454        x.insert(2, Bytes::from_static(b"3"), 1);
+455        x.insert(4, Bytes::from_static(b"5"), 1);
+456        x.insert(0, Bytes::from_static(b"123456"), 6);
+457        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123456");
+458        assert_matches!(next(&mut x, 32), None);
+459    }
+460
+461    #[test]
+462    fn assemble_complex_compact() {
+463        let mut x = Assembler::new();
+464        x.insert(0, Bytes::from_static(b"1"), 1);
+465        x.insert(2, Bytes::from_static(b"3"), 1);
+466        x.insert(4, Bytes::from_static(b"5"), 1);
+467        x.insert(0, Bytes::from_static(b"123456"), 6);
+468        x.defragment();
+469        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123456");
+470        assert_matches!(next(&mut x, 32), None);
+471    }
+472
+473    #[test]
+474    fn assemble_old() {
+475        let mut x = Assembler::new();
+476        x.insert(0, Bytes::from_static(b"1234"), 4);
+477        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"1234");
+478        x.insert(0, Bytes::from_static(b"1234"), 4);
+479        assert_matches!(next(&mut x, 32), None);
+480    }
+481
+482    #[test]
+483    fn compact() {
+484        let mut x = Assembler::new();
+485        x.insert(0, Bytes::from_static(b"abc"), 4);
+486        x.insert(3, Bytes::from_static(b"def"), 4);
+487        x.insert(9, Bytes::from_static(b"jkl"), 4);
+488        x.insert(12, Bytes::from_static(b"mno"), 4);
+489        x.defragment();
+490        assert_eq!(
+491            next_unordered(&mut x),
+492            Chunk::new(0, Bytes::from_static(b"abcdef"))
+493        );
+494        assert_eq!(
+495            next_unordered(&mut x),
+496            Chunk::new(9, Bytes::from_static(b"jklmno"))
+497        );
+498    }
+499
+500    #[test]
+501    fn defrag_with_missing_prefix() {
+502        let mut x = Assembler::new();
+503        x.insert(3, Bytes::from_static(b"def"), 3);
+504        x.defragment();
+505        assert_eq!(
+506            next_unordered(&mut x),
+507            Chunk::new(3, Bytes::from_static(b"def"))
+508        );
+509    }
+510
+511    #[test]
+512    fn defrag_read_chunk() {
+513        let mut x = Assembler::new();
+514        x.insert(3, Bytes::from_static(b"def"), 4);
+515        x.insert(0, Bytes::from_static(b"abc"), 4);
+516        x.insert(7, Bytes::from_static(b"hij"), 4);
+517        x.insert(11, Bytes::from_static(b"lmn"), 4);
+518        x.defragment();
+519        assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"abcdef");
+520        x.insert(5, Bytes::from_static(b"fghijklmn"), 9);
+521        assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"ghijklmn");
+522        x.insert(13, Bytes::from_static(b"nopq"), 4);
+523        assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"opq");
+524        x.insert(15, Bytes::from_static(b"pqrs"), 4);
+525        assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"rs");
+526        assert_matches!(x.read(usize::MAX, true), None);
+527    }
+528
+529    #[test]
+530    fn unordered_happy_path() {
+531        let mut x = Assembler::new();
+532        x.ensure_ordering(false).unwrap();
+533        x.insert(0, Bytes::from_static(b"abc"), 3);
+534        assert_eq!(
+535            next_unordered(&mut x),
+536            Chunk::new(0, Bytes::from_static(b"abc"))
+537        );
+538        assert_eq!(x.read(usize::MAX, false), None);
+539        x.insert(3, Bytes::from_static(b"def"), 3);
+540        assert_eq!(
+541            next_unordered(&mut x),
+542            Chunk::new(3, Bytes::from_static(b"def"))
+543        );
+544        assert_eq!(x.read(usize::MAX, false), None);
+545    }
+546
+547    #[test]
+548    fn unordered_dedup() {
+549        let mut x = Assembler::new();
+550        x.ensure_ordering(false).unwrap();
+551        x.insert(3, Bytes::from_static(b"def"), 3);
+552        assert_eq!(
+553            next_unordered(&mut x),
+554            Chunk::new(3, Bytes::from_static(b"def"))
+555        );
+556        assert_eq!(x.read(usize::MAX, false), None);
+557        x.insert(0, Bytes::from_static(b"a"), 1);
+558        x.insert(0, Bytes::from_static(b"abcdefghi"), 9);
+559        x.insert(0, Bytes::from_static(b"abcd"), 4);
+560        assert_eq!(
+561            next_unordered(&mut x),
+562            Chunk::new(0, Bytes::from_static(b"a"))
+563        );
+564        assert_eq!(
+565            next_unordered(&mut x),
+566            Chunk::new(1, Bytes::from_static(b"bc"))
+567        );
+568        assert_eq!(
+569            next_unordered(&mut x),
+570            Chunk::new(6, Bytes::from_static(b"ghi"))
+571        );
+572        assert_eq!(x.read(usize::MAX, false), None);
+573        x.insert(8, Bytes::from_static(b"ijkl"), 4);
+574        assert_eq!(
+575            next_unordered(&mut x),
+576            Chunk::new(9, Bytes::from_static(b"jkl"))
+577        );
+578        assert_eq!(x.read(usize::MAX, false), None);
+579        x.insert(12, Bytes::from_static(b"mno"), 3);
+580        assert_eq!(
+581            next_unordered(&mut x),
+582            Chunk::new(12, Bytes::from_static(b"mno"))
+583        );
+584        assert_eq!(x.read(usize::MAX, false), None);
+585        x.insert(2, Bytes::from_static(b"cde"), 3);
+586        assert_eq!(x.read(usize::MAX, false), None);
+587    }
+588
+589    #[test]
+590    fn chunks_dedup() {
+591        let mut x = Assembler::new();
+592        x.insert(3, Bytes::from_static(b"def"), 3);
+593        assert_eq!(x.read(usize::MAX, true), None);
+594        x.insert(0, Bytes::from_static(b"a"), 1);
+595        x.insert(1, Bytes::from_static(b"bcdefghi"), 9);
+596        x.insert(0, Bytes::from_static(b"abcd"), 4);
+597        assert_eq!(
+598            x.read(usize::MAX, true),
+599            Some(Chunk::new(0, Bytes::from_static(b"abcd")))
+600        );
+601        assert_eq!(
+602            x.read(usize::MAX, true),
+603            Some(Chunk::new(4, Bytes::from_static(b"efghi")))
+604        );
+605        assert_eq!(x.read(usize::MAX, true), None);
+606        x.insert(8, Bytes::from_static(b"ijkl"), 4);
+607        assert_eq!(
+608            x.read(usize::MAX, true),
+609            Some(Chunk::new(9, Bytes::from_static(b"jkl")))
+610        );
+611        assert_eq!(x.read(usize::MAX, true), None);
+612        x.insert(12, Bytes::from_static(b"mno"), 3);
+613        assert_eq!(
+614            x.read(usize::MAX, true),
+615            Some(Chunk::new(12, Bytes::from_static(b"mno")))
+616        );
+617        assert_eq!(x.read(usize::MAX, true), None);
+618        x.insert(2, Bytes::from_static(b"cde"), 3);
+619        assert_eq!(x.read(usize::MAX, true), None);
+620    }
+621
+622    #[test]
+623    fn ordered_eager_discard() {
+624        let mut x = Assembler::new();
+625        x.insert(0, Bytes::from_static(b"abc"), 3);
+626        assert_eq!(x.data.len(), 1);
+627        assert_eq!(
+628            x.read(usize::MAX, true),
+629            Some(Chunk::new(0, Bytes::from_static(b"abc")))
+630        );
+631        x.insert(0, Bytes::from_static(b"ab"), 2);
+632        assert_eq!(x.data.len(), 0);
+633        x.insert(2, Bytes::from_static(b"cd"), 2);
+634        assert_eq!(
+635            x.data.peek(),
+636            Some(&Buffer::new(3, Bytes::from_static(b"d"), 2))
+637        );
+638    }
+639
+640    #[test]
+641    fn ordered_insert_unordered_read() {
+642        let mut x = Assembler::new();
+643        x.insert(0, Bytes::from_static(b"abc"), 3);
+644        x.insert(0, Bytes::from_static(b"abc"), 3);
+645        x.ensure_ordering(false).unwrap();
+646        assert_eq!(
+647            x.read(3, false),
+648            Some(Chunk::new(0, Bytes::from_static(b"abc")))
+649        );
+650        assert_eq!(x.read(3, false), None);
+651    }
+652
+653    #[test]
+654    fn no_duplicate_after_mode_switch() {
+655        // Regression test: bytes read in ordered mode should not be returned again in unordered mode
+656        let mut x = Assembler::new();
+657        x.insert(0, Bytes::from_static(b"a"), 1);
+658        x.insert(0, Bytes::from_static(b"a"), 1); // duplicate
+659        assert_eq!(
+660            x.read(1, true),
+661            Some(Chunk::new(0, Bytes::from_static(b"a")))
+662        );
+663        x.ensure_ordering(false).unwrap();
+664        assert_eq!(x.read(1, false), None); // should be None, byte 0 already returned
+665    }
+666
+667    fn next_unordered(x: &mut Assembler) -> Chunk {
+668        x.read(usize::MAX, false).unwrap()
+669    }
+670
+671    fn next(x: &mut Assembler, size: usize) -> Option<Bytes> {
+672        x.read(size, true).map(|chunk| chunk.bytes)
+673    }
+674}
+675
+676#[cfg(all(test, not(target_family = "wasm")))]
+677mod proptests {
+678    use proptest::prelude::*;
+679    use test_strategy::{Arbitrary, proptest};
+680
+681    use super::*;
+682
+683    const MAX_OFFSET: u64 = 512;
+684    const MAX_LEN: usize = 64;
+685
+686    #[derive(Debug, Clone, Arbitrary)]
+687    enum Op {
+688        #[weight(10)]
+689        Insert {
+690            #[strategy(0..MAX_OFFSET)]
+691            offset: u64,
+692            #[strategy(1..MAX_LEN)]
+693            len: usize,
+694        },
+695        #[weight(10)]
+696        Read {
+697            #[strategy(1..MAX_LEN)]
+698            max_len: usize,
+699        },
+700        #[weight(1)]
+701        EnsureOrdering { ordered: bool },
+702        #[weight(1)]
+703        Defragment,
+704    }
+705
+706    /// Tracks the state of the assembler for verification
+707    struct RefState {
+708        received: Vec<bool>,
+709        returned: Vec<bool>,
+710        ordered: bool,
+711    }
+712
+713    fn set_range(bits: &mut [bool], start: u64, len: usize) {
+714        for i in start..(start + len as u64).min(bits.len() as u64) {
+715            bits[i as usize] = true;
+716        }
+717    }
+718
+719    impl RefState {
+720        fn new() -> Self {
+721            Self {
+722                received: vec![false; MAX_OFFSET as usize],
+723                returned: vec![false; MAX_OFFSET as usize],
+724                ordered: true,
+725            }
+726        }
+727
+728        fn insert(&mut self, offset: u64, len: usize) {
+729            set_range(&mut self.received, offset, len);
+730        }
+731
+732        fn ensure_ordering(&mut self, ordered: bool) -> bool {
+733            if ordered && !self.ordered {
+734                return false;
+735            }
+736            self.ordered = ordered;
+737            true
+738        }
+739
+740        fn bytes_read(&self) -> u64 {
+741            self.returned.iter().filter(|&&x| x).count() as u64
+742        }
+743    }
+744
+745    fn make_data() -> Vec<u8> {
+746        use rand::{Rng, SeedableRng};
+747        let mut rng = rand::rngs::StdRng::seed_from_u64(0xDEADBEEF);
+748        let mut data = vec![0u8; MAX_OFFSET as usize];
+749        rng.fill(data.as_mut_slice());
+750        data
+751    }
+752
+753    fn get_slice(data: &[u8], offset: u64, len: usize) -> Bytes {
+754        let start = offset as usize;
+755        let end = (start + len).min(data.len());
+756        Bytes::copy_from_slice(&data[start..end])
+757    }
+758
+759    fn verify_chunk(data: &[u8], chunk: &Chunk) -> bool {
+760        let start = chunk.offset as usize;
+761        chunk.bytes[..] == data[start..start + chunk.bytes.len()]
+762    }
+763
+764    #[proptest]
+765    fn assembler_matches_reference(
+766        #[strategy(proptest::collection::vec(any::<Op>(), 1..100))] ops: Vec<Op>,
+767    ) {
+768        let data = make_data();
+769        let mut asm = Assembler::new();
+770        let mut reference = RefState::new();
+771
+772        for op in ops {
+773            match op {
+774                Op::Insert { offset, len } => {
+775                    let bytes = get_slice(&data, offset, len);
+776                    asm.insert(offset, bytes, len);
+777                    reference.insert(offset, len);
+778                }
+779                Op::Read { max_len } => {
+780                    let ordered = reference.ordered;
+781                    let actual = asm.read(max_len, ordered);
+782
+783                    match actual {
+784                        None => {
+785                            // Should only be None if no unreturned received bytes available
+786                            let has_available = if ordered {
+787                                // In ordered mode, check if the first unreturned byte is received
+788                                reference
+789                                    .returned
+790                                    .iter()
+791                                    .position(|&x| !x)
+792                                    .is_some_and(|pos| reference.received[pos])
+793                            } else {
+794                                // In unordered mode, check if any unreturned received byte exists
+795                                reference
+796                                    .received
+797                                    .iter()
+798                                    .zip(&reference.returned)
+799                                    .any(|(&r, &ret)| r && !ret)
+800                            };
+801                            prop_assert!(
+802                                !has_available,
+803                                "read returned None but data was available"
+804                            );
+805                        }
+806                        Some(chunk) => {
+807                            prop_assert!(chunk.bytes.len() <= max_len, "chunk exceeds max_len");
+808                            prop_assert!(verify_chunk(&data, &chunk), "data corruption");
+809                            // Mark as returned, check for duplicates
+810                            for i in 0..chunk.bytes.len() {
+811                                let offset = chunk.offset as usize + i;
+812                                prop_assert!(
+813                                    reference.received[offset],
+814                                    "returned unreceived byte at {offset}"
+815                                );
+816                                prop_assert!(
+817                                    !reference.returned[offset],
+818                                    "duplicate byte at {offset}"
+819                                );
+820                                reference.returned[offset] = true;
+821                            }
+822                        }
+823                    }
+824                }
+825                Op::EnsureOrdering { ordered } => {
+826                    let actual = asm.ensure_ordering(ordered).is_ok();
+827                    let expected = reference.ensure_ordering(ordered);
+828                    prop_assert_eq!(actual, expected, "ensure_ordering result mismatch");
+829                }
+830                Op::Defragment => {
+831                    if asm.state.is_ordered() {
+832                        asm.defragment();
+833                    }
+834                }
+835            }
+836        }
+837
+838        // Invariant: bytes_read matches
+839        prop_assert_eq!(
+840            asm.bytes_read(),
+841            reference.bytes_read(),
+842            "bytes_read mismatch"
+843        );
+844    }
+845}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/cid_state.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/cid_state.rs.html new file mode 100644 index 000000000..f756a56ed --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/cid_state.rs.html @@ -0,0 +1,229 @@ +cid_state.rs - source

iroh_quinn_proto/connection/
cid_state.rs

1//! Maintain the state of local connection IDs
+2use std::collections::VecDeque;
+3
+4use rustc_hash::FxHashSet;
+5use tracing::{debug, trace};
+6
+7use crate::{Duration, Instant, TransportError, shared::IssuedCid};
+8
+9/// Local connection ID management
+10#[derive(Debug)]
+11pub(super) struct CidState {
+12    /// Timestamp when issued cids should be retired
+13    ///
+14    /// Each entry indicates the expiration of all timestamps up to the sequence number in
+15    /// the entry.  This means one entry can expire multiple CIDs if the sequence number
+16    /// jumps by more than 1 between entries.
+17    retire_timestamp: VecDeque<CidTimestamp>,
+18    /// Number of local connection IDs that have been issued in NEW_CONNECTION_ID frames.
+19    ///
+20    /// This is thus also the sequence number of the next CID to be issued.
+21    issued: u64,
+22    /// Sequence numbers of local connection IDs not yet retired by the peer
+23    active_seq: FxHashSet<u64>,
+24    /// Sequence number the peer has already retired all CIDs below at our request via `retire_prior_to`
+25    prev_retire_seq: u64,
+26    /// Sequence number to set in retire_prior_to field in NEW_CONNECTION_ID frame
+27    retire_seq: u64,
+28    /// cid length used to decode short packet
+29    cid_len: usize,
+30    /// cid lifetime
+31    cid_lifetime: Option<Duration>,
+32}
+33
+34impl CidState {
+35    pub(crate) fn new(
+36        cid_len: usize,
+37        cid_lifetime: Option<Duration>,
+38        now: Instant,
+39        issued: u64,
+40    ) -> Self {
+41        let mut active_seq = FxHashSet::default();
+42        // Add sequence number of CIDs used in handshaking into tracking set
+43        for seq in 0..issued {
+44            active_seq.insert(seq);
+45        }
+46        let mut this = Self {
+47            retire_timestamp: VecDeque::new(),
+48            issued,
+49            active_seq,
+50            prev_retire_seq: 0,
+51            retire_seq: 0,
+52            cid_len,
+53            cid_lifetime,
+54        };
+55        // Track lifetime of CIDs used in handshaking
+56        for seq in 0..issued {
+57            this.track_lifetime(seq, now);
+58        }
+59        this
+60    }
+61
+62    /// Find the earliest time when previously issued CID should be retired
+63    pub(crate) fn next_timeout(&self) -> Option<Instant> {
+64        self.retire_timestamp.front().map(|nc| {
+65            trace!("CID {} will expire at {:?}", nc.sequence, nc.timestamp);
+66            nc.timestamp
+67        })
+68    }
+69
+70    /// Track the lifetime of issued cids in `retire_timestamp`
+71    fn track_lifetime(&mut self, new_cid_seq: u64, now: Instant) {
+72        let Some(lifetime) = self.cid_lifetime else {
+73            return;
+74        };
+75        let Some(expire_at) = now.checked_add(lifetime) else {
+76            return;
+77        };
+78
+79        let last_record = self.retire_timestamp.back_mut();
+80        if let Some(last) = last_record {
+81            // Compare the timestamp with the last inserted record
+82            // Combine into a single batch if timestamp of current cid is same as the last record
+83            if expire_at == last.timestamp {
+84                debug_assert!(new_cid_seq > last.sequence);
+85                last.sequence = new_cid_seq;
+86                return;
+87            }
+88        }
+89
+90        self.retire_timestamp.push_back(CidTimestamp {
+91            sequence: new_cid_seq,
+92            timestamp: expire_at,
+93        });
+94    }
+95
+96    /// Update local CID state when previously issued CID is retired
+97    ///
+98    /// Return whether a new CID needs to be pushed that notifies remote peer to respond `RETIRE_CONNECTION_ID`
+99    pub(crate) fn on_cid_timeout(&mut self) -> bool {
+100        // Whether the peer hasn't retired all the CIDs we asked it to yet
+101        let unretired_ids_found =
+102            (self.prev_retire_seq..self.retire_seq).any(|seq| self.active_seq.contains(&seq));
+103
+104        let current_retire_prior_to = self.retire_seq;
+105        let next_retire_sequence = self
+106            .retire_timestamp
+107            .pop_front()
+108            .map(|seq| seq.sequence + 1);
+109
+110        // According to RFC:
+111        // Endpoints SHOULD NOT issue updates of the Retire Prior To field
+112        // before receiving RETIRE_CONNECTION_ID frames that retire all
+113        // connection IDs indicated by the previous Retire Prior To value.
+114        // https://tools.ietf.org/html/draft-ietf-quic-transport-29#section-5.1.2
+115        if !unretired_ids_found {
+116            // All Cids are retired, `prev_retire_cid_seq` can be assigned to `retire_cid_seq`
+117            self.prev_retire_seq = self.retire_seq;
+118            // Advance `retire_seq` if next cid that needs to be retired exists
+119            if let Some(next_retire_prior_to) = next_retire_sequence {
+120                self.retire_seq = next_retire_prior_to;
+121            }
+122        }
+123
+124        // Check if retirement of all CIDs that reach their lifetime is still needed
+125        // According to RFC:
+126        // An endpoint MUST NOT
+127        // provide more connection IDs than the peer's limit.  An endpoint MAY
+128        // send connection IDs that temporarily exceed a peer's limit if the
+129        // NEW_CONNECTION_ID frame also requires the retirement of any excess,
+130        // by including a sufficiently large value in the Retire Prior To field.
+131        //
+132        // If yes (return true), a new CID must be pushed with updated `retire_prior_to` field to remote peer.
+133        // If no (return false), it means CIDs that reach the end of lifetime have been retired already. Do not push a new CID in order to avoid violating above RFC.
+134        (current_retire_prior_to..self.retire_seq).any(|seq| self.active_seq.contains(&seq))
+135    }
+136
+137    /// Update cid state when `NewIdentifiers` event is received
+138    ///
+139    /// These are newly generated CIDs which we'll send to the peer in
+140    /// (PATH_)NEW_CONNECTION_ID frames in the next packet that is sent.  This records them
+141    /// and tracks their lifetime.
+142    pub(crate) fn new_cids(&mut self, ids: &[IssuedCid], now: Instant) {
+143        // `ids` could be `None` once active_connection_id_limit is set to 1 by peer
+144        let last_cid = match ids.last() {
+145            Some(cid) => cid,
+146            None => return,
+147        };
+148        self.issued += ids.len() as u64;
+149        // Record the timestamp of CID with the largest seq number
+150        let sequence = last_cid.sequence;
+151        ids.iter().for_each(|frame| {
+152            self.active_seq.insert(frame.sequence);
+153        });
+154        self.track_lifetime(sequence, now);
+155    }
+156
+157    /// Update CidState for receipt of a `RETIRE_CONNECTION_ID` frame
+158    ///
+159    /// Returns whether a new CID can be issued, or an error if the frame was illegal.
+160    pub(crate) fn on_cid_retirement(
+161        &mut self,
+162        sequence: u64,
+163        limit: u64,
+164    ) -> Result<bool, TransportError> {
+165        if self.cid_len == 0 {
+166            return Err(TransportError::PROTOCOL_VIOLATION(
+167                "RETIRE_CONNECTION_ID when CIDs aren't in use",
+168            ));
+169        }
+170        if sequence > self.issued {
+171            debug!(
+172                sequence,
+173                "got RETIRE_CONNECTION_ID for unissued sequence number"
+174            );
+175            return Err(TransportError::PROTOCOL_VIOLATION(
+176                "RETIRE_CONNECTION_ID for unissued sequence number",
+177            ));
+178        }
+179        self.active_seq.remove(&sequence);
+180        // Consider a scenario where peer A has active remote cid 0,1,2.
+181        // Peer B first send a NEW_CONNECTION_ID with cid 3 and retire_prior_to set to 1.
+182        // Peer A processes this NEW_CONNECTION_ID frame; update remote cid to 1,2,3
+183        // and meanwhile send a RETIRE_CONNECTION_ID to retire cid 0 to peer B.
+184        // If peer B doesn't check the cid limit here and send a new cid again, peer A will then face CONNECTION_ID_LIMIT_ERROR
+185        Ok(limit > self.active_seq.len() as u64)
+186    }
+187
+188    /// Length of local Connection IDs
+189    pub(crate) fn cid_len(&self) -> usize {
+190        self.cid_len
+191    }
+192
+193    /// The value for `retire_prior_to` field in `NEW_CONNECTION_ID` frame
+194    pub(crate) fn retire_prior_to(&self) -> u64 {
+195        self.retire_seq
+196    }
+197
+198    pub(crate) fn active_seq(&self) -> (u64, u64) {
+199        let mut min = u64::MAX;
+200        let mut max = u64::MIN;
+201        for n in self.active_seq.iter() {
+202            if n < &min {
+203                min = *n;
+204            }
+205            if n > &max {
+206                max = *n;
+207            }
+208        }
+209        (min, max)
+210    }
+211
+212    #[cfg(test)]
+213    pub(crate) fn assign_retire_seq(&mut self, v: u64) -> u64 {
+214        // Cannot retire more CIDs than what have been issued
+215        debug_assert!(v <= *self.active_seq.iter().max().unwrap() + 1);
+216        let n = v.checked_sub(self.retire_seq).unwrap();
+217        self.retire_seq = v;
+218        n
+219    }
+220}
+221
+222/// Data structure that records when issued cids should be retired
+223#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+224struct CidTimestamp {
+225    /// Highest cid sequence number created in a batch
+226    sequence: u64,
+227    /// Timestamp when cid needs to be retired
+228    timestamp: Instant,
+229}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/datagrams.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/datagrams.rs.html new file mode 100644 index 000000000..8d086e148 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/datagrams.rs.html @@ -0,0 +1,222 @@ +datagrams.rs - source

iroh_quinn_proto/connection/
datagrams.rs

1use std::collections::VecDeque;
+2
+3use bytes::Bytes;
+4use thiserror::Error;
+5use tracing::{debug, trace};
+6
+7use super::Connection;
+8use crate::{
+9    FrameStats, TransportError,
+10    connection::PacketBuilder,
+11    frame::{Datagram, FrameStruct},
+12};
+13
+14/// API to control datagram traffic
+15pub struct Datagrams<'a> {
+16    pub(super) conn: &'a mut Connection,
+17}
+18
+19impl Datagrams<'_> {
+20    /// Queue an unreliable, unordered datagram for immediate transmission
+21    ///
+22    /// If `drop` is true, previously queued datagrams which are still unsent may be discarded to
+23    /// make space for this datagram, in order of oldest to newest. If `drop` is false, and there
+24    /// isn't enough space due to previously queued datagrams, this function will return
+25    /// `SendDatagramError::Blocked`. `Event::DatagramsUnblocked` will be emitted once datagrams
+26    /// have been sent.
+27    ///
+28    /// Returns `Err` iff a `len`-byte datagram cannot currently be sent.
+29    pub fn send(&mut self, data: Bytes, drop: bool) -> Result<(), SendDatagramError> {
+30        if self.conn.config.datagram_receive_buffer_size.is_none() {
+31            return Err(SendDatagramError::Disabled);
+32        }
+33        let max = self
+34            .max_size()
+35            .ok_or(SendDatagramError::UnsupportedByPeer)?;
+36        if data.len() > max {
+37            return Err(SendDatagramError::TooLarge);
+38        }
+39        if drop {
+40            while self.conn.datagrams.outgoing_total > self.conn.config.datagram_send_buffer_size {
+41                let prev = self
+42                    .conn
+43                    .datagrams
+44                    .outgoing
+45                    .pop_front()
+46                    .expect("datagrams.outgoing_total desynchronized");
+47                trace!(len = prev.data.len(), "dropping outgoing datagram");
+48                self.conn.datagrams.outgoing_total -= prev.data.len();
+49            }
+50        } else if self.conn.datagrams.outgoing_total + data.len()
+51            > self.conn.config.datagram_send_buffer_size
+52        {
+53            self.conn.datagrams.send_blocked = true;
+54            return Err(SendDatagramError::Blocked(data));
+55        }
+56        self.conn.datagrams.outgoing_total += data.len();
+57        self.conn.datagrams.outgoing.push_back(Datagram { data });
+58        Ok(())
+59    }
+60
+61    /// Compute the maximum size of datagrams that may be passed to `send_datagram`
+62    ///
+63    /// Returns `None` if datagrams are unsupported by the peer or disabled locally.
+64    ///
+65    /// This may change over the lifetime of a connection according to variation in the path MTU
+66    /// estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer's
+67    /// limit is large this is guaranteed to be a little over a kilobyte at minimum.
+68    ///
+69    /// Not necessarily the maximum size of received datagrams.
+70    ///
+71    /// When multipath is enabled, this is calculated using the smallest MTU across all
+72    /// available paths.
+73    pub fn max_size(&self) -> Option<usize> {
+74        // We use the conservative overhead bound for any packet number, reducing the budget by at
+75        // most 3 bytes, so that PN size fluctuations don't cause users sending maximum-size
+76        // datagrams to suffer avoidable packet loss.
+77        let max_size = self.conn.current_mtu() as usize
+78            - self.conn.predict_1rtt_overhead_no_pn()
+79            - Datagram::SIZE_BOUND;
+80        let limit = self
+81            .conn
+82            .peer_params
+83            .max_datagram_frame_size?
+84            .into_inner()
+85            .saturating_sub(Datagram::SIZE_BOUND as u64);
+86        Some(limit.min(max_size as u64) as usize)
+87    }
+88
+89    /// Receive an unreliable, unordered datagram
+90    pub fn recv(&mut self) -> Option<Bytes> {
+91        self.conn.datagrams.recv()
+92    }
+93
+94    /// Bytes available in the outgoing datagram buffer
+95    ///
+96    /// When greater than zero, [`send`](Self::send)ing a datagram of at most this size is
+97    /// guaranteed not to cause older datagrams to be dropped.
+98    pub fn send_buffer_space(&self) -> usize {
+99        self.conn
+100            .config
+101            .datagram_send_buffer_size
+102            .saturating_sub(self.conn.datagrams.outgoing_total)
+103    }
+104}
+105
+106#[derive(Default)]
+107pub(super) struct DatagramState {
+108    /// Number of bytes of datagrams that have been received by the local transport but not
+109    /// delivered to the application
+110    pub(super) recv_buffered: usize,
+111    pub(super) incoming: VecDeque<Datagram>,
+112    pub(super) outgoing: VecDeque<Datagram>,
+113    pub(super) outgoing_total: usize,
+114    pub(super) send_blocked: bool,
+115}
+116
+117impl DatagramState {
+118    pub(super) fn received(
+119        &mut self,
+120        datagram: Datagram,
+121        window: &Option<usize>,
+122    ) -> Result<bool, TransportError> {
+123        let window = match window {
+124            None => {
+125                return Err(TransportError::PROTOCOL_VIOLATION(
+126                    "unexpected DATAGRAM frame",
+127                ));
+128            }
+129            Some(x) => *x,
+130        };
+131
+132        if datagram.data.len() > window {
+133            return Err(TransportError::PROTOCOL_VIOLATION("oversized datagram"));
+134        }
+135
+136        let was_empty = self.recv_buffered == 0;
+137        while datagram.data.len() + self.recv_buffered > window {
+138            debug!("dropping stale datagram");
+139            self.recv();
+140        }
+141
+142        self.recv_buffered += datagram.data.len();
+143        self.incoming.push_back(datagram);
+144        Ok(was_empty)
+145    }
+146
+147    /// Discard outgoing datagrams with a payload larger than `max_payload` bytes
+148    ///
+149    /// Returns whether any datagrams were dropped.
+150    ///
+151    /// Used to ensure that reductions in MTU don't get us stuck in a state where we have a datagram
+152    /// queued but can't send it.
+153    pub(super) fn drop_oversized(&mut self, max_payload: usize) -> bool {
+154        let mut dropped_any = false;
+155        self.outgoing.retain(|datagram| {
+156            let result = datagram.data.len() < max_payload;
+157            if !result {
+158                trace!(
+159                    "dropping {} byte datagram violating {} byte limit",
+160                    datagram.data.len(),
+161                    max_payload
+162                );
+163                self.outgoing_total -= datagram.data.len();
+164                dropped_any = true;
+165            }
+166            result
+167        });
+168        dropped_any
+169    }
+170
+171    /// Attempt to write a datagram frame into `buf`, consuming it from `self.outgoing`
+172    ///
+173    /// Returns whether a frame was written. At most `max_size` bytes will be written, including
+174    /// framing.
+175    pub(super) fn write<'a, 'b>(
+176        &mut self,
+177        buf: &mut PacketBuilder<'a, 'b>,
+178        stat: &mut FrameStats,
+179    ) -> bool {
+180        let datagram = match self.outgoing.pop_front() {
+181            Some(x) => x,
+182            None => return false,
+183        };
+184
+185        if buf.frame_space_remaining() < datagram.size(true) {
+186            // Future work: we could be more clever about cramming small datagrams into
+187            // mostly-full packets when a larger one is queued first
+188            self.outgoing.push_front(datagram);
+189            return false;
+190        }
+191
+192        self.outgoing_total -= datagram.data.len();
+193        buf.write_frame(datagram, stat);
+194        true
+195    }
+196
+197    pub(super) fn recv(&mut self) -> Option<Bytes> {
+198        let x = self.incoming.pop_front()?.data;
+199        self.recv_buffered -= x.len();
+200        Some(x)
+201    }
+202}
+203
+204/// Errors that can arise when sending a datagram
+205#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+206pub enum SendDatagramError {
+207    /// The peer does not support receiving datagram frames
+208    #[error("datagrams not supported by peer")]
+209    UnsupportedByPeer,
+210    /// Datagram support is disabled locally
+211    #[error("datagram support disabled")]
+212    Disabled,
+213    /// The datagram is larger than the connection can currently accommodate
+214    ///
+215    /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been
+216    /// exceeded.
+217    #[error("datagram too large")]
+218    TooLarge,
+219    /// Send would block
+220    #[error("datagram send blocked")]
+221    Blocked(Bytes),
+222}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/mod.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/mod.rs.html new file mode 100644 index 000000000..fc2e1ed81 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/mod.rs.html @@ -0,0 +1,7064 @@ +mod.rs - source

iroh_quinn_proto/connection/
mod.rs

1use std::{
+2    cmp,
+3    collections::{BTreeMap, VecDeque, btree_map},
+4    convert::TryFrom,
+5    fmt, io, mem,
+6    net::{IpAddr, SocketAddr},
+7    num::{NonZeroU32, NonZeroUsize},
+8    ops::Not,
+9    sync::Arc,
+10};
+11
+12use bytes::{BufMut, Bytes, BytesMut};
+13use frame::StreamMetaVec;
+14
+15use rand::{Rng, SeedableRng, rngs::StdRng};
+16use rustc_hash::{FxHashMap, FxHashSet};
+17use thiserror::Error;
+18use tracing::{debug, error, trace, trace_span, warn};
+19
+20use crate::{
+21    Dir, Duration, EndpointConfig, FourTuple, Frame, INITIAL_MTU, Instant, MAX_CID_SIZE,
+22    MAX_STREAM_COUNT, MIN_INITIAL_SIZE, Side, StreamId, TIMER_GRANULARITY, TokenStore, Transmit,
+23    TransportError, TransportErrorCode, VarInt,
+24    cid_generator::ConnectionIdGenerator,
+25    cid_queue::CidQueue,
+26    config::{ServerConfig, TransportConfig},
+27    congestion::Controller,
+28    connection::{
+29        paths::AbandonState,
+30        qlog::{QlogRecvPacket, QlogSink},
+31        spaces::LostPacket,
+32        timer::{ConnTimer, PathTimer},
+33    },
+34    crypto::{self, KeyPair, Keys, PacketKey},
+35    frame::{
+36        self, Close, DataBlocked, Datagram, FrameStruct, NewToken, ObservedAddr, StreamDataBlocked,
+37        StreamsBlocked,
+38    },
+39    iroh_hp,
+40    packet::{
+41        FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, LongType, Packet,
+42        PacketNumber, PartialDecode, SpaceId,
+43    },
+44    range_set::ArrayRangeSet,
+45    shared::{
+46        ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint,
+47        EndpointEvent, EndpointEventInner,
+48    },
+49    token::{ResetToken, Token, TokenPayload},
+50    transport_parameters::TransportParameters,
+51};
+52
+53mod ack_frequency;
+54use ack_frequency::AckFrequencyState;
+55
+56mod assembler;
+57pub use assembler::Chunk;
+58
+59mod cid_state;
+60use cid_state::CidState;
+61
+62mod datagrams;
+63use datagrams::DatagramState;
+64pub use datagrams::{Datagrams, SendDatagramError};
+65
+66mod mtud;
+67mod pacing;
+68
+69mod packet_builder;
+70use packet_builder::{PacketBuilder, PadDatagram};
+71
+72mod packet_crypto;
+73use packet_crypto::{PrevCrypto, ZeroRttCrypto};
+74
+75mod paths;
+76pub use paths::{ClosedPath, PathEvent, PathId, PathStatus, RttEstimator, SetPathStatusError};
+77use paths::{PathData, PathState};
+78
+79pub(crate) mod qlog;
+80pub(crate) mod send_buffer;
+81
+82mod spaces;
+83#[cfg(fuzzing)]
+84pub use spaces::Retransmits;
+85#[cfg(not(fuzzing))]
+86use spaces::Retransmits;
+87use spaces::{PacketSpace, SendableFrames, SentPacket, ThinRetransmits};
+88
+89mod stats;
+90pub use stats::{ConnectionStats, FrameStats, PathStats, UdpStats};
+91
+92mod streams;
+93#[cfg(fuzzing)]
+94pub use streams::StreamsState;
+95#[cfg(not(fuzzing))]
+96use streams::StreamsState;
+97pub use streams::{
+98    Chunks, ClosedStream, FinishError, ReadError, ReadableError, RecvStream, SendStream,
+99    ShouldTransmit, StreamEvent, Streams, WriteError, Written,
+100};
+101
+102mod timer;
+103use timer::{Timer, TimerTable};
+104
+105mod transmit_buf;
+106use transmit_buf::TransmitBuf;
+107
+108mod state;
+109
+110#[cfg(not(fuzzing))]
+111use state::State;
+112#[cfg(fuzzing)]
+113pub use state::State;
+114use state::StateType;
+115
+116/// Protocol state and logic for a single QUIC connection
+117///
+118/// Objects of this type receive [`ConnectionEvent`]s and emit [`EndpointEvent`]s and application
+119/// [`Event`]s to make progress. To handle timeouts, a `Connection` returns timer updates and
+120/// expects timeouts through various methods. A number of simple getter methods are exposed
+121/// to allow callers to inspect some of the connection state.
+122///
+123/// `Connection` has roughly 4 types of methods:
+124///
+125/// - A. Simple getters, taking `&self`
+126/// - B. Handlers for incoming events from the network or system, named `handle_*`.
+127/// - C. State machine mutators, for incoming commands from the application. For convenience we
+128///   refer to this as "performing I/O" below, however as per the design of this library none of the
+129///   functions actually perform system-level I/O. For example, [`read`](RecvStream::read) and
+130///   [`write`](SendStream::write), but also things like [`reset`](SendStream::reset).
+131/// - D. Polling functions for outgoing events or actions for the caller to
+132///   take, named `poll_*`.
+133///
+134/// The simplest way to use this API correctly is to call (B) and (C) whenever
+135/// appropriate, then after each of those calls, as soon as feasible call all
+136/// polling methods (D) and deal with their outputs appropriately, e.g. by
+137/// passing it to the application or by making a system-level I/O call. You
+138/// should call the polling functions in this order:
+139///
+140/// 1. [`poll_transmit`](Self::poll_transmit)
+141/// 2. [`poll_timeout`](Self::poll_timeout)
+142/// 3. [`poll_endpoint_events`](Self::poll_endpoint_events)
+143/// 4. [`poll`](Self::poll)
+144///
+145/// Currently the only actual dependency is from (2) to (1), however additional
+146/// dependencies may be added in future, so the above order is recommended.
+147///
+148/// (A) may be called whenever desired.
+149///
+150/// Care should be made to ensure that the input events represent monotonically
+151/// increasing time. Specifically, calling [`handle_timeout`](Self::handle_timeout)
+152/// with events of the same [`Instant`] may be interleaved in any order with a
+153/// call to [`handle_event`](Self::handle_event) at that same instant; however
+154/// events or timeouts with different instants must not be interleaved.
+155pub struct Connection {
+156    endpoint_config: Arc<EndpointConfig>,
+157    config: Arc<TransportConfig>,
+158    rng: StdRng,
+159    crypto: Box<dyn crypto::Session>,
+160    /// The CID we initially chose, for use during the handshake
+161    handshake_cid: ConnectionId,
+162    /// The CID the peer initially chose, for use during the handshake
+163    remote_handshake_cid: ConnectionId,
+164    /// The [`PathData`] for each path
+165    ///
+166    /// This needs to be ordered because [`Connection::poll_transmit`] needs to
+167    /// deterministically select the next PathId to send on.
+168    // TODO(flub): well does it really? But deterministic is nice for now.
+169    paths: BTreeMap<PathId, PathState>,
+170    /// Counter to uniquely identify every [`PathData`] created in this connection.
+171    ///
+172    /// Each [`PathData`] gets a [`PathData::generation`] that is unique among all
+173    /// [`PathData`]s created in the lifetime of this connection. This helps identify the
+174    /// correct path when RFC9000-style migrations happen, even when they are
+175    /// aborted.
+176    ///
+177    /// Multipath does not change this, each path can also undergo RFC9000-style
+178    /// migrations. So a single multipath path ID could see several [`PathData`]s each with
+179    /// their unique [`PathData::generation].
+180    path_generation_counter: u64,
+181    /// Whether MTU detection is supported in this environment
+182    allow_mtud: bool,
+183    state: State,
+184    side: ConnectionSide,
+185    /// Whether or not 0-RTT was enabled during the handshake. Does not imply acceptance.
+186    zero_rtt_enabled: bool,
+187    /// Set if 0-RTT is supported, then cleared when no longer needed.
+188    zero_rtt_crypto: Option<ZeroRttCrypto>,
+189    key_phase: bool,
+190    /// How many packets are in the current key phase. Used only for `Data` space.
+191    key_phase_size: u64,
+192    /// Transport parameters set by the peer
+193    peer_params: TransportParameters,
+194    /// Source ConnectionId of the first packet received from the peer
+195    original_remote_cid: ConnectionId,
+196    /// Destination ConnectionId sent by the client on the first Initial
+197    initial_dst_cid: ConnectionId,
+198    /// The value that the server included in the Source Connection ID field of a Retry packet, if
+199    /// one was received
+200    retry_src_cid: Option<ConnectionId>,
+201    /// Events returned by [`Connection::poll`]
+202    events: VecDeque<Event>,
+203    endpoint_events: VecDeque<EndpointEventInner>,
+204    /// Whether the spin bit is in use for this connection
+205    spin_enabled: bool,
+206    /// Outgoing spin bit state
+207    spin: bool,
+208    /// Packet number spaces: initial, handshake, 1-RTT
+209    spaces: [PacketSpace; 3],
+210    /// Highest usable [`SpaceId`]
+211    highest_space: SpaceId,
+212    /// 1-RTT keys used prior to a key update
+213    prev_crypto: Option<PrevCrypto>,
+214    /// 1-RTT keys to be used for the next key update
+215    ///
+216    /// These are generated in advance to prevent timing attacks and/or DoS by third-party attackers
+217    /// spoofing key updates.
+218    next_crypto: Option<KeyPair<Box<dyn PacketKey>>>,
+219    accepted_0rtt: bool,
+220    /// Whether the idle timer should be reset the next time an ack-eliciting packet is transmitted.
+221    permit_idle_reset: bool,
+222    /// Negotiated idle timeout
+223    idle_timeout: Option<Duration>,
+224    timers: TimerTable,
+225    /// Number of packets received which could not be authenticated
+226    authentication_failures: u64,
+227
+228    //
+229    // Queued non-retransmittable 1-RTT data
+230    //
+231    /// If the CONNECTION_CLOSE frame needs to be sent
+232    connection_close_pending: bool,
+233
+234    //
+235    // ACK frequency
+236    //
+237    ack_frequency: AckFrequencyState,
+238
+239    //
+240    // Congestion Control
+241    //
+242    /// Whether the most recently received packet had an ECN codepoint set
+243    receiving_ecn: bool,
+244    /// Number of packets authenticated
+245    total_authed_packets: u64,
+246    /// Whether the last `poll_transmit` call yielded no data because there was
+247    /// no outgoing application data.
+248    app_limited: bool,
+249
+250    //
+251    // ObservedAddr
+252    //
+253    /// Sequence number for the next observed address frame sent to the peer.
+254    next_observed_addr_seq_no: VarInt,
+255
+256    streams: StreamsState,
+257    /// Surplus remote CIDs for future use on new paths
+258    ///
+259    /// These are given out before multiple paths exist, also for paths that will never
+260    /// exist.  So if multipath is supported the number of paths here will be higher than
+261    /// the actual number of paths in use.
+262    remote_cids: FxHashMap<PathId, CidQueue>,
+263    /// Attributes of CIDs generated by local endpoint
+264    ///
+265    /// Any path that is allowed to be opened is present in this map, as well as the already
+266    /// opened paths. However since CIDs are issued async by the endpoint driver via
+267    /// connection events it can not be used to know if CIDs have been issued for a path or
+268    /// not. See [`Connection::max_path_id_with_cids`] for this.
+269    local_cid_state: FxHashMap<PathId, CidState>,
+270    /// State of the unreliable datagram extension
+271    datagrams: DatagramState,
+272    /// Connection level statistics
+273    stats: ConnectionStats,
+274    /// Path level statistics
+275    path_stats: FxHashMap<PathId, PathStats>,
+276    /// QUIC version used for the connection.
+277    version: u32,
+278
+279    //
+280    // Multipath
+281    //
+282    /// Maximum number of concurrent paths
+283    ///
+284    /// Initially set from the [`TransportConfig::max_concurrent_multipath_paths`]. Even
+285    /// when multipath is disabled this will be set to 1, it is not used in that case
+286    /// though.
+287    max_concurrent_paths: NonZeroU32,
+288    /// Local maximum [`PathId`] to be used
+289    ///
+290    /// This is initially set to [`TransportConfig::get_initial_max_path_id`] when multipath
+291    /// is negotiated, or to [`PathId::ZERO`] otherwise. This is essentially the value of
+292    /// the highest MAX_PATH_ID frame sent.
+293    ///
+294    /// Any path with an ID equal or below this [`PathId`] is either:
+295    ///
+296    /// - Abandoned, if it is also in [`Connection::abandoned_paths`].
+297    /// - Open, in this case it is present in [`Connection::paths`]
+298    /// - Not yet opened, if it is in neither of these two places.
+299    ///
+300    /// Note that for not-yet-open there may or may not be any CIDs issued. See
+301    /// [`Connection::max_path_id_with_cids`].
+302    local_max_path_id: PathId,
+303    /// Remote's maximum [`PathId`] to be used
+304    ///
+305    /// This is initially set to the peer's [`TransportParameters::initial_max_path_id`] when
+306    /// multipath is negotiated, or to [`PathId::ZERO`] otherwise. A peer may increase this limit
+307    /// by sending [`Frame::MaxPathId`] frames.
+308    remote_max_path_id: PathId,
+309    /// The greatest [`PathId`] we have issued CIDs for
+310    ///
+311    /// CIDs are only issued for `min(local_max_path_id, remote_max_path_id)`. It is not
+312    /// possible to use [`Connection::local_cid_state`] to know if CIDs have been issued
+313    /// since they are issued asynchronously by the endpoint driver.
+314    max_path_id_with_cids: PathId,
+315    /// The paths already abandoned
+316    ///
+317    /// They may still have some state left in [`Connection::paths`] or
+318    /// [`Connection::local_cid_state`] since some of this has to be kept around for some
+319    /// time after a path is abandoned.
+320    // TODO(flub): Make this a more efficient data structure.  Like ranges of abandoned
+321    //    paths.  Or a set together with a minimum.  Or something.
+322    abandoned_paths: FxHashSet<PathId>,
+323
+324    iroh_hp: iroh_hp::State,
+325    qlog: QlogSink,
+326}
+327
+328/// Return value for [`Connection::poll_transmit_path`].
+329#[derive(Debug)]
+330enum PollPathStatus {
+331    /// Nothing to send on the path, nothing was written into the [`TransmitBuf`].
+332    NothingToSend {
+333        /// If true there was data to send but congestion control did not allow so.
+334        congestion_blocked: bool,
+335    },
+336    /// The transmit is ready to be sent.
+337    Send(Transmit),
+338}
+339
+340/// Return value for [`Connection::poll_transmit_path_space`].
+341#[derive(Debug)]
+342enum PollPathSpaceStatus {
+343    /// Nothing to send in the space, nothing was written into the [`TransmitBuf`].
+344    NothingToSend {
+345        /// If true there was data to send but congestion control did not allow so.
+346        congestion_blocked: bool,
+347    },
+348    /// One or more packets have been written into the [`TransmitBuf`].
+349    WrotePacket {
+350        /// The highest packet number.
+351        last_packet_number: u64,
+352        /// Whether to pad an already started datagram in the next packet.
+353        ///
+354        /// When packets in Initial, 0-RTT or Handshake packet do not fill the entire
+355        /// datagram they may decide to coalesce with the next packet from a higher
+356        /// encryption level on the same path. But the earlier packet may require specific
+357        /// size requirements for the datagram they are sent in.
+358        ///
+359        /// If a space did not complete the datagram, they use this to request the correct
+360        /// padding in the final packet of the datagram so that the final datagram will have
+361        /// the correct size.
+362        ///
+363        /// If a space did fill an entire datagram, it leaves this to the default of
+364        /// [`PadDatagram::No`].
+365        pad_datagram: PadDatagram,
+366    },
+367    /// Send the contents of the transmit immediately.
+368    ///
+369    /// Packets were written and the GSO batch must end now, regardless from whether higher
+370    /// spaces still have frames to write. This is used when the last datagram written would
+371    /// require too much padding to continue a GSO batch, which would waste space on the
+372    /// wire.
+373    Send {
+374        /// The highest packet number written into the transmit.
+375        last_packet_number: u64,
+376    },
+377}
+378
+379impl Connection {
+380    pub(crate) fn new(
+381        endpoint_config: Arc<EndpointConfig>,
+382        config: Arc<TransportConfig>,
+383        init_cid: ConnectionId,
+384        local_cid: ConnectionId,
+385        remote_cid: ConnectionId,
+386        network_path: FourTuple,
+387        crypto: Box<dyn crypto::Session>,
+388        cid_gen: &dyn ConnectionIdGenerator,
+389        now: Instant,
+390        version: u32,
+391        allow_mtud: bool,
+392        rng_seed: [u8; 32],
+393        side_args: SideArgs,
+394        qlog: QlogSink,
+395    ) -> Self {
+396        let pref_addr_cid = side_args.pref_addr_cid();
+397        let path_validated = side_args.path_validated();
+398        let connection_side = ConnectionSide::from(side_args);
+399        let side = connection_side.side();
+400        let mut rng = StdRng::from_seed(rng_seed);
+401        let initial_space = {
+402            let mut space = PacketSpace::new(now, SpaceId::Initial, &mut rng);
+403            space.crypto = Some(crypto.initial_keys(init_cid, side));
+404            space
+405        };
+406        let handshake_space = PacketSpace::new(now, SpaceId::Handshake, &mut rng);
+407        #[cfg(test)]
+408        let data_space = match config.deterministic_packet_numbers {
+409            true => PacketSpace::new_deterministic(now, SpaceId::Data),
+410            false => PacketSpace::new(now, SpaceId::Data, &mut rng),
+411        };
+412        #[cfg(not(test))]
+413        let data_space = PacketSpace::new(now, SpaceId::Data, &mut rng);
+414        let state = State::handshake(state::Handshake {
+415            remote_cid_set: side.is_server(),
+416            expected_token: Bytes::new(),
+417            client_hello: None,
+418            allow_server_migration: side.is_client(),
+419        });
+420        let local_cid_state = FxHashMap::from_iter([(
+421            PathId::ZERO,
+422            CidState::new(
+423                cid_gen.cid_len(),
+424                cid_gen.cid_lifetime(),
+425                now,
+426                if pref_addr_cid.is_some() { 2 } else { 1 },
+427            ),
+428        )]);
+429
+430        let mut path = PathData::new(network_path, allow_mtud, None, 0, now, &config);
+431        // TODO(@divma): consider if we want to delay this until the path is validated
+432        path.open = true;
+433        let mut this = Self {
+434            endpoint_config,
+435            crypto,
+436            handshake_cid: local_cid,
+437            remote_handshake_cid: remote_cid,
+438            local_cid_state,
+439            paths: BTreeMap::from_iter([(
+440                PathId::ZERO,
+441                PathState {
+442                    data: path,
+443                    prev: None,
+444                },
+445            )]),
+446            path_generation_counter: 0,
+447            allow_mtud,
+448            state,
+449            side: connection_side,
+450            zero_rtt_enabled: false,
+451            zero_rtt_crypto: None,
+452            key_phase: false,
+453            // A small initial key phase size ensures peers that don't handle key updates correctly
+454            // fail sooner rather than later. It's okay for both peers to do this, as the first one
+455            // to perform an update will reset the other's key phase size in `update_keys`, and a
+456            // simultaneous key update by both is just like a regular key update with a really fast
+457            // response. Inspired by quic-go's similar behavior of performing the first key update
+458            // at the 100th short-header packet.
+459            key_phase_size: rng.random_range(10..1000),
+460            peer_params: TransportParameters::default(),
+461            original_remote_cid: remote_cid,
+462            initial_dst_cid: init_cid,
+463            retry_src_cid: None,
+464            events: VecDeque::new(),
+465            endpoint_events: VecDeque::new(),
+466            spin_enabled: config.allow_spin && rng.random_ratio(7, 8),
+467            spin: false,
+468            spaces: [initial_space, handshake_space, data_space],
+469            highest_space: SpaceId::Initial,
+470            prev_crypto: None,
+471            next_crypto: None,
+472            accepted_0rtt: false,
+473            permit_idle_reset: true,
+474            idle_timeout: match config.max_idle_timeout {
+475                None | Some(VarInt(0)) => None,
+476                Some(dur) => Some(Duration::from_millis(dur.0)),
+477            },
+478            timers: TimerTable::default(),
+479            authentication_failures: 0,
+480            connection_close_pending: false,
+481
+482            ack_frequency: AckFrequencyState::new(get_max_ack_delay(
+483                &TransportParameters::default(),
+484            )),
+485
+486            app_limited: false,
+487            receiving_ecn: false,
+488            total_authed_packets: 0,
+489
+490            next_observed_addr_seq_no: 0u32.into(),
+491
+492            streams: StreamsState::new(
+493                side,
+494                config.max_concurrent_uni_streams,
+495                config.max_concurrent_bidi_streams,
+496                config.send_window,
+497                config.receive_window,
+498                config.stream_receive_window,
+499            ),
+500            datagrams: DatagramState::default(),
+501            config,
+502            remote_cids: FxHashMap::from_iter([(PathId::ZERO, CidQueue::new(remote_cid))]),
+503            rng,
+504            stats: ConnectionStats::default(),
+505            path_stats: Default::default(),
+506            version,
+507
+508            // peer params are not yet known, so multipath is not enabled
+509            max_concurrent_paths: NonZeroU32::MIN,
+510            local_max_path_id: PathId::ZERO,
+511            remote_max_path_id: PathId::ZERO,
+512            max_path_id_with_cids: PathId::ZERO,
+513            abandoned_paths: Default::default(),
+514
+515            // iroh's nat traversal
+516            iroh_hp: Default::default(),
+517            qlog,
+518        };
+519        if path_validated {
+520            this.on_path_validated(PathId::ZERO);
+521        }
+522        if side.is_client() {
+523            // Kick off the connection
+524            this.write_crypto();
+525            this.init_0rtt(now);
+526        }
+527        this.qlog
+528            .emit_tuple_assigned(PathId::ZERO, network_path, now);
+529        this
+530    }
+531
+532    /// Returns the next time at which `handle_timeout` should be called
+533    ///
+534    /// The value returned may change after:
+535    /// - the application performed some I/O on the connection
+536    /// - a call was made to `handle_event`
+537    /// - a call to `poll_transmit` returned `Some`
+538    /// - a call was made to `handle_timeout`
+539    #[must_use]
+540    pub fn poll_timeout(&mut self) -> Option<Instant> {
+541        self.timers.peek()
+542    }
+543
+544    /// Returns application-facing events
+545    ///
+546    /// Connections should be polled for events after:
+547    /// - a call was made to `handle_event`
+548    /// - a call was made to `handle_timeout`
+549    #[must_use]
+550    pub fn poll(&mut self) -> Option<Event> {
+551        if let Some(x) = self.events.pop_front() {
+552            return Some(x);
+553        }
+554
+555        if let Some(event) = self.streams.poll() {
+556            return Some(Event::Stream(event));
+557        }
+558
+559        if let Some(reason) = self.state.take_error() {
+560            return Some(Event::ConnectionLost { reason });
+561        }
+562
+563        None
+564    }
+565
+566    /// Return endpoint-facing events
+567    #[must_use]
+568    pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent> {
+569        self.endpoint_events.pop_front().map(EndpointEvent)
+570    }
+571
+572    /// Provide control over streams
+573    #[must_use]
+574    pub fn streams(&mut self) -> Streams<'_> {
+575        Streams {
+576            state: &mut self.streams,
+577            conn_state: &self.state,
+578        }
+579    }
+580
+581    /// Provide control over streams
+582    #[must_use]
+583    pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_> {
+584        assert!(id.dir() == Dir::Bi || id.initiator() != self.side.side());
+585        RecvStream {
+586            id,
+587            state: &mut self.streams,
+588            pending: &mut self.spaces[SpaceId::Data].pending,
+589        }
+590    }
+591
+592    /// Provide control over streams
+593    #[must_use]
+594    pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_> {
+595        assert!(id.dir() == Dir::Bi || id.initiator() == self.side.side());
+596        SendStream {
+597            id,
+598            state: &mut self.streams,
+599            pending: &mut self.spaces[SpaceId::Data].pending,
+600            conn_state: &self.state,
+601        }
+602    }
+603
+604    /// Opens a new path only if no path on the same network path currently exists.
+605    ///
+606    /// This comparison will use [`FourTuple::is_probably_same_path`] on the given `network_path`
+607    /// and pass it existing path's network paths.
+608    ///
+609    /// This means that you can pass `local_ip: None` to make the comparison only compare
+610    /// remote addresses.
+611    ///
+612    /// This avoids having to guess which local interface will be used to communicate with the
+613    /// remote, should it not be known yet. We assume that if we already have a path to the remote,
+614    /// the OS is likely to use the same interface to talk to said remote.
+615    ///
+616    /// See also [`open_path`]. Returns `(path_id, true)` if the path already existed. `(path_id,
+617    /// false)` if was opened.
+618    ///
+619    /// [`open_path`]: Connection::open_path
+620    pub fn open_path_ensure(
+621        &mut self,
+622        network_path: FourTuple,
+623        initial_status: PathStatus,
+624        now: Instant,
+625    ) -> Result<(PathId, bool), PathError> {
+626        let existing_open_path = self.paths.iter().find(|(id, path)| {
+627            network_path.is_probably_same_path(&path.data.network_path)
+628                && !self.abandoned_paths.contains(*id)
+629        });
+630        match existing_open_path {
+631            Some((path_id, _state)) => Ok((*path_id, true)),
+632            None => Ok((self.open_path(network_path, initial_status, now)?, false)),
+633        }
+634    }
+635
+636    /// Opens a new path
+637    ///
+638    /// Further errors might occur and they will be emitted in [`PathEvent::LocallyClosed`] events.
+639    /// When the path is opened it will be reported as an [`PathEvent::Opened`].
+640    pub fn open_path(
+641        &mut self,
+642        network_path: FourTuple,
+643        initial_status: PathStatus,
+644        now: Instant,
+645    ) -> Result<PathId, PathError> {
+646        if !self.is_multipath_negotiated() {
+647            return Err(PathError::MultipathNotNegotiated);
+648        }
+649        if self.side().is_server() {
+650            return Err(PathError::ServerSideNotAllowed);
+651        }
+652
+653        let max_abandoned = self.abandoned_paths.iter().max().copied();
+654        let max_used = self.paths.keys().last().copied();
+655        let path_id = max_abandoned
+656            .max(max_used)
+657            .unwrap_or(PathId::ZERO)
+658            .saturating_add(1u8);
+659
+660        if Some(path_id) > self.max_path_id() {
+661            return Err(PathError::MaxPathIdReached);
+662        }
+663        if path_id > self.remote_max_path_id {
+664            self.spaces[SpaceId::Data].pending.paths_blocked = true;
+665            return Err(PathError::MaxPathIdReached);
+666        }
+667        if self
+668            .remote_cids
+669            .get(&path_id)
+670            .map(CidQueue::active)
+671            .is_none()
+672        {
+673            self.spaces[SpaceId::Data]
+674                .pending
+675                .path_cids_blocked
+676                .insert(path_id);
+677            return Err(PathError::RemoteCidsExhausted);
+678        }
+679
+680        let path = self.ensure_path(path_id, network_path, now, None);
+681        path.status.local_update(initial_status);
+682
+683        Ok(path_id)
+684    }
+685
+686    /// Closes a path by sending a PATH_ABANDON frame
+687    ///
+688    /// This will not allow closing the last path. It does allow closing paths which have
+689    /// not yet been opened, as e.g. is the case when receiving a PATH_ABANDON from the peer
+690    /// for a path that was never opened locally.
+691    pub fn close_path(
+692        &mut self,
+693        now: Instant,
+694        path_id: PathId,
+695        error_code: VarInt,
+696    ) -> Result<(), ClosePathError> {
+697        if self.abandoned_paths.contains(&path_id)
+698            || Some(path_id) > self.max_path_id()
+699            || !self.paths.contains_key(&path_id)
+700        {
+701            return Err(ClosePathError::ClosedPath);
+702        }
+703        if self
+704            .paths
+705            .iter()
+706            // Would there be any remaining, non-abandoned, validated paths
+707            .any(|(id, path)| {
+708                *id != path_id && !self.abandoned_paths.contains(id) && path.data.validated
+709            })
+710            .not()
+711        {
+712            return Err(ClosePathError::LastOpenPath);
+713        }
+714
+715        // Send PATH_ABANDON
+716        self.spaces[SpaceId::Data]
+717            .pending
+718            .path_abandon
+719            .insert(path_id, error_code.into());
+720
+721        // Remove pending NEW CIDs for this path
+722        let pending_space = &mut self.spaces[SpaceId::Data].pending;
+723        pending_space.new_cids.retain(|cid| cid.path_id != path_id);
+724        pending_space.path_cids_blocked.retain(|&id| id != path_id);
+725        pending_space.path_status.retain(|&id| id != path_id);
+726
+727        // Cleanup retransmits across ALL paths (CIDs for path_id may have been transmitted on other paths)
+728        for space in self.spaces[SpaceId::Data].iter_paths_mut() {
+729            for sent_packet in space.sent_packets.values_mut() {
+730                if let Some(retransmits) = sent_packet.retransmits.get_mut() {
+731                    retransmits.new_cids.retain(|cid| cid.path_id != path_id);
+732                    retransmits.path_cids_blocked.retain(|&id| id != path_id);
+733                    retransmits.path_status.retain(|&id| id != path_id);
+734                }
+735            }
+736        }
+737
+738        // Consider remotely issued CIDs as retired.
+739        // Technically we don't have to do this just yet.  We only need to do this *after*
+740        // the ABANDON_PATH frame is sent, allowing us to still send it on the
+741        // to-be-abandoned path.  However it is recommended to send it on another path, and
+742        // we do not allow abandoning the last path anyway.
+743        self.remote_cids.remove(&path_id);
+744        self.endpoint_events
+745            .push_back(EndpointEventInner::RetireResetToken(path_id));
+746
+747        trace!(%path_id, "abandoning path");
+748        self.abandoned_paths.insert(path_id);
+749
+750        self.set_max_path_id(now, self.local_max_path_id.saturating_add(1u8));
+751
+752        // Clear all timers.
+753        // We still need some timers after we close a path, e.g. the `DiscardPath` timer,
+754        // but that timer is going to be set once we the `PATH_ABANDON` frame is sent.
+755        self.timers.stop_per_path(path_id, self.qlog.with_time(now));
+756
+757        Ok(())
+758    }
+759
+760    /// Gets the [`PathData`] for a known [`PathId`].
+761    ///
+762    /// Will panic if the path_id does not reference any known path.
+763    #[track_caller]
+764    fn path_data(&self, path_id: PathId) -> &PathData {
+765        if let Some(data) = self.paths.get(&path_id) {
+766            &data.data
+767        } else {
+768            panic!(
+769                "unknown path: {path_id}, currently known paths: {:?}",
+770                self.paths.keys().collect::<Vec<_>>()
+771            );
+772        }
+773    }
+774
+775    /// Gets a reference to the [`PathData`] for a [`PathId`]
+776    fn path(&self, path_id: PathId) -> Option<&PathData> {
+777        self.paths.get(&path_id).map(|path_state| &path_state.data)
+778    }
+779
+780    /// Gets a mutable reference to the [`PathData`] for a [`PathId`]
+781    fn path_mut(&mut self, path_id: PathId) -> Option<&mut PathData> {
+782        self.paths
+783            .get_mut(&path_id)
+784            .map(|path_state| &mut path_state.data)
+785    }
+786
+787    /// Returns all known paths.
+788    ///
+789    /// There is no guarantee any of these paths are open or usable.
+790    pub fn paths(&self) -> Vec<PathId> {
+791        self.paths.keys().copied().collect()
+792    }
+793
+794    /// Gets the local [`PathStatus`] for a known [`PathId`]
+795    pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath> {
+796        self.path(path_id)
+797            .map(PathData::local_status)
+798            .ok_or(ClosedPath { _private: () })
+799    }
+800
+801    /// Returns the path's network path represented as a 4-tuple.
+802    pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath> {
+803        self.path(path_id)
+804            .map(|path| path.network_path)
+805            .ok_or(ClosedPath { _private: () })
+806    }
+807
+808    /// Sets the [`PathStatus`] for a known [`PathId`]
+809    ///
+810    /// Returns the previous path status on success.
+811    pub fn set_path_status(
+812        &mut self,
+813        path_id: PathId,
+814        status: PathStatus,
+815    ) -> Result<PathStatus, SetPathStatusError> {
+816        if !self.is_multipath_negotiated() {
+817            return Err(SetPathStatusError::MultipathNotNegotiated);
+818        }
+819        let path = self
+820            .path_mut(path_id)
+821            .ok_or(SetPathStatusError::ClosedPath)?;
+822        let prev = match path.status.local_update(status) {
+823            Some(prev) => {
+824                self.spaces[SpaceId::Data]
+825                    .pending
+826                    .path_status
+827                    .insert(path_id);
+828                prev
+829            }
+830            None => path.local_status(),
+831        };
+832        Ok(prev)
+833    }
+834
+835    /// Returns the remote path status
+836    // TODO(flub): Probably should also be some kind of path event?  Not even sure if I like
+837    //    this as an API, but for now it allows me to write a test easily.
+838    // TODO(flub): Technically this should be a Result<Option<PathSTatus>>?
+839    pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus> {
+840        self.path(path_id).and_then(|path| path.remote_status())
+841    }
+842
+843    /// Sets the max_idle_timeout for a specific path
+844    ///
+845    /// See [`TransportConfig::default_path_max_idle_timeout`] for details.
+846    ///
+847    /// Returns the previous value of the setting.
+848    pub fn set_path_max_idle_timeout(
+849        &mut self,
+850        path_id: PathId,
+851        timeout: Option<Duration>,
+852    ) -> Result<Option<Duration>, ClosedPath> {
+853        let path = self
+854            .paths
+855            .get_mut(&path_id)
+856            .ok_or(ClosedPath { _private: () })?;
+857        Ok(std::mem::replace(&mut path.data.idle_timeout, timeout))
+858    }
+859
+860    /// Sets the keep_alive_interval for a specific path
+861    ///
+862    /// See [`TransportConfig::default_path_keep_alive_interval`] for details.
+863    ///
+864    /// Returns the previous value of the setting.
+865    pub fn set_path_keep_alive_interval(
+866        &mut self,
+867        path_id: PathId,
+868        interval: Option<Duration>,
+869    ) -> Result<Option<Duration>, ClosedPath> {
+870        let path = self
+871            .paths
+872            .get_mut(&path_id)
+873            .ok_or(ClosedPath { _private: () })?;
+874        Ok(std::mem::replace(&mut path.data.keep_alive, interval))
+875    }
+876
+877    /// Gets the [`PathData`] for a known [`PathId`].
+878    ///
+879    /// Will panic if the path_id does not reference any known path.
+880    #[track_caller]
+881    fn path_data_mut(&mut self, path_id: PathId) -> &mut PathData {
+882        &mut self.paths.get_mut(&path_id).expect("known path").data
+883    }
+884
+885    /// Find an open, validated path that's on the same network path as the given network path.
+886    ///
+887    /// Returns the first path matching, even if there's multiple.
+888    fn find_validated_path_on_network_path(
+889        &self,
+890        network_path: FourTuple,
+891    ) -> Option<(&PathId, &PathState)> {
+892        self.paths.iter().find(|(path_id, path_state)| {
+893            path_state.data.validated
+894                // Would this use the same network path, if network_path were used to send right now?
+895                && network_path.is_probably_same_path(&path_state.data.network_path)
+896                && !self.abandoned_paths.contains(path_id)
+897        })
+898        // TODO(@divma): we might want to ensure the path has been recently active to consider the
+899        // address validated
+900        // matheus23: Perhaps looking at !self.abandoned_paths.contains(path_id) is enough, given keep-alives?
+901    }
+902
+903    /// Creates the [`PathData`] for a new [`PathId`].
+904    ///
+905    /// Called for incoming packets as well as when opening a new path locally.
+906    fn ensure_path(
+907        &mut self,
+908        path_id: PathId,
+909        network_path: FourTuple,
+910        now: Instant,
+911        pn: Option<u64>,
+912    ) -> &mut PathData {
+913        let valid_path = self.find_validated_path_on_network_path(network_path);
+914        let validated = valid_path.is_some();
+915        let initial_rtt = valid_path.map(|(_, path)| path.data.rtt.conservative());
+916        let vacant_entry = match self.paths.entry(path_id) {
+917            btree_map::Entry::Vacant(vacant_entry) => vacant_entry,
+918            btree_map::Entry::Occupied(occupied_entry) => {
+919                return &mut occupied_entry.into_mut().data;
+920            }
+921        };
+922
+923        debug!(%validated, %path_id, %network_path, "path added");
+924        let peer_max_udp_payload_size =
+925            u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX);
+926        self.path_generation_counter = self.path_generation_counter.wrapping_add(1);
+927        let mut data = PathData::new(
+928            network_path,
+929            self.allow_mtud,
+930            Some(peer_max_udp_payload_size),
+931            self.path_generation_counter,
+932            now,
+933            &self.config,
+934        );
+935
+936        data.validated = validated;
+937        if let Some(initial_rtt) = initial_rtt {
+938            data.rtt.reset_initial_rtt(initial_rtt);
+939        }
+940
+941        let pto = self.ack_frequency.max_ack_delay_for_pto() + data.rtt.pto_base();
+942        self.timers.set(
+943            Timer::PerPath(path_id, PathTimer::PathOpen),
+944            now + 3 * pto,
+945            self.qlog.with_time(now),
+946        );
+947
+948        // To open a path locally we need to send a packet on the path. Sending a challenge
+949        // guarantees this.
+950        data.send_new_challenge = true;
+951
+952        let path = vacant_entry.insert(PathState { data, prev: None });
+953
+954        let mut pn_space = spaces::PacketNumberSpace::new(now, SpaceId::Data, &mut self.rng);
+955        if let Some(pn) = pn {
+956            pn_space.dedup.insert(pn);
+957        }
+958        self.spaces[SpaceId::Data]
+959            .number_spaces
+960            .insert(path_id, pn_space);
+961        self.qlog.emit_tuple_assigned(path_id, network_path, now);
+962
+963        // If the remote opened this path we may not have CIDs for it. For locally opened
+964        // paths the caller should have already made sure we have CIDs and refused to open
+965        // it if there were none.
+966        if !self.remote_cids.contains_key(&path_id) {
+967            debug!(%path_id, "Remote opened path without issuing CIDs");
+968            self.spaces[SpaceId::Data]
+969                .pending
+970                .path_cids_blocked
+971                .insert(path_id);
+972            // Do not abandon this path right away. CIDs might be in-flight still and arrive
+973            // soon. It is up to the remote to handle this situation.
+974        }
+975
+976        &mut path.data
+977    }
+978
+979    /// Returns packets to transmit
+980    ///
+981    /// Connections should be polled for transmit after:
+982    /// - the application performed some I/O on the connection
+983    /// - a call was made to `handle_event`
+984    /// - a call was made to `handle_timeout`
+985    ///
+986    /// `max_datagrams` specifies how many datagrams can be returned inside a
+987    /// single Transmit using GSO. This must be at least 1.
+988    #[must_use]
+989    pub fn poll_transmit(
+990        &mut self,
+991        now: Instant,
+992        max_datagrams: NonZeroUsize,
+993        buf: &mut Vec<u8>,
+994    ) -> Option<Transmit> {
+995        if let Some(probing) = self
+996            .iroh_hp
+997            .server_side_mut()
+998            .ok()
+999            .and_then(iroh_hp::ServerState::next_probe)
+1000        {
+1001            let destination = probing.remote();
+1002            trace!(%destination, "RAND_DATA packet");
+1003            let token: u64 = self.rng.random();
+1004            buf.put_u64(token);
+1005            probing.finish(token);
+1006            return Some(Transmit {
+1007                destination,
+1008                ecn: None,
+1009                size: 8,
+1010                segment_size: None,
+1011                src_ip: None,
+1012            });
+1013        }
+1014
+1015        let max_datagrams = match self.config.enable_segmentation_offload {
+1016            false => NonZeroUsize::MIN,
+1017            true => max_datagrams,
+1018        };
+1019
+1020        // Each call to poll_transmit can only send datagrams to one destination, because
+1021        // all datagrams in a GSO batch are for the same destination.  Therefore only
+1022        // datagrams for one destination address are produced for each poll_transmit call.
+1023
+1024        // Check whether we need to send a close message
+1025        let connection_close_pending = match self.state.as_type() {
+1026            StateType::Drained => {
+1027                self.app_limited = true;
+1028                return None;
+1029            }
+1030            StateType::Draining | StateType::Closed => {
+1031                // self.close is only reset once the associated packet had been
+1032                // encoded successfully
+1033                if !self.connection_close_pending {
+1034                    self.app_limited = true;
+1035                    return None;
+1036                }
+1037                true
+1038            }
+1039            _ => false,
+1040        };
+1041
+1042        // Schedule an ACK_FREQUENCY frame if a new one needs to be sent.
+1043        if let Some(config) = &self.config.ack_frequency_config {
+1044            let rtt = self
+1045                .paths
+1046                .values()
+1047                .map(|p| p.data.rtt.get())
+1048                .min()
+1049                .expect("one path exists");
+1050            self.spaces[SpaceId::Data].pending.ack_frequency = self
+1051                .ack_frequency
+1052                .should_send_ack_frequency(rtt, config, &self.peer_params)
+1053                && self.highest_space == SpaceId::Data
+1054                && self.peer_supports_ack_frequency();
+1055        }
+1056
+1057        // TODO(flub): path scheduling logic might be buggy if there are only un-validated
+1058        //    paths and PATH_STATUS_BACKUP paths.
+1059
+1060        // Path scheduling logic is currently as such:
+1061        //
+1062        // - For any un-validated paths we only send frames that *must* be sent on that
+1063        //   path. E.g. PATH_CHALLENGE, PATH_RESPONSE.
+1064        //
+1065        // - If there are any validated paths with CIDs and PathStatus::Available:
+1066        //   - Frames that can be sent on any path, e.g. STREAM, DATAGRAM, are only sent on
+1067        //     these available paths.
+1068        //   - All other paths only send frames that *must* be sent on those paths,
+1069        //     e.g. PATH_CHALLENGE, PATH_RESPONSE, tail-loss probes, keep alive PING.
+1070        //
+1071        // - If there are no validated paths with CIDs and PathStatus::Available all frames
+1072        //   are sent on the earlierst possible path.
+1073        //
+1074        // For all this we use the *path_exclusive_only* boolean: If set to true, only
+1075        // frames that must be sent on the path will be built into the packet.
+1076
+1077        // Is there any open, validated and status available path with dst CIDs? If so we'll
+1078        // want to set path_exclusive_only for any other paths.
+1079        let have_available_path = self.paths.iter().any(|(id, path)| {
+1080            path.data.validated
+1081                && path.data.local_status() == PathStatus::Available
+1082                && self.remote_cids.contains_key(id)
+1083        });
+1084
+1085        // TODO: how to avoid the allocation? Cannot use a for loop because of
+1086        // borrowing. Maybe SmallVec or similar.
+1087        let path_ids: Vec<_> = self.paths.keys().copied().collect();
+1088
+1089        // If we end up not sending anything, we need to know if that was because there was
+1090        // nothing to send or because we were congestion blocked.
+1091        let mut congestion_blocked = false;
+1092
+1093        for &path_id in &path_ids {
+1094            if !connection_close_pending
+1095                && let Some(transmit) = self.poll_transmit_off_path(now, buf, path_id)
+1096            {
+1097                return Some(transmit);
+1098            }
+1099
+1100            // Poll for on-path transmits.
+1101            match self.poll_transmit_on_path(
+1102                now,
+1103                buf,
+1104                path_id,
+1105                max_datagrams,
+1106                have_available_path,
+1107                connection_close_pending,
+1108            ) {
+1109                PollPathStatus::Send(transmit) => {
+1110                    return Some(transmit);
+1111                }
+1112                PollPathStatus::NothingToSend {
+1113                    congestion_blocked: cb,
+1114                } => {
+1115                    congestion_blocked |= cb;
+1116                    // Continue checking other paths, tail-loss probes may need to be sent
+1117                    // in all spaces.
+1118                    debug_assert!(
+1119                        buf.is_empty(),
+1120                        "nothing to send on path but buffer not empty"
+1121                    );
+1122                }
+1123            }
+1124        }
+1125
+1126        // We didn't produce any application data packet
+1127        debug_assert!(
+1128            buf.is_empty(),
+1129            "there was data in the buffer, but it was not sent"
+1130        );
+1131
+1132        self.app_limited = !congestion_blocked;
+1133
+1134        if self.state.is_established() {
+1135            // Try MTU probing now
+1136            for path_id in path_ids {
+1137                if let Some(transmit) = self.poll_transmit_mtu_probe(now, buf, path_id) {
+1138                    return Some(transmit);
+1139                }
+1140            }
+1141        }
+1142
+1143        None
+1144    }
+1145
+1146    fn build_transmit(&mut self, path_id: PathId, transmit: TransmitBuf<'_>) -> Transmit {
+1147        debug_assert!(
+1148            !transmit.is_empty(),
+1149            "must not be called with an empty transmit buffer"
+1150        );
+1151
+1152        let network_path = self.path_data(path_id).network_path;
+1153        trace!(
+1154            segment_size = transmit.segment_size(),
+1155            last_datagram_len = transmit.len() % transmit.segment_size(),
+1156            %network_path,
+1157            "sending {} bytes in {} datagrams",
+1158            transmit.len(),
+1159            transmit.num_datagrams()
+1160        );
+1161        self.path_data_mut(path_id)
+1162            .inc_total_sent(transmit.len() as u64);
+1163
+1164        self.stats
+1165            .udp_tx
+1166            .on_sent(transmit.num_datagrams() as u64, transmit.len());
+1167        self.path_stats
+1168            .entry(path_id)
+1169            .or_default()
+1170            .udp_tx
+1171            .on_sent(transmit.num_datagrams() as u64, transmit.len());
+1172
+1173        Transmit {
+1174            destination: network_path.remote,
+1175            size: transmit.len(),
+1176            ecn: if self.path_data(path_id).sending_ecn {
+1177                Some(EcnCodepoint::Ect0)
+1178            } else {
+1179                None
+1180            },
+1181            segment_size: match transmit.num_datagrams() {
+1182                1 => None,
+1183                _ => Some(transmit.segment_size()),
+1184            },
+1185            src_ip: network_path.local_ip,
+1186        }
+1187    }
+1188
+1189    /// poll_transmit logic for off-path data.
+1190    fn poll_transmit_off_path(
+1191        &mut self,
+1192        now: Instant,
+1193        buf: &mut Vec<u8>,
+1194        path_id: PathId,
+1195    ) -> Option<Transmit> {
+1196        if let Some(challenge) = self.send_prev_path_challenge(now, buf, path_id) {
+1197            return Some(challenge);
+1198        }
+1199        if let Some(response) = self.send_off_path_path_response(now, buf, path_id) {
+1200            return Some(response);
+1201        }
+1202        None
+1203    }
+1204
+1205    /// poll_transmit logic for on-path data.
+1206    ///
+1207    /// This is not quite the same as for a multipath packet space, since [`PathId::ZERO`]
+1208    /// has 3 packet spaces, which this handles.
+1209    ///
+1210    /// See [`Self::poll_transmit_off_path`] for off-path data.
+1211    #[must_use]
+1212    fn poll_transmit_on_path(
+1213        &mut self,
+1214        now: Instant,
+1215        buf: &mut Vec<u8>,
+1216        path_id: PathId,
+1217        max_datagrams: NonZeroUsize,
+1218        have_available_path: bool,
+1219        connection_close_pending: bool,
+1220    ) -> PollPathStatus {
+1221        // Check if there is at least one active CID to use for sending
+1222        let Some(remote_cid) = self.remote_cids.get(&path_id).map(CidQueue::active) else {
+1223            if !self.abandoned_paths.contains(&path_id) {
+1224                debug!(%path_id, "no remote CIDs for path");
+1225            }
+1226            return PollPathStatus::NothingToSend {
+1227                congestion_blocked: false,
+1228            };
+1229        };
+1230
+1231        // Whether the last packet in the datagram must be padded so the datagram takes up
+1232        // an exact size. An earlier space can decide to not fill an entire datagram and
+1233        // require the next space to fill it further. But may need a specific size of the
+1234        // datagram containing the packet. The final packet built in the datagram must pad
+1235        // to this size.
+1236        let mut pad_datagram = PadDatagram::No;
+1237
+1238        // The packet number of the last built packet. This is kept kept across spaces.
+1239        // QUIC is supposed to have a single congestion controller for the Initial,
+1240        // Handshake and Data(PathId::ZERO) spaces.
+1241        let mut last_packet_number = None;
+1242
+1243        // If we end up not sending anything, we need to know if that was because there was
+1244        // nothing to send or because we were congestion blocked.
+1245        let mut congestion_blocked = false;
+1246
+1247        // Set the segment size to this path's MTU for on-path data.
+1248        let pmtu = self.path_data(path_id).current_mtu().into();
+1249        let mut transmit = TransmitBuf::new(buf, max_datagrams, pmtu);
+1250
+1251        // Iterate over the available spaces.
+1252        for space_id in SpaceId::iter() {
+1253            // Only PathId::ZERO uses non Data space ids.
+1254            if path_id != PathId::ZERO && space_id != SpaceId::Data {
+1255                continue;
+1256            }
+1257            match self.poll_transmit_path_space(
+1258                now,
+1259                &mut transmit,
+1260                path_id,
+1261                space_id,
+1262                remote_cid,
+1263                have_available_path,
+1264                connection_close_pending,
+1265                pad_datagram,
+1266            ) {
+1267                PollPathSpaceStatus::NothingToSend {
+1268                    congestion_blocked: cb,
+1269                } => {
+1270                    congestion_blocked |= cb;
+1271                    // Continue checking other spaces, tail-loss probes may need to be sent
+1272                    // in all spaces.
+1273                }
+1274                PollPathSpaceStatus::WrotePacket {
+1275                    last_packet_number: pn,
+1276                    pad_datagram: pad,
+1277                } => {
+1278                    debug_assert!(!transmit.is_empty(), "transmit must contain packets");
+1279                    last_packet_number = Some(pn);
+1280                    pad_datagram = pad;
+1281                    // Always check higher spaces. If the transmit is full or they have
+1282                    // nothing to send they will not write packets. But if they can, they
+1283                    // must always be allowed to add to this transmit because coalescing may
+1284                    // be required.
+1285                    continue;
+1286                }
+1287                PollPathSpaceStatus::Send {
+1288                    last_packet_number: pn,
+1289                } => {
+1290                    debug_assert!(!transmit.is_empty(), "transmit must contain packets");
+1291                    last_packet_number = Some(pn);
+1292                    break;
+1293                }
+1294            }
+1295        }
+1296
+1297        if last_packet_number.is_some() || congestion_blocked {
+1298            self.qlog.emit_recovery_metrics(
+1299                path_id,
+1300                &mut self.paths.get_mut(&path_id).unwrap().data,
+1301                now,
+1302            );
+1303        }
+1304
+1305        match last_packet_number {
+1306            Some(last_packet_number) => {
+1307                // Note that when sending in multiple spaces the last packet number will be
+1308                // the one from the highest space.
+1309                self.path_data_mut(path_id).congestion.on_sent(
+1310                    now,
+1311                    transmit.len() as u64,
+1312                    last_packet_number,
+1313                );
+1314                PollPathStatus::Send(self.build_transmit(path_id, transmit))
+1315            }
+1316            None => PollPathStatus::NothingToSend { congestion_blocked },
+1317        }
+1318    }
+1319
+1320    /// poll_transmit logic for a QUIC-MULTIPATH packet number space (PathID + SpaceId).
+1321    #[must_use]
+1322    fn poll_transmit_path_space(
+1323        &mut self,
+1324        now: Instant,
+1325        transmit: &mut TransmitBuf<'_>,
+1326        path_id: PathId,
+1327        space_id: SpaceId,
+1328        remote_cid: ConnectionId,
+1329        // If any other packet space has a usable path with PathStatus::Available.
+1330        have_available_path: bool,
+1331        // If we need to send a CONNECTION_CLOSE frame.
+1332        connection_close_pending: bool,
+1333        // Whether the current datagram needs to be padded to a certain size.
+1334        mut pad_datagram: PadDatagram,
+1335    ) -> PollPathSpaceStatus {
+1336        // Keep track of the last packet number we wrote. If None we did not write any
+1337        // packets.
+1338        let mut last_packet_number = None;
+1339
+1340        // Each loop of this may build one packet. It works logically as follows:
+1341        //
+1342        // - Check if something *needs* to be sent in this space and *can* be sent.
+1343        //   - If not, return to the caller who will call us again for the next space.
+1344        // - Start a new datagram.
+1345        //   - Unless coalescing the packet into an existing datagram.
+1346        // - Write the packet header and payload.
+1347        // - Check if coalescing a next packet into the datagram is possible.
+1348        // - If coalescing, finish packet without padding to leave space in the datagram.
+1349        // - If not coalescing, complete the datagram:
+1350        //   - Finish packet with padding.
+1351        //   - Set the transmit segment size if this is the first datagram.
+1352        // - Loop: next iteration will exit the loop if nothing more to send in this
+1353        //   space. The TransmitBuf will contain a started datagram with space if
+1354        //   coalescing, or completely filled datagram if not coalescing.
+1355        loop {
+1356            // Determine if anything can be sent in this packet number space (SpaceId + PathId).
+1357            let max_packet_size = if transmit.datagram_remaining_mut() > 0 {
+1358                // A datagram is started already, we are coalescing another packet into it.
+1359                transmit.datagram_remaining_mut()
+1360            } else {
+1361                // A new datagram needs to be started.
+1362                transmit.segment_size()
+1363            };
+1364            let can_send =
+1365                self.space_can_send(space_id, path_id, max_packet_size, connection_close_pending);
+1366
+1367            // Whether we would like to send any frames on this packet space. See the packet
+1368            // scheduling described in poll_transmit.
+1369            let space_should_send = {
+1370                let path_exclusive_only = space_id == SpaceId::Data
+1371                    && have_available_path
+1372                    && self.path_data(path_id).local_status() == PathStatus::Backup;
+1373                let path_should_send = if path_exclusive_only {
+1374                    can_send.path_exclusive
+1375                } else {
+1376                    !can_send.is_empty()
+1377                };
+1378                let needs_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0;
+1379                path_should_send || needs_loss_probe
+1380            };
+1381
+1382            if !space_should_send {
+1383                // Nothing more to send. Previous iterations of this loop may have built
+1384                // packets already.
+1385                return match last_packet_number {
+1386                    Some(pn) => PollPathSpaceStatus::WrotePacket {
+1387                        last_packet_number: pn,
+1388                        pad_datagram,
+1389                    },
+1390                    None => {
+1391                        // Only log for spaces which have crypto.
+1392                        if self.spaces[space_id].crypto.is_some()
+1393                            || (space_id == SpaceId::Data && self.zero_rtt_crypto.is_some())
+1394                        {
+1395                            trace!(?space_id, %path_id, "nothing to send in space");
+1396                        }
+1397                        PollPathSpaceStatus::NothingToSend {
+1398                            congestion_blocked: false,
+1399                        }
+1400                    }
+1401                };
+1402            }
+1403
+1404            // We want to send on this space, check congestion control if we can. But only
+1405            // if we will need to start a new datagram. If we are coalescing into an already
+1406            // started datagram we do not need to check congestion control again.
+1407            if transmit.datagram_remaining_mut() == 0 {
+1408                let congestion_blocked =
+1409                    self.path_congestion_check(space_id, path_id, transmit, &can_send, now);
+1410                if congestion_blocked != PathBlocked::No {
+1411                    // Previous iterations of this loop may have built packets already.
+1412                    return match last_packet_number {
+1413                        Some(pn) => PollPathSpaceStatus::WrotePacket {
+1414                            last_packet_number: pn,
+1415                            pad_datagram,
+1416                        },
+1417                        None => {
+1418                            return PollPathSpaceStatus::NothingToSend {
+1419                                congestion_blocked: true,
+1420                            };
+1421                        }
+1422                    };
+1423                }
+1424            }
+1425
+1426            // If the datagram is full (or there never was one started), we need to start a
+1427            // new one.
+1428            if transmit.datagram_remaining_mut() == 0 {
+1429                if transmit.num_datagrams() >= transmit.max_datagrams().get() {
+1430                    // No more datagrams allowed.
+1431                    // Previous iterations of this loop may have built packets already.
+1432                    return match last_packet_number {
+1433                        Some(pn) => PollPathSpaceStatus::WrotePacket {
+1434                            last_packet_number: pn,
+1435                            pad_datagram,
+1436                        },
+1437                        None => {
+1438                            return PollPathSpaceStatus::NothingToSend {
+1439                                congestion_blocked: false,
+1440                            };
+1441                        }
+1442                    };
+1443                }
+1444
+1445                match self.spaces[space_id].for_path(path_id).loss_probes {
+1446                    0 => transmit.start_new_datagram(),
+1447                    _ => {
+1448                        // We need something to send for a tail-loss probe.
+1449                        let request_immediate_ack =
+1450                            space_id == SpaceId::Data && self.peer_supports_ack_frequency();
+1451                        self.spaces[space_id].maybe_queue_probe(
+1452                            path_id,
+1453                            request_immediate_ack,
+1454                            &self.streams,
+1455                        );
+1456
+1457                        self.spaces[space_id].for_path(path_id).loss_probes -= 1;
+1458
+1459                        // Clamp the datagram to at most the minimum MTU to ensure that loss
+1460                        // probes can get through and enable recovery even if the path MTU
+1461                        // has shrank unexpectedly.
+1462                        transmit.start_new_datagram_with_size(std::cmp::min(
+1463                            usize::from(INITIAL_MTU),
+1464                            transmit.segment_size(),
+1465                        ));
+1466                    }
+1467                }
+1468                trace!(count = transmit.num_datagrams(), "new datagram started");
+1469
+1470                // We started a new datagram, we decide later if it needs padding.
+1471                pad_datagram = PadDatagram::No;
+1472            }
+1473
+1474            // If coalescing another packet into the existing datagram, there should
+1475            // still be enough space for a whole packet.
+1476            if transmit.datagram_start_offset() < transmit.len() {
+1477                debug_assert!(transmit.datagram_remaining_mut() >= MIN_PACKET_SPACE);
+1478            }
+1479
+1480            //
+1481            // From here on, we've determined that a packet will definitely be sent.
+1482            //
+1483
+1484            if self.spaces[SpaceId::Initial].crypto.is_some()
+1485                && space_id == SpaceId::Handshake
+1486                && self.side.is_client()
+1487            {
+1488                // A client stops both sending and processing Initial packets when it
+1489                // sends its first Handshake packet.
+1490                self.discard_space(now, SpaceId::Initial);
+1491            }
+1492            if let Some(ref mut prev) = self.prev_crypto {
+1493                prev.update_unacked = false;
+1494            }
+1495
+1496            let Some(mut builder) = PacketBuilder::new(
+1497                now,
+1498                space_id,
+1499                path_id,
+1500                remote_cid,
+1501                transmit,
+1502                can_send.other,
+1503                self,
+1504            ) else {
+1505                // Confidentiality limit is exceeded and the connection has been killed. We
+1506                // should not send any other packets. This works in a roundabout way: We
+1507                // have started a datagram but not written anything into it. So even if we
+1508                // get called again for another space we will see an already started
+1509                // datagram and try and start another packet here. Then be stopped by the
+1510                // same confidentiality limit.
+1511                return PollPathSpaceStatus::NothingToSend {
+1512                    congestion_blocked: false,
+1513                };
+1514            };
+1515            last_packet_number = Some(builder.packet_number);
+1516
+1517            if space_id == SpaceId::Initial && (self.side.is_client() || can_send.other) {
+1518                // https://www.rfc-editor.org/rfc/rfc9000.html#section-14.1
+1519                pad_datagram |= PadDatagram::ToMinMtu;
+1520            }
+1521            if space_id == SpaceId::Data && self.config.pad_to_mtu {
+1522                pad_datagram |= PadDatagram::ToSegmentSize;
+1523            }
+1524
+1525            if can_send.close {
+1526                trace!("sending CONNECTION_CLOSE");
+1527                // Encode ACKs before the ConnectionClose message, to give the receiver
+1528                // a better approximate on what data has been processed. This is
+1529                // especially important with ack delay, since the peer might not
+1530                // have gotten any other ACK for the data earlier on.
+1531                let is_multipath_negotiated = self.is_multipath_negotiated();
+1532                for path_id in self.spaces[space_id]
+1533                    .number_spaces
+1534                    .iter()
+1535                    .filter(|(_, pns)| !pns.pending_acks.ranges().is_empty())
+1536                    .map(|(&path_id, _)| path_id)
+1537                    .collect::<Vec<_>>()
+1538                {
+1539                    Self::populate_acks(
+1540                        now,
+1541                        self.receiving_ecn,
+1542                        path_id,
+1543                        space_id,
+1544                        &mut self.spaces[space_id],
+1545                        is_multipath_negotiated,
+1546                        &mut builder,
+1547                        &mut self.stats.frame_tx,
+1548                    );
+1549                }
+1550
+1551                // Since there only 64 ACK frames there will always be enough space
+1552                // to encode the ConnectionClose frame too. However we still have the
+1553                // check here to prevent crashes if something changes.
+1554
+1555                // TODO(flub): This needs fixing for multipath, to ensure we can always
+1556                //    write the CONNECTION_CLOSE even if we have many PATH_ACKs to send:
+1557                //    https://github.com/n0-computer/quinn/issues/367.
+1558                debug_assert!(
+1559                    builder.frame_space_remaining() > frame::ConnectionClose::SIZE_BOUND,
+1560                    "ACKs should leave space for ConnectionClose"
+1561                );
+1562                let stats = &mut self.stats.frame_tx;
+1563                if frame::ConnectionClose::SIZE_BOUND < builder.frame_space_remaining() {
+1564                    let max_frame_size = builder.frame_space_remaining();
+1565                    let close: Close = match self.state.as_type() {
+1566                        StateType::Closed => {
+1567                            let reason: Close =
+1568                                self.state.as_closed().expect("checked").clone().into();
+1569                            if space_id == SpaceId::Data || reason.is_transport_layer() {
+1570                                reason
+1571                            } else {
+1572                                TransportError::APPLICATION_ERROR("").into()
+1573                            }
+1574                        }
+1575                        StateType::Draining => TransportError::NO_ERROR("").into(),
+1576                        _ => unreachable!(
+1577                            "tried to make a close packet when the connection wasn't closed"
+1578                        ),
+1579                    };
+1580                    builder.write_frame(close.encoder(max_frame_size), stats);
+1581                }
+1582                let last_pn = builder.packet_number;
+1583                builder.finish_and_track(now, self, path_id, pad_datagram);
+1584                if space_id == self.highest_space {
+1585                    // Don't send another close packet. Even with multipath we only send
+1586                    // CONNECTION_CLOSE on a single path since we expect our paths to work.
+1587                    self.connection_close_pending = false;
+1588                }
+1589                // Send a close frame in every possible space for robustness, per
+1590                // RFC9000 "Immediate Close during the Handshake". Don't bother trying
+1591                // to send anything else.
+1592                // TODO(flub): This breaks during the handshake if we can not coalesce
+1593                //    packets due to space reasons: the next space would either fail a
+1594                //    debug_assert checking for enough packet space or produce an invalid
+1595                //    packet. We need to keep track of per-space pending CONNECTION_CLOSE to
+1596                //    be able to send these across multiple calls to poll_transmit. Then
+1597                //    check for coalescing space here because initial packets need to be in
+1598                //    padded datagrams. And also add space checks for CONNECTION_CLOSE in
+1599                //    space_can_send so it would stop a GSO batch if the datagram is too
+1600                //    small for another CONNECTION_CLOSE packet.
+1601                return PollPathSpaceStatus::WrotePacket {
+1602                    last_packet_number: last_pn,
+1603                    pad_datagram,
+1604                };
+1605            }
+1606
+1607            // If this boolean is true we only want to send frames which can not be sent on
+1608            // any other path. See the path scheduling notes in Self::poll_transmit.
+1609            let path_exclusive_only =
+1610                have_available_path && self.path_data(path_id).local_status() == PathStatus::Backup;
+1611
+1612            self.populate_packet(now, space_id, path_id, path_exclusive_only, &mut builder);
+1613
+1614            // ACK-only packets should only be sent when explicitly allowed. If we write them due to
+1615            // any other reason, there is a bug which leads to one component announcing write
+1616            // readiness while not writing any data. This degrades performance. The condition is
+1617            // only checked if the full MTU is available and when potentially large fixed-size
+1618            // frames aren't queued, so that lack of space in the datagram isn't the reason for just
+1619            // writing ACKs.
+1620            debug_assert!(
+1621                !(builder.sent_frames().is_ack_only(&self.streams)
+1622                    && !can_send.acks
+1623                    && can_send.other
+1624                    && builder.buf.segment_size()
+1625                        == self.path_data(path_id).current_mtu() as usize
+1626                    && self.datagrams.outgoing.is_empty()),
+1627                "SendableFrames was {can_send:?}, but only ACKs have been written"
+1628            );
+1629            if builder.sent_frames().requires_padding {
+1630                pad_datagram |= PadDatagram::ToMinMtu;
+1631            }
+1632
+1633            for (path_id, _pn) in builder.sent_frames().largest_acked.iter() {
+1634                self.spaces[space_id]
+1635                    .for_path(*path_id)
+1636                    .pending_acks
+1637                    .acks_sent();
+1638                self.timers.stop(
+1639                    Timer::PerPath(*path_id, PathTimer::MaxAckDelay),
+1640                    self.qlog.with_time(now),
+1641                );
+1642            }
+1643
+1644            // Now we need to finish the packet.  Before we do so we need to know if we will
+1645            // be coalescing the next packet into this one, or will be ending the datagram
+1646            // as well.  Because if this is the last packet in the datagram more padding
+1647            // might be needed because of the packet type, or to fill the GSO segment size.
+1648
+1649            // Are we allowed to coalesce AND is there enough space for another *packet* in
+1650            // this datagram AND will we definitely send another packet?
+1651            if builder.can_coalesce && path_id == PathId::ZERO && {
+1652                let max_packet_size = builder
+1653                    .buf
+1654                    .datagram_remaining_mut()
+1655                    .saturating_sub(builder.predict_packet_end());
+1656                max_packet_size > MIN_PACKET_SPACE
+1657                    && self.has_pending_packet(space_id, max_packet_size, connection_close_pending)
+1658            } {
+1659                // We can append/coalesce the next packet into the current
+1660                // datagram. Finish the current packet without adding extra padding.
+1661                trace!("will coalesce with next packet");
+1662                builder.finish_and_track(now, self, path_id, PadDatagram::No);
+1663            } else {
+1664                // We need a new datagram for the next packet.  Finish the current
+1665                // packet with padding.
+1666                // TODO(flub): if there isn't any more data to be sent, this will still pad
+1667                //    to the segment size and only discover there is nothing to send before
+1668                //    starting the next packet. That is wasting up to 32 bytes.
+1669                if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) {
+1670                    // If too many padding bytes would be required to continue the
+1671                    // GSO batch after this packet, end the GSO batch here. Ensures
+1672                    // that fixed-size frames with heterogeneous sizes
+1673                    // (e.g. application datagrams) won't inadvertently waste large
+1674                    // amounts of bandwidth. The exact threshold is a bit arbitrary
+1675                    // and might benefit from further tuning, though there's no
+1676                    // universally optimal value.
+1677                    const MAX_PADDING: usize = 32;
+1678                    if builder.buf.datagram_remaining_mut()
+1679                        > builder.predict_packet_end() + MAX_PADDING
+1680                    {
+1681                        trace!(
+1682                            "GSO truncated by demand for {} padding bytes",
+1683                            builder.buf.datagram_remaining_mut() - builder.predict_packet_end()
+1684                        );
+1685                        let last_pn = builder.packet_number;
+1686                        builder.finish_and_track(now, self, path_id, PadDatagram::No);
+1687                        return PollPathSpaceStatus::Send {
+1688                            last_packet_number: last_pn,
+1689                        };
+1690                    }
+1691
+1692                    // Pad the current datagram to GSO segment size so it can be
+1693                    // included in the GSO batch.
+1694                    builder.finish_and_track(now, self, path_id, PadDatagram::ToSegmentSize);
+1695                } else {
+1696                    builder.finish_and_track(now, self, path_id, pad_datagram);
+1697                }
+1698
+1699                // If this is the first datagram we set the segment size to the size of the
+1700                // first datagram.
+1701                if transmit.num_datagrams() == 1 {
+1702                    transmit.clip_segment_size();
+1703                }
+1704            }
+1705        }
+1706    }
+1707
+1708    fn poll_transmit_mtu_probe(
+1709        &mut self,
+1710        now: Instant,
+1711        buf: &mut Vec<u8>,
+1712        path_id: PathId,
+1713    ) -> Option<Transmit> {
+1714        let (active_cid, probe_size) = self.get_mtu_probe_data(now, path_id)?;
+1715
+1716        // We are definitely sending a DPLPMTUD probe.
+1717        let mut transmit = TransmitBuf::new(buf, NonZeroUsize::MIN, probe_size as usize);
+1718        transmit.start_new_datagram_with_size(probe_size as usize);
+1719
+1720        let mut builder = PacketBuilder::new(
+1721            now,
+1722            SpaceId::Data,
+1723            path_id,
+1724            active_cid,
+1725            &mut transmit,
+1726            true,
+1727            self,
+1728        )?;
+1729
+1730        // We implement MTU probes as ping packets padded up to the probe size
+1731        trace!(?probe_size, "writing MTUD probe");
+1732        builder.write_frame(frame::Ping, &mut self.stats.frame_tx);
+1733
+1734        // If supported by the peer, we want no delays to the probe's ACK
+1735        if self.peer_supports_ack_frequency() {
+1736            builder.write_frame(frame::ImmediateAck, &mut self.stats.frame_tx);
+1737        }
+1738
+1739        builder.finish_and_track(now, self, path_id, PadDatagram::ToSize(probe_size));
+1740
+1741        self.path_stats
+1742            .entry(path_id)
+1743            .or_default()
+1744            .sent_plpmtud_probes += 1;
+1745
+1746        Some(self.build_transmit(path_id, transmit))
+1747    }
+1748
+1749    /// Returns the CID and probe size if a DPLPMTUD probe is needed.
+1750    ///
+1751    /// We MTU probe all paths for which all of the following is true:
+1752    /// - We have an active destination CID for the path.
+1753    /// - The remote address *and* path are validated.
+1754    /// - The path is not abandoned.
+1755    /// - The MTU Discovery subsystem wants to probe the path.
+1756    fn get_mtu_probe_data(&mut self, now: Instant, path_id: PathId) -> Option<(ConnectionId, u16)> {
+1757        let active_cid = self.remote_cids.get(&path_id).map(CidQueue::active)?;
+1758        let is_eligible = self.path_data(path_id).validated
+1759            && !self.path_data(path_id).is_validating_path()
+1760            && !self.abandoned_paths.contains(&path_id);
+1761
+1762        if !is_eligible {
+1763            return None;
+1764        }
+1765        let next_pn = self.spaces[SpaceId::Data]
+1766            .for_path(path_id)
+1767            .peek_tx_number();
+1768        let probe_size = self
+1769            .path_data_mut(path_id)
+1770            .mtud
+1771            .poll_transmit(now, next_pn)?;
+1772
+1773        Some((active_cid, probe_size))
+1774    }
+1775
+1776    /// Returns true if there is a further packet to send on [`PathId::ZERO`].
+1777    ///
+1778    /// In other words this is predicting whether the next call to
+1779    /// [`Connection::space_can_send`] issued will return some frames to be sent. Including
+1780    /// having to predict which packet number space it will be invoked with. This depends on
+1781    /// how both [`Connection::poll_transmit_path`] and
+1782    /// [`Connection::poll_transmit_path_space`] behave.
+1783    ///
+1784    /// This is needed to determine if packet coalescing can happen. Because the last packet
+1785    /// in a datagram may need to be padded and thus we must know if another packet will
+1786    /// follow or not.
+1787    ///
+1788    /// The next packet can be either in the same space, or in one of the following spaces
+1789    /// on the same path. Because a 0-RTT packet can be coalesced with a 1-RTT packet and
+1790    /// both are in the Data(PathId::ZERO) space. Previous spaces are not checked, because
+1791    /// packets are built from Initial to Handshake to Data spaces.
+1792    fn has_pending_packet(
+1793        &mut self,
+1794        current_space_id: SpaceId,
+1795        max_packet_size: usize,
+1796        connection_close_pending: bool,
+1797    ) -> bool {
+1798        let mut space_id = current_space_id;
+1799        loop {
+1800            let can_send = self.space_can_send(
+1801                space_id,
+1802                PathId::ZERO,
+1803                max_packet_size,
+1804                connection_close_pending,
+1805            );
+1806            if !can_send.is_empty() {
+1807                return true;
+1808            }
+1809            match space_id.next() {
+1810                Some(next_space_id) => space_id = next_space_id,
+1811                None => break,
+1812            }
+1813        }
+1814        false
+1815    }
+1816
+1817    /// Checks if creating a new datagram would be blocked by congestion control
+1818    fn path_congestion_check(
+1819        &mut self,
+1820        space_id: SpaceId,
+1821        path_id: PathId,
+1822        transmit: &TransmitBuf<'_>,
+1823        can_send: &SendableFrames,
+1824        now: Instant,
+1825    ) -> PathBlocked {
+1826        // Anti-amplification is only based on `total_sent`, which gets updated after
+1827        // the transmit is sent. Therefore we pass the amount of bytes for datagrams
+1828        // that are already created, as well as 1 byte for starting another datagram. If
+1829        // there is any anti-amplification budget left, we always allow a full MTU to be
+1830        // sent (see https://github.com/quinn-rs/quinn/issues/1082).
+1831        if self.side().is_server()
+1832            && self
+1833                .path_data(path_id)
+1834                .anti_amplification_blocked(transmit.len() as u64 + 1)
+1835        {
+1836            trace!(?space_id, %path_id, "blocked by anti-amplification");
+1837            return PathBlocked::AntiAmplification;
+1838        }
+1839
+1840        // Congestion control check.
+1841        // Tail loss probes must not be blocked by congestion, or a deadlock could arise.
+1842        let bytes_to_send = transmit.segment_size() as u64;
+1843        let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0;
+1844
+1845        if can_send.other && !need_loss_probe && !can_send.close {
+1846            let path = self.path_data(path_id);
+1847            if path.in_flight.bytes + bytes_to_send >= path.congestion.window() {
+1848                trace!(?space_id, %path_id, "blocked by congestion control");
+1849                return PathBlocked::Congestion;
+1850            }
+1851        }
+1852
+1853        // Pacing check.
+1854        if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) {
+1855            self.timers.set(
+1856                Timer::PerPath(path_id, PathTimer::Pacing),
+1857                delay,
+1858                self.qlog.with_time(now),
+1859            );
+1860            // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though
+1861            // they are not congestion controlled.
+1862            trace!(?space_id, %path_id, "blocked by pacing");
+1863            return PathBlocked::Pacing;
+1864        }
+1865
+1866        PathBlocked::No
+1867    }
+1868
+1869    /// Send PATH_CHALLENGE for a previous path if necessary
+1870    ///
+1871    /// QUIC-TRANSPORT section 9.3.3
+1872    /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding>
+1873    fn send_prev_path_challenge(
+1874        &mut self,
+1875        now: Instant,
+1876        buf: &mut Vec<u8>,
+1877        path_id: PathId,
+1878    ) -> Option<Transmit> {
+1879        let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?;
+1880        // TODO (matheus23): We could use !prev_path.is_validating() here instead to
+1881        // (possibly) also re-send challenges when they get lost.
+1882        if !prev_path.send_new_challenge {
+1883            return None;
+1884        };
+1885        prev_path.send_new_challenge = false;
+1886        let network_path = prev_path.network_path;
+1887        let token = self.rng.random();
+1888        let info = paths::SentChallengeInfo {
+1889            sent_instant: now,
+1890            network_path,
+1891        };
+1892        prev_path.challenges_sent.insert(token, info);
+1893        debug_assert_eq!(
+1894            self.highest_space,
+1895            SpaceId::Data,
+1896            "PATH_CHALLENGE queued without 1-RTT keys"
+1897        );
+1898        let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into());
+1899        buf.start_new_datagram();
+1900
+1901        // Use the previous CID to avoid linking the new path with the previous path. We
+1902        // don't bother accounting for possible retirement of that prev_cid because this is
+1903        // sent once, immediately after migration, when the CID is known to be valid. Even
+1904        // if a post-migration packet caused the CID to be retired, it's fair to pretend
+1905        // this is sent first.
+1906        let mut builder =
+1907            PacketBuilder::new(now, SpaceId::Data, path_id, *prev_cid, buf, false, self)?;
+1908        let challenge = frame::PathChallenge(token);
+1909        let stats = &mut self.stats.frame_tx;
+1910        builder.write_frame_with_log_msg(challenge, stats, Some("validating previous path"));
+1911
+1912        // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame
+1913        // to at least the smallest allowed maximum datagram size of 1200 bytes,
+1914        // unless the anti-amplification limit for the path does not permit
+1915        // sending a datagram of this size
+1916        builder.pad_to(MIN_INITIAL_SIZE);
+1917
+1918        builder.finish(self, now);
+1919        self.stats.udp_tx.on_sent(1, buf.len());
+1920        self.path_stats
+1921            .entry(path_id)
+1922            .or_default()
+1923            .udp_tx
+1924            .on_sent(1, buf.len());
+1925
+1926        Some(Transmit {
+1927            destination: network_path.remote,
+1928            size: buf.len(),
+1929            ecn: None,
+1930            segment_size: None,
+1931            src_ip: network_path.local_ip,
+1932        })
+1933    }
+1934
+1935    fn send_off_path_path_response(
+1936        &mut self,
+1937        now: Instant,
+1938        buf: &mut Vec<u8>,
+1939        path_id: PathId,
+1940    ) -> Option<Transmit> {
+1941        let path = self.paths.get_mut(&path_id).map(|state| &mut state.data)?;
+1942        let cid_queue = self.remote_cids.get_mut(&path_id)?;
+1943        let (token, network_path) = path.path_responses.pop_off_path(path.network_path)?;
+1944
+1945        let cid = cid_queue
+1946            .next_reserved()
+1947            .unwrap_or_else(|| cid_queue.active());
+1948        // TODO(@divma): we should take a different approach when there is no fresh CID to use.
+1949        // https://github.com/quinn-rs/quinn/issues/2184
+1950
+1951        let frame = frame::PathResponse(token);
+1952
+1953        let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into());
+1954        buf.start_new_datagram();
+1955
+1956        let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, cid, buf, false, self)?;
+1957        let stats = &mut self.stats.frame_tx;
+1958        builder.write_frame_with_log_msg(frame, stats, Some("(off-path)"));
+1959        builder.finish_and_track(now, self, path_id, PadDatagram::ToMinMtu);
+1960
+1961        let size = buf.len();
+1962
+1963        self.stats.udp_tx.on_sent(1, size);
+1964        self.path_stats
+1965            .entry(path_id)
+1966            .or_default()
+1967            .udp_tx
+1968            .on_sent(1, size);
+1969        Some(Transmit {
+1970            destination: network_path.remote,
+1971            size,
+1972            ecn: None,
+1973            segment_size: None,
+1974            src_ip: network_path.local_ip,
+1975        })
+1976    }
+1977
+1978    /// Indicate what types of frames are ready to send for the given space
+1979    ///
+1980    /// *packet_size* is the number of bytes available to build the next packet.
+1981    /// *connection_close_pending* indicates whether a CONNECTION_CLOSE frame needs to be
+1982    /// sent.
+1983    fn space_can_send(
+1984        &mut self,
+1985        space_id: SpaceId,
+1986        path_id: PathId,
+1987        packet_size: usize,
+1988        connection_close_pending: bool,
+1989    ) -> SendableFrames {
+1990        let space = &mut self.spaces[space_id];
+1991        let space_has_crypto = space.crypto.is_some();
+1992
+1993        if !space_has_crypto
+1994            && (space_id != SpaceId::Data
+1995                || self.zero_rtt_crypto.is_none()
+1996                || self.side.is_server())
+1997        {
+1998            // Nothing to send in this space
+1999            return SendableFrames::empty();
+2000        }
+2001
+2002        let mut can_send = space.can_send(path_id, &self.streams);
+2003
+2004        // Check for 1RTT space.
+2005        if space_id == SpaceId::Data {
+2006            let pn = space.for_path(path_id).peek_tx_number();
+2007            // Number of bytes available for frames if this is a 1-RTT packet. We're
+2008            // guaranteed to be able to send an individual frame at least this large in the
+2009            // next 1-RTT packet. This could be generalized to support every space, but it's
+2010            // only needed to handle large fixed-size frames, which only exist in 1-RTT
+2011            // (application datagrams).
+2012            let frame_space_1rtt =
+2013                packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id));
+2014            can_send |= self.can_send_1rtt(path_id, frame_space_1rtt);
+2015        }
+2016
+2017        can_send.close = connection_close_pending && space_has_crypto;
+2018
+2019        can_send
+2020    }
+2021
+2022    /// Process `ConnectionEvent`s generated by the associated `Endpoint`
+2023    ///
+2024    /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals
+2025    /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be
+2026    /// extracted through the relevant methods.
+2027    pub fn handle_event(&mut self, event: ConnectionEvent) {
+2028        use ConnectionEventInner::*;
+2029        match event.0 {
+2030            Datagram(DatagramConnectionEvent {
+2031                now,
+2032                network_path,
+2033                path_id,
+2034                ecn,
+2035                first_decode,
+2036                remaining,
+2037            }) => {
+2038                let span = trace_span!("pkt", %path_id);
+2039                let _guard = span.enter();
+2040
+2041                if self.update_network_path_or_discard(network_path, path_id) {
+2042                    // A return value of true indicates we should discard this packet.
+2043                    return;
+2044                }
+2045
+2046                let was_anti_amplification_blocked = self
+2047                    .path(path_id)
+2048                    .map(|path| path.anti_amplification_blocked(1))
+2049                    // We never tried to send on an non-existing (new) path so have not been
+2050                    // anti-amplification blocked for it previously.
+2051                    .unwrap_or(false);
+2052
+2053                self.stats.udp_rx.datagrams += 1;
+2054                self.stats.udp_rx.bytes += first_decode.len() as u64;
+2055                let rx = &mut self.path_stats.entry(path_id).or_default().udp_rx;
+2056                rx.datagrams += 1;
+2057                rx.bytes += first_decode.len() as u64;
+2058                let data_len = first_decode.len();
+2059
+2060                self.handle_decode(now, network_path, path_id, ecn, first_decode);
+2061                // The current `path` might have changed inside `handle_decode` since the packet
+2062                // could have triggered a migration. The packet might also belong to an unknown
+2063                // path and have been rejected. Make sure the data received is accounted for the
+2064                // most recent path by accessing `path` after `handle_decode`.
+2065                if let Some(path) = self.path_mut(path_id) {
+2066                    path.inc_total_recvd(data_len as u64);
+2067                }
+2068
+2069                if let Some(data) = remaining {
+2070                    self.stats.udp_rx.bytes += data.len() as u64;
+2071                    self.path_stats.entry(path_id).or_default().udp_rx.bytes += data.len() as u64;
+2072                    self.handle_coalesced(now, network_path, path_id, ecn, data);
+2073                }
+2074
+2075                if let Some(path) = self.paths.get_mut(&path_id) {
+2076                    self.qlog
+2077                        .emit_recovery_metrics(path_id, &mut path.data, now);
+2078                }
+2079
+2080                if was_anti_amplification_blocked {
+2081                    // A prior attempt to set the loss detection timer may have failed due to
+2082                    // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if
+2083                    // the server's first flight is lost.
+2084                    self.set_loss_detection_timer(now, path_id);
+2085                }
+2086            }
+2087            NewIdentifiers(ids, now, cid_len, cid_lifetime) => {
+2088                let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default();
+2089                debug_assert!(ids.iter().all(|issued| issued.path_id == path_id));
+2090                let cid_state = self
+2091                    .local_cid_state
+2092                    .entry(path_id)
+2093                    .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0));
+2094                cid_state.new_cids(&ids, now);
+2095
+2096                ids.into_iter().rev().for_each(|frame| {
+2097                    self.spaces[SpaceId::Data].pending.new_cids.push(frame);
+2098                });
+2099                // Always update Timer::PushNewCid
+2100                self.reset_cid_retirement(now);
+2101            }
+2102        }
+2103    }
+2104
+2105    /// Updates the network path for `path_id`.
+2106    ///
+2107    /// Returns true if a packet coming in for this `path_id` over given `network_path` should be discarded.
+2108    /// Returns false if the path was updated and the packet doesn't need to be discarded.
+2109    fn update_network_path_or_discard(&mut self, network_path: FourTuple, path_id: PathId) -> bool {
+2110        let remote_may_migrate = self.side.remote_may_migrate(&self.state);
+2111        let local_ip_may_migrate = self.side.is_client();
+2112        // If this packet could initiate a migration and we're a client or a server that
+2113        // forbids migration, drop the datagram. This could be relaxed to heuristically
+2114        // permit NAT-rebinding-like migration.
+2115        if let Some(known_path) = self.path_mut(path_id) {
+2116            if network_path.remote != known_path.network_path.remote && !remote_may_migrate {
+2117                trace!(
+2118                    %path_id,
+2119                    %network_path,
+2120                    %known_path.network_path,
+2121                    "discarding packet from unrecognized peer"
+2122                );
+2123                return true;
+2124            }
+2125
+2126            if known_path.network_path.local_ip.is_some()
+2127                && network_path.local_ip.is_some()
+2128                && known_path.network_path.local_ip != network_path.local_ip
+2129                && !local_ip_may_migrate
+2130            {
+2131                trace!(
+2132                    %path_id,
+2133                    %network_path,
+2134                    %known_path.network_path,
+2135                    "discarding packet sent to incorrect interface"
+2136                );
+2137                return true;
+2138            }
+2139            // If the datagram indicates that we've changed our local IP, we update it.
+2140            // This is alluded to in Section 5.2 of the Multipath RFC draft 18:
+2141            // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#name-using-multiple-paths-on-the
+2142            // > Client receives the packet, recognizes a path migration, updates the source address of path 2 to 192.0.2.1.
+2143            if let Some(local_ip) = network_path.local_ip {
+2144                if known_path
+2145                    .network_path
+2146                    .local_ip
+2147                    .is_some_and(|ip| ip != local_ip)
+2148                {
+2149                    debug!(
+2150                        %path_id,
+2151                        %network_path,
+2152                        %known_path.network_path,
+2153                        "path's local address seemingly migrated"
+2154                    );
+2155                }
+2156                // We update the address without path validation on the client side.
+2157                // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#section-5.1
+2158                // > Servers observing a 4-tuple change will perform path validation (see Section 9 of [QUIC-TRANSPORT]).
+2159                // This sounds like it's *only* the server endpoints that do this.
+2160                // TODO(matheus23): We should still consider doing a proper migration on the client side in the future.
+2161                // For now, this preserves the behavior of this code pre 4-tuple tracking.
+2162                known_path.network_path.local_ip = Some(local_ip);
+2163            }
+2164        }
+2165        false
+2166    }
+2167
+2168    /// Process timer expirations
+2169    ///
+2170    /// Executes protocol logic, potentially preparing signals (including application `Event`s,
+2171    /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant
+2172    /// methods.
+2173    ///
+2174    /// It is most efficient to call this immediately after the system clock reaches the latest
+2175    /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply
+2176    /// no-op and therefore are safe.
+2177    pub fn handle_timeout(&mut self, now: Instant) {
+2178        while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) {
+2179            // TODO(@divma): remove `at` when the unicorn is born
+2180            trace!(?timer, at=?now, "timeout");
+2181            match timer {
+2182                Timer::Conn(timer) => match timer {
+2183                    ConnTimer::Close => {
+2184                        self.state.move_to_drained(None);
+2185                        self.endpoint_events.push_back(EndpointEventInner::Drained);
+2186                    }
+2187                    ConnTimer::Idle => {
+2188                        self.kill(ConnectionError::TimedOut);
+2189                    }
+2190                    ConnTimer::KeepAlive => {
+2191                        trace!("sending keep-alive");
+2192                        self.ping();
+2193                    }
+2194                    ConnTimer::KeyDiscard => {
+2195                        self.zero_rtt_crypto = None;
+2196                        self.prev_crypto = None;
+2197                    }
+2198                    ConnTimer::PushNewCid => {
+2199                        while let Some((path_id, when)) = self.next_cid_retirement() {
+2200                            if when > now {
+2201                                break;
+2202                            }
+2203                            match self.local_cid_state.get_mut(&path_id) {
+2204                                None => error!(%path_id, "No local CID state for path"),
+2205                                Some(cid_state) => {
+2206                                    // Update `retire_prior_to` field in NEW_CONNECTION_ID frame
+2207                                    let num_new_cid = cid_state.on_cid_timeout().into();
+2208                                    if !self.state.is_closed() {
+2209                                        trace!(
+2210                                            "push a new CID to peer RETIRE_PRIOR_TO field {}",
+2211                                            cid_state.retire_prior_to()
+2212                                        );
+2213                                        self.endpoint_events.push_back(
+2214                                            EndpointEventInner::NeedIdentifiers(
+2215                                                path_id,
+2216                                                now,
+2217                                                num_new_cid,
+2218                                            ),
+2219                                        );
+2220                                    }
+2221                                }
+2222                            }
+2223                        }
+2224                    }
+2225                },
+2226                // TODO: add path_id as span somehow
+2227                Timer::PerPath(path_id, timer) => {
+2228                    let span = trace_span!("per-path timer fired", %path_id, ?timer);
+2229                    let _guard = span.enter();
+2230                    match timer {
+2231                        PathTimer::PathIdle => {
+2232                            if let Err(err) = self.close_path(
+2233                                now,
+2234                                path_id,
+2235                                TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(),
+2236                            ) {
+2237                                warn!(?err, "failed closing path");
+2238                            }
+2239                        }
+2240
+2241                        PathTimer::PathKeepAlive => {
+2242                            trace!("sending keep-alive on path");
+2243                            self.ping_path(path_id).ok();
+2244                        }
+2245                        PathTimer::LossDetection => {
+2246                            self.on_loss_detection_timeout(now, path_id);
+2247                            self.qlog.emit_recovery_metrics(
+2248                                path_id,
+2249                                &mut self.paths.get_mut(&path_id).unwrap().data,
+2250                                now,
+2251                            );
+2252                        }
+2253                        PathTimer::PathValidation => {
+2254                            let Some(path) = self.paths.get_mut(&path_id) else {
+2255                                continue;
+2256                            };
+2257                            self.timers.stop(
+2258                                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+2259                                self.qlog.with_time(now),
+2260                            );
+2261                            debug!("path validation failed");
+2262                            if let Some((_, prev)) = path.prev.take() {
+2263                                path.data = prev;
+2264                            }
+2265                            path.data.challenges_sent.clear();
+2266                            path.data.send_new_challenge = false;
+2267                        }
+2268                        PathTimer::PathChallengeLost => {
+2269                            let Some(path) = self.paths.get_mut(&path_id) else {
+2270                                continue;
+2271                            };
+2272                            trace!("path challenge deemed lost");
+2273                            path.data.send_new_challenge = true;
+2274                        }
+2275                        PathTimer::PathOpen => {
+2276                            let Some(path) = self.paths.get_mut(&path_id) else {
+2277                                continue;
+2278                            };
+2279                            path.data.challenges_sent.clear();
+2280                            path.data.send_new_challenge = false;
+2281                            self.timers.stop(
+2282                                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+2283                                self.qlog.with_time(now),
+2284                            );
+2285                            debug!("new path validation failed");
+2286                            if let Err(err) = self.close_path(
+2287                                now,
+2288                                path_id,
+2289                                TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(),
+2290                            ) {
+2291                                warn!(?err, "failed closing path");
+2292                            }
+2293
+2294                            self.events.push_back(Event::Path(PathEvent::LocallyClosed {
+2295                                id: path_id,
+2296                                error: PathError::ValidationFailed,
+2297                            }));
+2298                        }
+2299                        PathTimer::Pacing => trace!("pacing timer expired"),
+2300                        PathTimer::MaxAckDelay => {
+2301                            trace!("max ack delay reached");
+2302                            // This timer is only armed in the Data space
+2303                            self.spaces[SpaceId::Data]
+2304                                .for_path(path_id)
+2305                                .pending_acks
+2306                                .on_max_ack_delay_timeout()
+2307                        }
+2308                        PathTimer::DiscardPath => {
+2309                            // The path was abandoned and 3*PTO has expired since.  Clean up all
+2310                            // remaining state and install stateless reset token.
+2311                            self.timers.stop_per_path(path_id, self.qlog.with_time(now));
+2312                            if let Some(local_cid_state) = self.local_cid_state.remove(&path_id) {
+2313                                let (min_seq, max_seq) = local_cid_state.active_seq();
+2314                                for seq in min_seq..=max_seq {
+2315                                    self.endpoint_events.push_back(
+2316                                        EndpointEventInner::RetireConnectionId(
+2317                                            now, path_id, seq, false,
+2318                                        ),
+2319                                    );
+2320                                }
+2321                            }
+2322                            self.discard_path(path_id, now);
+2323                        }
+2324                    }
+2325                }
+2326            }
+2327        }
+2328    }
+2329
+2330    /// Close a connection immediately
+2331    ///
+2332    /// This does not ensure delivery of outstanding data. It is the application's responsibility to
+2333    /// call this only when all important communications have been completed, e.g. by calling
+2334    /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding
+2335    /// [`StreamEvent::Finished`] event.
+2336    ///
+2337    /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been
+2338    /// delivered. There may still be data from the peer that has not been received.
+2339    ///
+2340    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+2341    pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) {
+2342        self.close_inner(
+2343            now,
+2344            Close::Application(frame::ApplicationClose { error_code, reason }),
+2345        )
+2346    }
+2347
+2348    /// Close the connection immediately, initiated by an API call.
+2349    ///
+2350    /// This will not produce a [`ConnectionLost`] event propagated by the
+2351    /// [`Connection::poll`] call, because the API call already propagated the error to the
+2352    /// user.
+2353    ///
+2354    /// Not to be used when entering immediate close due to an internal state change based
+2355    /// on an event. See [`State::move_to_closed_local`] for details.
+2356    ///
+2357    /// This initiates immediate close from
+2358    /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2>, moving to the closed
+2359    /// state.
+2360    ///
+2361    /// [`ConnectionLost`]: crate::Event::ConnectionLost
+2362    /// [`Connection::poll`]: super::Connection::poll
+2363    fn close_inner(&mut self, now: Instant, reason: Close) {
+2364        let was_closed = self.state.is_closed();
+2365        if !was_closed {
+2366            self.close_common();
+2367            self.set_close_timer(now);
+2368            self.connection_close_pending = true;
+2369            self.state.move_to_closed_local(reason);
+2370        }
+2371    }
+2372
+2373    /// Control datagrams
+2374    pub fn datagrams(&mut self) -> Datagrams<'_> {
+2375        Datagrams { conn: self }
+2376    }
+2377
+2378    /// Returns connection statistics
+2379    pub fn stats(&mut self) -> ConnectionStats {
+2380        self.stats.clone()
+2381    }
+2382
+2383    /// Returns path statistics
+2384    pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> {
+2385        let path = self.paths.get(&path_id)?;
+2386        let stats = self.path_stats.entry(path_id).or_default();
+2387        stats.rtt = path.data.rtt.get();
+2388        stats.cwnd = path.data.congestion.window();
+2389        stats.current_mtu = path.data.mtud.current_mtu();
+2390        Some(*stats)
+2391    }
+2392
+2393    /// Ping the remote endpoint
+2394    ///
+2395    /// Causes an ACK-eliciting packet to be transmitted on the connection.
+2396    pub fn ping(&mut self) {
+2397        // TODO(flub): This is very brute-force: it pings *all* the paths.  Instead it would
+2398        //    be nice if we could only send a single packet for this.
+2399        for path_data in self.spaces[self.highest_space].number_spaces.values_mut() {
+2400            path_data.ping_pending = true;
+2401        }
+2402    }
+2403
+2404    /// Ping the remote endpoint over a specific path
+2405    ///
+2406    /// Causes an ACK-eliciting packet to be transmitted on the path.
+2407    pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> {
+2408        let path_data = self.spaces[self.highest_space]
+2409            .number_spaces
+2410            .get_mut(&path)
+2411            .ok_or(ClosedPath { _private: () })?;
+2412        path_data.ping_pending = true;
+2413        Ok(())
+2414    }
+2415
+2416    /// Update traffic keys spontaneously
+2417    ///
+2418    /// This can be useful for testing key updates, as they otherwise only happen infrequently.
+2419    pub fn force_key_update(&mut self) {
+2420        if !self.state.is_established() {
+2421            debug!("ignoring forced key update in illegal state");
+2422            return;
+2423        }
+2424        if self.prev_crypto.is_some() {
+2425            // We already just updated, or are currently updating, the keys. Concurrent key updates
+2426            // are illegal.
+2427            debug!("ignoring redundant forced key update");
+2428            return;
+2429        }
+2430        self.update_keys(None, false);
+2431    }
+2432
+2433    /// Get a session reference
+2434    pub fn crypto_session(&self) -> &dyn crypto::Session {
+2435        &*self.crypto
+2436    }
+2437
+2438    /// Whether the connection is in the process of being established
+2439    ///
+2440    /// If this returns `false`, the connection may be either established or closed, signaled by the
+2441    /// emission of a `Connected` or `ConnectionLost` message respectively.
+2442    pub fn is_handshaking(&self) -> bool {
+2443        self.state.is_handshake()
+2444    }
+2445
+2446    /// Whether the connection is closed
+2447    ///
+2448    /// Closed connections cannot transport any further data. A connection becomes closed when
+2449    /// either peer application intentionally closes it, or when either transport layer detects an
+2450    /// error such as a time-out or certificate validation failure.
+2451    ///
+2452    /// A `ConnectionLost` event is emitted with details when the connection becomes closed.
+2453    pub fn is_closed(&self) -> bool {
+2454        self.state.is_closed()
+2455    }
+2456
+2457    /// Whether there is no longer any need to keep the connection around
+2458    ///
+2459    /// Closed connections become drained after a brief timeout to absorb any remaining in-flight
+2460    /// packets from the peer. All drained connections have been closed.
+2461    pub fn is_drained(&self) -> bool {
+2462        self.state.is_drained()
+2463    }
+2464
+2465    /// For clients, if the peer accepted the 0-RTT data packets
+2466    ///
+2467    /// The value is meaningless until after the handshake completes.
+2468    pub fn accepted_0rtt(&self) -> bool {
+2469        self.accepted_0rtt
+2470    }
+2471
+2472    /// Whether 0-RTT is/was possible during the handshake
+2473    pub fn has_0rtt(&self) -> bool {
+2474        self.zero_rtt_enabled
+2475    }
+2476
+2477    /// Whether there are any pending retransmits
+2478    pub fn has_pending_retransmits(&self) -> bool {
+2479        !self.spaces[SpaceId::Data].pending.is_empty(&self.streams)
+2480    }
+2481
+2482    /// Look up whether we're the client or server of this Connection
+2483    pub fn side(&self) -> Side {
+2484        self.side.side()
+2485    }
+2486
+2487    /// Get the address observed by the remote over the given path
+2488    pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> {
+2489        self.path(path_id)
+2490            .map(|path_data| {
+2491                path_data
+2492                    .last_observed_addr_report
+2493                    .as_ref()
+2494                    .map(|observed| observed.socket_addr())
+2495            })
+2496            .ok_or(ClosedPath { _private: () })
+2497    }
+2498
+2499    /// Current best estimate of this connection's latency (round-trip-time)
+2500    pub fn rtt(&self, path_id: PathId) -> Option<Duration> {
+2501        self.path(path_id).map(|d| d.rtt.get())
+2502    }
+2503
+2504    /// Current state of this connection's congestion controller, for debugging purposes
+2505    pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> {
+2506        self.path(path_id).map(|d| d.congestion.as_ref())
+2507    }
+2508
+2509    /// Modify the number of remotely initiated streams that may be concurrently open
+2510    ///
+2511    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+2512    /// `count`s increase both minimum and worst-case memory consumption.
+2513    pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) {
+2514        self.streams.set_max_concurrent(dir, count);
+2515        // If the limit was reduced, then a flow control update previously deemed insignificant may
+2516        // now be significant.
+2517        let pending = &mut self.spaces[SpaceId::Data].pending;
+2518        self.streams.queue_max_stream_id(pending);
+2519    }
+2520
+2521    /// Modify the number of open paths allowed when multipath is enabled
+2522    ///
+2523    /// When reducing the number of concurrent paths this will only affect delaying sending
+2524    /// new MAX_PATH_ID frames until fewer than this number of paths are possible.  To
+2525    /// actively reduce paths they must be closed using [`Connection::close_path`], which
+2526    /// can also be used to close not-yet-opened paths.
+2527    ///
+2528    /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable
+2529    /// multipath and will fail.
+2530    pub fn set_max_concurrent_paths(
+2531        &mut self,
+2532        now: Instant,
+2533        count: NonZeroU32,
+2534    ) -> Result<(), MultipathNotNegotiated> {
+2535        if !self.is_multipath_negotiated() {
+2536            return Err(MultipathNotNegotiated { _private: () });
+2537        }
+2538        self.max_concurrent_paths = count;
+2539
+2540        let in_use_count = self
+2541            .local_max_path_id
+2542            .next()
+2543            .saturating_sub(self.abandoned_paths.len() as u32)
+2544            .as_u32();
+2545        let extra_needed = count.get().saturating_sub(in_use_count);
+2546        let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed);
+2547
+2548        self.set_max_path_id(now, new_max_path_id);
+2549
+2550        Ok(())
+2551    }
+2552
+2553    /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths
+2554    fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) {
+2555        if max_path_id <= self.local_max_path_id {
+2556            return;
+2557        }
+2558
+2559        self.local_max_path_id = max_path_id;
+2560        self.spaces[SpaceId::Data].pending.max_path_id = true;
+2561
+2562        self.issue_first_path_cids(now);
+2563    }
+2564
+2565    /// Current number of remotely initiated streams that may be concurrently open
+2566    ///
+2567    /// If the target for this limit is reduced using [`set_max_concurrent_streams`](Self::set_max_concurrent_streams),
+2568    /// it will not change immediately, even if fewer streams are open. Instead, it will
+2569    /// decrement by one for each time a remotely initiated stream of matching directionality is closed.
+2570    pub fn max_concurrent_streams(&self, dir: Dir) -> u64 {
+2571        self.streams.max_concurrent(dir)
+2572    }
+2573
+2574    /// See [`TransportConfig::send_window()`]
+2575    pub fn set_send_window(&mut self, send_window: u64) {
+2576        self.streams.set_send_window(send_window);
+2577    }
+2578
+2579    /// See [`TransportConfig::receive_window()`]
+2580    pub fn set_receive_window(&mut self, receive_window: VarInt) {
+2581        if self.streams.set_receive_window(receive_window) {
+2582            self.spaces[SpaceId::Data].pending.max_data = true;
+2583        }
+2584    }
+2585
+2586    /// Whether the Multipath for QUIC extension is enabled.
+2587    ///
+2588    /// Multipath is only enabled after the handshake is completed and if it was enabled by both
+2589    /// peers.
+2590    pub fn is_multipath_negotiated(&self) -> bool {
+2591        !self.is_handshaking()
+2592            && self.config.max_concurrent_multipath_paths.is_some()
+2593            && self.peer_params.initial_max_path_id.is_some()
+2594    }
+2595
+2596    fn on_ack_received(
+2597        &mut self,
+2598        now: Instant,
+2599        space: SpaceId,
+2600        ack: frame::Ack,
+2601    ) -> Result<(), TransportError> {
+2602        // All ACKs are referencing path 0
+2603        let path = PathId::ZERO;
+2604        self.inner_on_ack_received(now, space, path, ack)
+2605    }
+2606
+2607    fn on_path_ack_received(
+2608        &mut self,
+2609        now: Instant,
+2610        space: SpaceId,
+2611        path_ack: frame::PathAck,
+2612    ) -> Result<(), TransportError> {
+2613        let (ack, path) = path_ack.into_ack();
+2614        self.inner_on_ack_received(now, space, path, ack)
+2615    }
+2616
+2617    /// Handles an ACK frame acknowledging packets sent on *path*.
+2618    fn inner_on_ack_received(
+2619        &mut self,
+2620        now: Instant,
+2621        space: SpaceId,
+2622        path: PathId,
+2623        ack: frame::Ack,
+2624    ) -> Result<(), TransportError> {
+2625        if self.abandoned_paths.contains(&path) {
+2626            // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.4.3-3
+2627            // > PATH_ACK frames received with an abandoned path ID are silently ignored, as specified in Section 4.
+2628            trace!("silently ignoring PATH_ACK on abandoned path");
+2629            return Ok(());
+2630        }
+2631        if ack.largest >= self.spaces[space].for_path(path).next_packet_number {
+2632            return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked"));
+2633        }
+2634        let new_largest = {
+2635            let space = &mut self.spaces[space].for_path(path);
+2636            if space.largest_acked_packet.is_none_or(|pn| ack.largest > pn) {
+2637                space.largest_acked_packet = Some(ack.largest);
+2638                if let Some(info) = space.sent_packets.get(ack.largest) {
+2639                    // This should always succeed, but a misbehaving peer might ACK a packet we
+2640                    // haven't sent. At worst, that will result in us spuriously reducing the
+2641                    // congestion window.
+2642                    space.largest_acked_packet_sent = info.time_sent;
+2643                }
+2644                true
+2645            } else {
+2646                false
+2647            }
+2648        };
+2649
+2650        if self.detect_spurious_loss(&ack, space, path) {
+2651            self.path_data_mut(path)
+2652                .congestion
+2653                .on_spurious_congestion_event();
+2654        }
+2655
+2656        // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks.
+2657        let mut newly_acked = ArrayRangeSet::new();
+2658        for range in ack.iter() {
+2659            self.spaces[space].for_path(path).check_ack(range.clone())?;
+2660            for (pn, _) in self.spaces[space]
+2661                .for_path(path)
+2662                .sent_packets
+2663                .iter_range(range)
+2664            {
+2665                newly_acked.insert_one(pn);
+2666            }
+2667        }
+2668
+2669        if newly_acked.is_empty() {
+2670            return Ok(());
+2671        }
+2672
+2673        let mut ack_eliciting_acked = false;
+2674        for packet in newly_acked.elts() {
+2675            if let Some(info) = self.spaces[space].for_path(path).take(packet) {
+2676                for (acked_path_id, acked_pn) in info.largest_acked.iter() {
+2677                    // Assume ACKs for all packets below the largest acknowledged in
+2678                    // `packet` have been received. This can cause the peer to spuriously
+2679                    // retransmit if some of our earlier ACKs were lost, but allows for
+2680                    // simpler state tracking. See discussion at
+2681                    // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking
+2682                    if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) {
+2683                        pns.pending_acks.subtract_below(*acked_pn);
+2684                    }
+2685                }
+2686                ack_eliciting_acked |= info.ack_eliciting;
+2687
+2688                // Notify MTU discovery that a packet was acked, because it might be an MTU probe
+2689                let path_data = self.path_data_mut(path);
+2690                let mtu_updated = path_data.mtud.on_acked(space, packet, info.size);
+2691                if mtu_updated {
+2692                    path_data
+2693                        .congestion
+2694                        .on_mtu_update(path_data.mtud.current_mtu());
+2695                }
+2696
+2697                // Notify ack frequency that a packet was acked, because it might contain an ACK_FREQUENCY frame
+2698                self.ack_frequency.on_acked(path, packet);
+2699
+2700                self.on_packet_acked(now, path, info);
+2701            }
+2702        }
+2703
+2704        let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet;
+2705        let app_limited = self.app_limited;
+2706        let path_data = self.path_data_mut(path);
+2707        let in_flight = path_data.in_flight.bytes;
+2708
+2709        path_data
+2710            .congestion
+2711            .on_end_acks(now, in_flight, app_limited, largest_ackd);
+2712
+2713        if new_largest && ack_eliciting_acked {
+2714            let ack_delay = if space != SpaceId::Data {
+2715                Duration::from_micros(0)
+2716            } else {
+2717                cmp::min(
+2718                    self.ack_frequency.peer_max_ack_delay,
+2719                    Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0),
+2720                )
+2721            };
+2722            let rtt = now.saturating_duration_since(
+2723                self.spaces[space].for_path(path).largest_acked_packet_sent,
+2724            );
+2725
+2726            let next_pn = self.spaces[space].for_path(path).next_packet_number;
+2727            let path_data = self.path_data_mut(path);
+2728            // TODO(@divma): should be a method of path, should be contained in a single place
+2729            path_data.rtt.update(ack_delay, rtt);
+2730            if path_data.first_packet_after_rtt_sample.is_none() {
+2731                path_data.first_packet_after_rtt_sample = Some((space, next_pn));
+2732            }
+2733        }
+2734
+2735        // Must be called before crypto/pto_count are clobbered
+2736        self.detect_lost_packets(now, space, path, true);
+2737
+2738        if self.peer_completed_address_validation(path) {
+2739            self.path_data_mut(path).pto_count = 0;
+2740        }
+2741
+2742        // Explicit congestion notification
+2743        // TODO(@divma): this code is a good example of logic that should be contained in a single
+2744        // place but it's split between the path data and the packet number space data, we should
+2745        // find a way to make this work without two lookups
+2746        if self.path_data(path).sending_ecn {
+2747            if let Some(ecn) = ack.ecn {
+2748                // We only examine ECN counters from ACKs that we are certain we received in transmit
+2749                // order, allowing us to compute an increase in ECN counts to compare against the number
+2750                // of newly acked packets that remains well-defined in the presence of arbitrary packet
+2751                // reordering.
+2752                if new_largest {
+2753                    let sent = self.spaces[space].for_path(path).largest_acked_packet_sent;
+2754                    self.process_ecn(now, space, path, newly_acked.len() as u64, ecn, sent);
+2755                }
+2756            } else {
+2757                // We always start out sending ECN, so any ack that doesn't acknowledge it disables it.
+2758                debug!("ECN not acknowledged by peer");
+2759                self.path_data_mut(path).sending_ecn = false;
+2760            }
+2761        }
+2762
+2763        self.set_loss_detection_timer(now, path);
+2764        Ok(())
+2765    }
+2766
+2767    fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool {
+2768        let lost_packets = &mut self.spaces[space].for_path(path).lost_packets;
+2769
+2770        if lost_packets.is_empty() {
+2771            return false;
+2772        }
+2773
+2774        for range in ack.iter() {
+2775            let spurious_losses: Vec<u64> = lost_packets
+2776                .iter_range(range.clone())
+2777                .map(|(pn, _info)| pn)
+2778                .collect();
+2779
+2780            for pn in spurious_losses {
+2781                lost_packets.remove(pn);
+2782            }
+2783        }
+2784
+2785        // If this ACK frame acknowledged all deemed lost packets,
+2786        // then we have raised a spurious congestion event in the past.
+2787        // We cannot conclude when there are remaining packets,
+2788        // but future ACK frames might indicate a spurious loss detection.
+2789        lost_packets.is_empty()
+2790    }
+2791
+2792    /// Drain lost packets that we reasonably think will never arrive
+2793    ///
+2794    /// The current criterion is copied from `msquic`:
+2795    /// discard packets that were sent earlier than 2 probe timeouts ago.
+2796    fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) {
+2797        let two_pto = 2 * self.path_data(path).rtt.pto_base();
+2798
+2799        let lost_packets = &mut self.spaces[space].for_path(path).lost_packets;
+2800        lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto);
+2801    }
+2802
+2803    /// Process a new ECN block from an in-order ACK
+2804    fn process_ecn(
+2805        &mut self,
+2806        now: Instant,
+2807        space: SpaceId,
+2808        path: PathId,
+2809        newly_acked: u64,
+2810        ecn: frame::EcnCounts,
+2811        largest_sent_time: Instant,
+2812    ) {
+2813        match self.spaces[space]
+2814            .for_path(path)
+2815            .detect_ecn(newly_acked, ecn)
+2816        {
+2817            Err(e) => {
+2818                debug!("halting ECN due to verification failure: {}", e);
+2819
+2820                self.path_data_mut(path).sending_ecn = false;
+2821                // Wipe out the existing value because it might be garbage and could interfere with
+2822                // future attempts to use ECN on new paths.
+2823                self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO;
+2824            }
+2825            Ok(false) => {}
+2826            Ok(true) => {
+2827                self.path_stats.entry(path).or_default().congestion_events += 1;
+2828                self.path_data_mut(path).congestion.on_congestion_event(
+2829                    now,
+2830                    largest_sent_time,
+2831                    false,
+2832                    true,
+2833                    0,
+2834                );
+2835            }
+2836        }
+2837    }
+2838
+2839    // Not timing-aware, so it's safe to call this for inferred acks, such as arise from
+2840    // high-latency handshakes
+2841    fn on_packet_acked(&mut self, now: Instant, path_id: PathId, info: SentPacket) {
+2842        self.paths
+2843            .get_mut(&path_id)
+2844            .expect("known path")
+2845            .remove_in_flight(&info);
+2846        let app_limited = self.app_limited;
+2847        let path = self.path_data_mut(path_id);
+2848        if info.ack_eliciting && !path.is_validating_path() {
+2849            // Only pass ACKs to the congestion controller if we are not validating the current
+2850            // path, so as to ignore any ACKs from older paths still coming in.
+2851            let rtt = path.rtt;
+2852            path.congestion
+2853                .on_ack(now, info.time_sent, info.size.into(), app_limited, &rtt);
+2854        }
+2855
+2856        // Update state for confirmed delivery of frames
+2857        if let Some(retransmits) = info.retransmits.get() {
+2858            for (id, _) in retransmits.reset_stream.iter() {
+2859                self.streams.reset_acked(*id);
+2860            }
+2861        }
+2862
+2863        for frame in info.stream_frames {
+2864            self.streams.received_ack_of(frame);
+2865        }
+2866    }
+2867
+2868    fn set_key_discard_timer(&mut self, now: Instant, space: SpaceId) {
+2869        let start = if self.zero_rtt_crypto.is_some() {
+2870            now
+2871        } else {
+2872            self.prev_crypto
+2873                .as_ref()
+2874                .expect("no previous keys")
+2875                .end_packet
+2876                .as_ref()
+2877                .expect("update not acknowledged yet")
+2878                .1
+2879        };
+2880
+2881        // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO of all paths.
+2882        self.timers.set(
+2883            Timer::Conn(ConnTimer::KeyDiscard),
+2884            start + self.max_pto_all_paths(space) * 3,
+2885            self.qlog.with_time(now),
+2886        );
+2887    }
+2888
+2889    /// Handle a [`PathTimer::LossDetection`] timeout.
+2890    ///
+2891    /// This timer expires for two reasons:
+2892    /// - An ACK-eliciting packet we sent should be considered lost.
+2893    /// - The PTO may have expired and a tail-loss probe needs to be scheduled.
+2894    ///
+2895    /// The former needs us to schedule re-transmission of the lost data.
+2896    ///
+2897    /// The latter means we have not received an ACK for an ack-eliciting packet we sent
+2898    /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting
+2899    /// packet, to try and elicit new acknowledgements. These new acknowledgements will
+2900    /// indicate whether the previously sent packets were lost or not.
+2901    fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) {
+2902        if let Some((_, pn_space)) = self.loss_time_and_space(path_id) {
+2903            // Time threshold loss Detection
+2904            self.detect_lost_packets(now, pn_space, path_id, false);
+2905            self.set_loss_detection_timer(now, path_id);
+2906            return;
+2907        }
+2908
+2909        let (_, space) = match self.pto_time_and_space(now, path_id) {
+2910            Some(x) => x,
+2911            None => {
+2912                error!(%path_id, "PTO expired while unset");
+2913                return;
+2914            }
+2915        };
+2916        trace!(
+2917            in_flight = self.path_data(path_id).in_flight.bytes,
+2918            count = self.path_data(path_id).pto_count,
+2919            ?space,
+2920            %path_id,
+2921            "PTO fired"
+2922        );
+2923
+2924        let count = match self.path_data(path_id).in_flight.ack_eliciting {
+2925            // A PTO when we're not expecting any ACKs must be due to handshake anti-amplification
+2926            // deadlock preventions
+2927            0 => {
+2928                debug_assert!(!self.peer_completed_address_validation(path_id));
+2929                1
+2930            }
+2931            // Conventional loss probe
+2932            _ => 2,
+2933        };
+2934        let pns = self.spaces[space].for_path(path_id);
+2935        pns.loss_probes = pns.loss_probes.saturating_add(count);
+2936        let path_data = self.path_data_mut(path_id);
+2937        path_data.pto_count = path_data.pto_count.saturating_add(1);
+2938        self.set_loss_detection_timer(now, path_id);
+2939    }
+2940
+2941    /// Detect any lost packets
+2942    ///
+2943    /// There are two cases in which we detects lost packets:
+2944    ///
+2945    /// - We received an ACK packet.
+2946    /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet
+2947    ///   that was followed by an acknowledged packet. The loss timer for this
+2948    ///   un-acknowledged packet expired and we need to detect that packet as lost.
+2949    ///
+2950    /// Packets are lost if they are both (See RFC9002 §6.1):
+2951    ///
+2952    /// - Unacknowledged, in flight and sent prior to an acknowledged packet.
+2953    /// - Old enough by either:
+2954    ///   - Having a packet number [`TransportConfig::packet_threshold`] lower then the last
+2955    ///     acknowledged packet.
+2956    ///   - Being sent [`TransportConfig::time_threshold`] * RTT in the past.
+2957    fn detect_lost_packets(
+2958        &mut self,
+2959        now: Instant,
+2960        pn_space: SpaceId,
+2961        path_id: PathId,
+2962        due_to_ack: bool,
+2963    ) {
+2964        let mut lost_packets = Vec::<u64>::new();
+2965        let mut lost_mtu_probe = None;
+2966        let mut in_persistent_congestion = false;
+2967        let mut size_of_lost_packets = 0u64;
+2968        self.spaces[pn_space].for_path(path_id).loss_time = None;
+2969
+2970        // Find all the lost packets, populating all variables initialised above.
+2971
+2972        let path = self.path_data(path_id);
+2973        let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe();
+2974        let loss_delay = path
+2975            .rtt
+2976            .conservative()
+2977            .mul_f32(self.config.time_threshold)
+2978            .max(TIMER_GRANULARITY);
+2979        let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample;
+2980
+2981        let largest_acked_packet = self.spaces[pn_space]
+2982            .for_path(path_id)
+2983            .largest_acked_packet
+2984            .expect("detect_lost_packets only to be called if path received at least one ACK");
+2985        let packet_threshold = self.config.packet_threshold as u64;
+2986
+2987        // InPersistentCongestion: Determine if all packets in the time period before the newest
+2988        // lost packet, including the edges, are marked lost. PTO computation must always
+2989        // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1).
+2990        let congestion_period = self
+2991            .pto(SpaceId::Data, path_id)
+2992            .saturating_mul(self.config.persistent_congestion_threshold);
+2993        let mut persistent_congestion_start: Option<Instant> = None;
+2994        let mut prev_packet = None;
+2995        let space = self.spaces[pn_space].for_path(path_id);
+2996
+2997        for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet) {
+2998            if prev_packet != Some(packet.wrapping_sub(1)) {
+2999                // An intervening packet was acknowledged
+3000                persistent_congestion_start = None;
+3001            }
+3002
+3003            // Packets sent before now - loss_delay are deemed lost.
+3004            // However, we avoid subtraction as it can panic and there's no
+3005            // saturating equivalent of this subtraction operation with a Duration.
+3006            let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay;
+3007            if packet_too_old || largest_acked_packet >= packet + packet_threshold {
+3008                // The packet should be declared lost.
+3009                if Some(packet) == in_flight_mtu_probe {
+3010                    // Lost MTU probes are not included in `lost_packets`, because they
+3011                    // should not trigger a congestion control response
+3012                    lost_mtu_probe = in_flight_mtu_probe;
+3013                } else {
+3014                    lost_packets.push(packet);
+3015                    size_of_lost_packets += info.size as u64;
+3016                    if info.ack_eliciting && due_to_ack {
+3017                        match persistent_congestion_start {
+3018                            // Two ACK-eliciting packets lost more than
+3019                            // congestion_period apart, with no ACKed packets in between
+3020                            Some(start) if info.time_sent - start > congestion_period => {
+3021                                in_persistent_congestion = true;
+3022                            }
+3023                            // Persistent congestion must start after the first RTT sample
+3024                            None if first_packet_after_rtt_sample
+3025                                .is_some_and(|x| x < (pn_space, packet)) =>
+3026                            {
+3027                                persistent_congestion_start = Some(info.time_sent);
+3028                            }
+3029                            _ => {}
+3030                        }
+3031                    }
+3032                }
+3033            } else {
+3034                // The packet should not yet be declared lost.
+3035                if space.loss_time.is_none() {
+3036                    // Since we iterate in order the lowest packet number's loss time will
+3037                    // always be the earliest.
+3038                    space.loss_time = Some(info.time_sent + loss_delay);
+3039                }
+3040                persistent_congestion_start = None;
+3041            }
+3042
+3043            prev_packet = Some(packet);
+3044        }
+3045
+3046        self.handle_lost_packets(
+3047            pn_space,
+3048            path_id,
+3049            now,
+3050            lost_packets,
+3051            lost_mtu_probe,
+3052            loss_delay,
+3053            in_persistent_congestion,
+3054            size_of_lost_packets,
+3055        );
+3056    }
+3057
+3058    /// Drops the path state, declaring any remaining in-flight packets as lost
+3059    fn discard_path(&mut self, path_id: PathId, now: Instant) {
+3060        trace!(%path_id, "dropping path state");
+3061        let path = self.path_data(path_id);
+3062        let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe();
+3063
+3064        let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes;
+3065        let lost_pns: Vec<_> = self.spaces[SpaceId::Data]
+3066            .for_path(path_id)
+3067            .sent_packets
+3068            .iter()
+3069            .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe)
+3070            .map(|(pn, info)| {
+3071                size_of_lost_packets += info.size as u64;
+3072                pn
+3073            })
+3074            .collect();
+3075
+3076        if !lost_pns.is_empty() {
+3077            trace!(
+3078                %path_id,
+3079                count = lost_pns.len(),
+3080                lost_bytes = size_of_lost_packets,
+3081                "packets lost on path abandon"
+3082            );
+3083            self.handle_lost_packets(
+3084                SpaceId::Data,
+3085                path_id,
+3086                now,
+3087                lost_pns,
+3088                in_flight_mtu_probe,
+3089                Duration::ZERO,
+3090                false,
+3091                size_of_lost_packets,
+3092            );
+3093        }
+3094        self.paths.remove(&path_id);
+3095        self.spaces[SpaceId::Data].number_spaces.remove(&path_id);
+3096
+3097        let path_stats = self.path_stats.remove(&path_id).unwrap_or_default();
+3098        self.events.push_back(
+3099            PathEvent::Abandoned {
+3100                id: path_id,
+3101                path_stats,
+3102            }
+3103            .into(),
+3104        );
+3105    }
+3106
+3107    fn handle_lost_packets(
+3108        &mut self,
+3109        pn_space: SpaceId,
+3110        path_id: PathId,
+3111        now: Instant,
+3112        lost_packets: Vec<u64>,
+3113        lost_mtu_probe: Option<u64>,
+3114        loss_delay: Duration,
+3115        in_persistent_congestion: bool,
+3116        size_of_lost_packets: u64,
+3117    ) {
+3118        debug_assert!(
+3119            {
+3120                let mut sorted = lost_packets.clone();
+3121                sorted.sort();
+3122                sorted == lost_packets
+3123            },
+3124            "lost_packets must be sorted"
+3125        );
+3126
+3127        self.drain_lost_packets(now, pn_space, path_id);
+3128
+3129        // OnPacketsLost
+3130        if let Some(largest_lost) = lost_packets.last().cloned() {
+3131            let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes;
+3132            let largest_lost_sent = self.spaces[pn_space]
+3133                .for_path(path_id)
+3134                .sent_packets
+3135                .get(largest_lost)
+3136                .unwrap()
+3137                .time_sent;
+3138            let path_stats = self.path_stats.entry(path_id).or_default();
+3139            path_stats.lost_packets += lost_packets.len() as u64;
+3140            path_stats.lost_bytes += size_of_lost_packets;
+3141            trace!(
+3142                %path_id,
+3143                count = lost_packets.len(),
+3144                lost_bytes = size_of_lost_packets,
+3145                "packets lost",
+3146            );
+3147
+3148            for &packet in &lost_packets {
+3149                let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else {
+3150                    continue;
+3151                };
+3152                self.qlog
+3153                    .emit_packet_lost(packet, &info, loss_delay, pn_space, now);
+3154                self.paths
+3155                    .get_mut(&path_id)
+3156                    .unwrap()
+3157                    .remove_in_flight(&info);
+3158
+3159                for frame in info.stream_frames {
+3160                    self.streams.retransmit(frame);
+3161                }
+3162                self.spaces[pn_space].pending |= info.retransmits;
+3163                self.path_data_mut(path_id)
+3164                    .mtud
+3165                    .on_non_probe_lost(packet, info.size);
+3166
+3167                self.spaces[pn_space].for_path(path_id).lost_packets.insert(
+3168                    packet,
+3169                    LostPacket {
+3170                        time_sent: info.time_sent,
+3171                    },
+3172                );
+3173            }
+3174
+3175            let path = self.path_data_mut(path_id);
+3176            if path.mtud.black_hole_detected(now) {
+3177                path.congestion.on_mtu_update(path.mtud.current_mtu());
+3178                if let Some(max_datagram_size) = self.datagrams().max_size()
+3179                    && self.datagrams.drop_oversized(max_datagram_size)
+3180                    && self.datagrams.send_blocked
+3181                {
+3182                    self.datagrams.send_blocked = false;
+3183                    self.events.push_back(Event::DatagramsUnblocked);
+3184                }
+3185                self.path_stats
+3186                    .entry(path_id)
+3187                    .or_default()
+3188                    .black_holes_detected += 1;
+3189            }
+3190
+3191            // Don't apply congestion penalty for lost ack-only packets
+3192            let lost_ack_eliciting =
+3193                old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes;
+3194
+3195            if lost_ack_eliciting {
+3196                self.path_stats
+3197                    .entry(path_id)
+3198                    .or_default()
+3199                    .congestion_events += 1;
+3200                self.path_data_mut(path_id).congestion.on_congestion_event(
+3201                    now,
+3202                    largest_lost_sent,
+3203                    in_persistent_congestion,
+3204                    false,
+3205                    size_of_lost_packets,
+3206                );
+3207            }
+3208        }
+3209
+3210        // Handle a lost MTU probe
+3211        if let Some(packet) = lost_mtu_probe {
+3212            let info = self.spaces[SpaceId::Data]
+3213                .for_path(path_id)
+3214                .take(packet)
+3215                .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and
+3216            // therefore must not have been removed yet
+3217            self.paths
+3218                .get_mut(&path_id)
+3219                .unwrap()
+3220                .remove_in_flight(&info);
+3221            self.path_data_mut(path_id).mtud.on_probe_lost();
+3222            self.path_stats
+3223                .entry(path_id)
+3224                .or_default()
+3225                .lost_plpmtud_probes += 1;
+3226        }
+3227    }
+3228
+3229    /// Returns the earliest time packets should be declared lost for all spaces on a path.
+3230    ///
+3231    /// If a path has an acknowledged packet with any prior un-acknowledged packets, the
+3232    /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed.
+3233    /// The time returned is when this packet should be declared lost.
+3234    fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> {
+3235        SpaceId::iter()
+3236            .filter_map(|id| {
+3237                self.spaces[id]
+3238                    .number_spaces
+3239                    .get(&path_id)
+3240                    .and_then(|pns| pns.loss_time)
+3241                    .map(|time| (time, id))
+3242            })
+3243            .min_by_key(|&(time, _)| time)
+3244    }
+3245
+3246    /// Returns the earliest next PTO should fire for all spaces on a path.
+3247    fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> {
+3248        let path = self.path(path_id)?;
+3249        let pto_count = path.pto_count;
+3250        let backoff = 2u32.pow(pto_count.min(MAX_BACKOFF_EXPONENT));
+3251        let mut duration = path.rtt.pto_base() * backoff;
+3252
+3253        if path_id == PathId::ZERO
+3254            && path.in_flight.ack_eliciting == 0
+3255            && !self.peer_completed_address_validation(PathId::ZERO)
+3256        {
+3257            // Address Validation during Connection Establishment:
+3258            // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a
+3259            // deadlock if an Initial or Handshake packet from the server is lost and the
+3260            // server can not send more due to its anti-amplification limit the client must
+3261            // send another packet on PTO.
+3262            let space = match self.highest_space {
+3263                SpaceId::Handshake => SpaceId::Handshake,
+3264                _ => SpaceId::Initial,
+3265            };
+3266
+3267            return Some((now + duration, space));
+3268        }
+3269
+3270        let mut result = None;
+3271        for space in SpaceId::iter() {
+3272            let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else {
+3273                continue;
+3274            };
+3275
+3276            if !pns.has_in_flight() {
+3277                continue;
+3278            }
+3279            if space == SpaceId::Data {
+3280                // Skip ApplicationData until handshake completes.
+3281                if self.is_handshaking() {
+3282                    return result;
+3283                }
+3284                // Include max_ack_delay and backoff for ApplicationData.
+3285                duration += self.ack_frequency.max_ack_delay_for_pto() * backoff;
+3286            }
+3287            let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else {
+3288                continue;
+3289            };
+3290            let pto = last_ack_eliciting + duration;
+3291            if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) {
+3292                if path.anti_amplification_blocked(1) {
+3293                    // Nothing would be able to be sent.
+3294                    continue;
+3295                }
+3296                if path.in_flight.ack_eliciting == 0 {
+3297                    // Nothing ack-eliciting, no PTO to arm/fire.
+3298                    continue;
+3299                }
+3300                result = Some((pto, space));
+3301            }
+3302        }
+3303        result
+3304    }
+3305
+3306    fn peer_completed_address_validation(&self, path: PathId) -> bool {
+3307        // TODO(flub): This logic needs updating for multipath
+3308        if self.side.is_server() || self.state.is_closed() {
+3309            return true;
+3310        }
+3311        // The server is guaranteed to have validated our address if any of our handshake or 1-RTT
+3312        // packets are acknowledged or we've seen HANDSHAKE_DONE and discarded handshake keys.
+3313        self.spaces[SpaceId::Handshake]
+3314            .path_space(PathId::ZERO)
+3315            .and_then(|pns| pns.largest_acked_packet)
+3316            .is_some()
+3317            || self.spaces[SpaceId::Data]
+3318                .path_space(path)
+3319                .and_then(|pns| pns.largest_acked_packet)
+3320                .is_some()
+3321            || (self.spaces[SpaceId::Data].crypto.is_some()
+3322                && self.spaces[SpaceId::Handshake].crypto.is_none())
+3323    }
+3324
+3325    /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed
+3326    ///
+3327    /// The timer must fire if either:
+3328    /// - An ack-eliciting packet we sent needs to be declared lost.
+3329    /// - A tail-loss probe needs to be sent.
+3330    ///
+3331    /// See [`Connection::on_loss_detection_timeout`] for details.
+3332    fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) {
+3333        if self.state.is_closed() {
+3334            // No loss detection takes place on closed connections, and `close_common` already
+3335            // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a
+3336            // reordered packet being handled by state-insensitive code.
+3337            return;
+3338        }
+3339
+3340        if let Some((loss_time, _)) = self.loss_time_and_space(path_id) {
+3341            // Time threshold loss detection.
+3342            self.timers.set(
+3343                Timer::PerPath(path_id, PathTimer::LossDetection),
+3344                loss_time,
+3345                self.qlog.with_time(now),
+3346            );
+3347            return;
+3348        }
+3349
+3350        // Determine which PN space to arm PTO for.
+3351        // Calculate PTO duration
+3352        if let Some((timeout, _)) = self.pto_time_and_space(now, path_id) {
+3353            self.timers.set(
+3354                Timer::PerPath(path_id, PathTimer::LossDetection),
+3355                timeout,
+3356                self.qlog.with_time(now),
+3357            );
+3358        } else {
+3359            self.timers.stop(
+3360                Timer::PerPath(path_id, PathTimer::LossDetection),
+3361                self.qlog.with_time(now),
+3362            );
+3363        }
+3364    }
+3365
+3366    /// The maximum probe timeout across all paths
+3367    ///
+3368    /// See [`Connection::pto`]
+3369    fn max_pto_all_paths(&self, space: SpaceId) -> Duration {
+3370        self.paths
+3371            .keys()
+3372            .map(|path_id| self.pto(space, *path_id))
+3373            .max()
+3374            .expect("there should be at least one path")
+3375    }
+3376
+3377    /// Probe Timeout
+3378    ///
+3379    /// The PTO is logically the time in which you'd expect to receive an acknowledgement
+3380    /// for a packet. So approximately RTT + max_ack_delay.
+3381    fn pto(&self, space: SpaceId, path_id: PathId) -> Duration {
+3382        let max_ack_delay = match space {
+3383            SpaceId::Initial | SpaceId::Handshake => Duration::ZERO,
+3384            SpaceId::Data => self.ack_frequency.max_ack_delay_for_pto(),
+3385        };
+3386        self.path_data(path_id).rtt.pto_base() + max_ack_delay
+3387    }
+3388
+3389    fn on_packet_authenticated(
+3390        &mut self,
+3391        now: Instant,
+3392        space_id: SpaceId,
+3393        path_id: PathId,
+3394        ecn: Option<EcnCodepoint>,
+3395        packet: Option<u64>,
+3396        spin: bool,
+3397        is_1rtt: bool,
+3398    ) {
+3399        self.total_authed_packets += 1;
+3400        if let Some(AbandonState::ExpectingPathAbandon { deadline }) = self
+3401            .paths
+3402            .get(&path_id)
+3403            .map(|path| &path.data.abandon_state)
+3404            && now > *deadline
+3405        {
+3406            warn!("received data on path which we abandoned more than 3 * PTO ago");
+3407            // The peer failed to respond with a PATH_ABANDON in time.
+3408            if !self.state.is_closed() {
+3409                self.state
+3410                    .move_to_closed(TransportError::PROTOCOL_VIOLATION(
+3411                        "peer failed to respond with PATH_ABANDON in time",
+3412                    ));
+3413                self.close_common();
+3414                self.set_close_timer(now);
+3415                self.connection_close_pending = true;
+3416            }
+3417            return;
+3418        }
+3419
+3420        self.reset_keep_alive(path_id, now);
+3421        self.reset_idle_timeout(now, space_id, path_id);
+3422        self.permit_idle_reset = true;
+3423        self.receiving_ecn |= ecn.is_some();
+3424        if let Some(x) = ecn {
+3425            let space = &mut self.spaces[space_id];
+3426            space.for_path(path_id).ecn_counters += x;
+3427
+3428            if x.is_ce() {
+3429                space
+3430                    .for_path(path_id)
+3431                    .pending_acks
+3432                    .set_immediate_ack_required();
+3433            }
+3434        }
+3435
+3436        let packet = match packet {
+3437            Some(x) => x,
+3438            None => return,
+3439        };
+3440        match &self.side {
+3441            ConnectionSide::Client { .. } => {
+3442                // If we received a handshake packet that authenticated, then we're talking to
+3443                // the real server.  From now on we should no longer allow the server to migrate
+3444                // its address.
+3445                if space_id == SpaceId::Handshake
+3446                    && let Some(hs) = self.state.as_handshake_mut()
+3447                {
+3448                    hs.allow_server_migration = false;
+3449                }
+3450            }
+3451            ConnectionSide::Server { .. } => {
+3452                if self.spaces[SpaceId::Initial].crypto.is_some() && space_id == SpaceId::Handshake
+3453                {
+3454                    // A server stops sending and processing Initial packets when it receives its first Handshake packet.
+3455                    self.discard_space(now, SpaceId::Initial);
+3456                }
+3457                if self.zero_rtt_crypto.is_some() && is_1rtt {
+3458                    // Discard 0-RTT keys soon after receiving a 1-RTT packet
+3459                    self.set_key_discard_timer(now, space_id)
+3460                }
+3461            }
+3462        }
+3463        let space = self.spaces[space_id].for_path(path_id);
+3464        space.pending_acks.insert_one(packet, now);
+3465        if packet >= space.rx_packet.unwrap_or_default() {
+3466            space.rx_packet = Some(packet);
+3467            // Update outgoing spin bit, inverting iff we're the client
+3468            self.spin = self.side.is_client() ^ spin;
+3469        }
+3470    }
+3471
+3472    /// Resets the idle timeout timers
+3473    ///
+3474    /// Without multipath there is only the connection-wide idle timeout. When multipath is
+3475    /// enabled there is an additional per-path idle timeout.
+3476    fn reset_idle_timeout(&mut self, now: Instant, space: SpaceId, path_id: PathId) {
+3477        // First reset the global idle timeout.
+3478        if let Some(timeout) = self.idle_timeout {
+3479            if self.state.is_closed() {
+3480                self.timers
+3481                    .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now));
+3482            } else {
+3483                let dt = cmp::max(timeout, 3 * self.max_pto_all_paths(space));
+3484                self.timers.set(
+3485                    Timer::Conn(ConnTimer::Idle),
+3486                    now + dt,
+3487                    self.qlog.with_time(now),
+3488                );
+3489            }
+3490        }
+3491
+3492        // Now handle the per-path state
+3493        if let Some(timeout) = self.path_data(path_id).idle_timeout {
+3494            if self.state.is_closed() {
+3495                self.timers.stop(
+3496                    Timer::PerPath(path_id, PathTimer::PathIdle),
+3497                    self.qlog.with_time(now),
+3498                );
+3499            } else {
+3500                let dt = cmp::max(timeout, 3 * self.pto(space, path_id));
+3501                self.timers.set(
+3502                    Timer::PerPath(path_id, PathTimer::PathIdle),
+3503                    now + dt,
+3504                    self.qlog.with_time(now),
+3505                );
+3506            }
+3507        }
+3508    }
+3509
+3510    /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers
+3511    fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) {
+3512        if !self.state.is_established() {
+3513            return;
+3514        }
+3515
+3516        if let Some(interval) = self.config.keep_alive_interval {
+3517            self.timers.set(
+3518                Timer::Conn(ConnTimer::KeepAlive),
+3519                now + interval,
+3520                self.qlog.with_time(now),
+3521            );
+3522        }
+3523
+3524        if let Some(interval) = self.path_data(path_id).keep_alive {
+3525            self.timers.set(
+3526                Timer::PerPath(path_id, PathTimer::PathKeepAlive),
+3527                now + interval,
+3528                self.qlog.with_time(now),
+3529            );
+3530        }
+3531    }
+3532
+3533    /// Sets the timer for when a previously issued CID should be retired next
+3534    fn reset_cid_retirement(&mut self, now: Instant) {
+3535        if let Some((_path, t)) = self.next_cid_retirement() {
+3536            self.timers.set(
+3537                Timer::Conn(ConnTimer::PushNewCid),
+3538                t,
+3539                self.qlog.with_time(now),
+3540            );
+3541        }
+3542    }
+3543
+3544    /// The next time when a previously issued CID should be retired
+3545    fn next_cid_retirement(&self) -> Option<(PathId, Instant)> {
+3546        self.local_cid_state
+3547            .iter()
+3548            .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t)))
+3549            .min_by_key(|(_path_id, timeout)| *timeout)
+3550    }
+3551
+3552    /// Handle the already-decrypted first packet from the client
+3553    ///
+3554    /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more
+3555    /// efficient.
+3556    pub(crate) fn handle_first_packet(
+3557        &mut self,
+3558        now: Instant,
+3559        network_path: FourTuple,
+3560        ecn: Option<EcnCodepoint>,
+3561        packet_number: u64,
+3562        packet: InitialPacket,
+3563        remaining: Option<BytesMut>,
+3564    ) -> Result<(), ConnectionError> {
+3565        let span = trace_span!("first recv");
+3566        let _guard = span.enter();
+3567        debug_assert!(self.side.is_server());
+3568        let len = packet.header_data.len() + packet.payload.len();
+3569        let path_id = PathId::ZERO;
+3570        self.path_data_mut(path_id).total_recvd = len as u64;
+3571
+3572        if let Some(hs) = self.state.as_handshake_mut() {
+3573            hs.expected_token = packet.header.token.clone();
+3574        } else {
+3575            unreachable!("first packet must be delivered in Handshake state");
+3576        }
+3577
+3578        // The first packet is always on PathId::ZERO
+3579        self.on_packet_authenticated(
+3580            now,
+3581            SpaceId::Initial,
+3582            path_id,
+3583            ecn,
+3584            Some(packet_number),
+3585            false,
+3586            false,
+3587        );
+3588
+3589        let packet: Packet = packet.into();
+3590
+3591        let mut qlog = QlogRecvPacket::new(len);
+3592        qlog.header(&packet.header, Some(packet_number), path_id);
+3593
+3594        self.process_decrypted_packet(
+3595            now,
+3596            network_path,
+3597            path_id,
+3598            Some(packet_number),
+3599            packet,
+3600            &mut qlog,
+3601        )?;
+3602        self.qlog.emit_packet_received(qlog, now);
+3603        if let Some(data) = remaining {
+3604            self.handle_coalesced(now, network_path, path_id, ecn, data);
+3605        }
+3606
+3607        self.qlog.emit_recovery_metrics(
+3608            path_id,
+3609            &mut self.paths.get_mut(&path_id).unwrap().data,
+3610            now,
+3611        );
+3612
+3613        Ok(())
+3614    }
+3615
+3616    fn init_0rtt(&mut self, now: Instant) {
+3617        let (header, packet) = match self.crypto.early_crypto() {
+3618            Some(x) => x,
+3619            None => return,
+3620        };
+3621        if self.side.is_client() {
+3622            match self.crypto.transport_parameters() {
+3623                Ok(params) => {
+3624                    let params = params
+3625                        .expect("crypto layer didn't supply transport parameters with ticket");
+3626                    // Certain values must not be cached
+3627                    let params = TransportParameters {
+3628                        initial_src_cid: None,
+3629                        original_dst_cid: None,
+3630                        preferred_address: None,
+3631                        retry_src_cid: None,
+3632                        stateless_reset_token: None,
+3633                        min_ack_delay: None,
+3634                        ack_delay_exponent: TransportParameters::default().ack_delay_exponent,
+3635                        max_ack_delay: TransportParameters::default().max_ack_delay,
+3636                        initial_max_path_id: None,
+3637                        ..params
+3638                    };
+3639                    self.set_peer_params(params);
+3640                    self.qlog.emit_peer_transport_params_restored(self, now);
+3641                }
+3642                Err(e) => {
+3643                    error!("session ticket has malformed transport parameters: {}", e);
+3644                    return;
+3645                }
+3646            }
+3647        }
+3648        trace!("0-RTT enabled");
+3649        self.zero_rtt_enabled = true;
+3650        self.zero_rtt_crypto = Some(ZeroRttCrypto { header, packet });
+3651    }
+3652
+3653    fn read_crypto(
+3654        &mut self,
+3655        space: SpaceId,
+3656        crypto: &frame::Crypto,
+3657        payload_len: usize,
+3658    ) -> Result<(), TransportError> {
+3659        let expected = if !self.state.is_handshake() {
+3660            SpaceId::Data
+3661        } else if self.highest_space == SpaceId::Initial {
+3662            SpaceId::Initial
+3663        } else {
+3664            // On the server, self.highest_space can be Data after receiving the client's first
+3665            // flight, but we expect Handshake CRYPTO until the handshake is complete.
+3666            SpaceId::Handshake
+3667        };
+3668        // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT
+3669        // packets are illegal, and we don't process 1-RTT packets until the handshake is
+3670        // complete. Therefore, we will never see CRYPTO data from a later-than-expected space.
+3671        debug_assert!(space <= expected, "received out-of-order CRYPTO data");
+3672
+3673        let end = crypto.offset + crypto.data.len() as u64;
+3674        if space < expected && end > self.spaces[space].crypto_stream.bytes_read() {
+3675            warn!(
+3676                "received new {:?} CRYPTO data when expecting {:?}",
+3677                space, expected
+3678            );
+3679            return Err(TransportError::PROTOCOL_VIOLATION(
+3680                "new data at unexpected encryption level",
+3681            ));
+3682        }
+3683
+3684        let space = &mut self.spaces[space];
+3685        let max = end.saturating_sub(space.crypto_stream.bytes_read());
+3686        if max > self.config.crypto_buffer_size as u64 {
+3687            return Err(TransportError::CRYPTO_BUFFER_EXCEEDED(""));
+3688        }
+3689
+3690        space
+3691            .crypto_stream
+3692            .insert(crypto.offset, crypto.data.clone(), payload_len);
+3693        while let Some(chunk) = space.crypto_stream.read(usize::MAX, true) {
+3694            trace!("consumed {} CRYPTO bytes", chunk.bytes.len());
+3695            if self.crypto.read_handshake(&chunk.bytes)? {
+3696                self.events.push_back(Event::HandshakeDataReady);
+3697            }
+3698        }
+3699
+3700        Ok(())
+3701    }
+3702
+3703    fn write_crypto(&mut self) {
+3704        loop {
+3705            let space = self.highest_space;
+3706            let mut outgoing = Vec::new();
+3707            if let Some(crypto) = self.crypto.write_handshake(&mut outgoing) {
+3708                match space {
+3709                    SpaceId::Initial => {
+3710                        self.upgrade_crypto(SpaceId::Handshake, crypto);
+3711                    }
+3712                    SpaceId::Handshake => {
+3713                        self.upgrade_crypto(SpaceId::Data, crypto);
+3714                    }
+3715                    _ => unreachable!("got updated secrets during 1-RTT"),
+3716                }
+3717            }
+3718            if outgoing.is_empty() {
+3719                if space == self.highest_space {
+3720                    break;
+3721                } else {
+3722                    // Keys updated, check for more data to send
+3723                    continue;
+3724                }
+3725            }
+3726            let offset = self.spaces[space].crypto_offset;
+3727            let outgoing = Bytes::from(outgoing);
+3728            if let Some(hs) = self.state.as_handshake_mut()
+3729                && space == SpaceId::Initial
+3730                && offset == 0
+3731                && self.side.is_client()
+3732            {
+3733                hs.client_hello = Some(outgoing.clone());
+3734            }
+3735            self.spaces[space].crypto_offset += outgoing.len() as u64;
+3736            trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space);
+3737            self.spaces[space].pending.crypto.push_back(frame::Crypto {
+3738                offset,
+3739                data: outgoing,
+3740            });
+3741        }
+3742    }
+3743
+3744    /// Switch to stronger cryptography during handshake
+3745    fn upgrade_crypto(&mut self, space: SpaceId, crypto: Keys) {
+3746        debug_assert!(
+3747            self.spaces[space].crypto.is_none(),
+3748            "already reached packet space {space:?}"
+3749        );
+3750        trace!("{:?} keys ready", space);
+3751        if space == SpaceId::Data {
+3752            // Precompute the first key update
+3753            self.next_crypto = Some(
+3754                self.crypto
+3755                    .next_1rtt_keys()
+3756                    .expect("handshake should be complete"),
+3757            );
+3758        }
+3759
+3760        self.spaces[space].crypto = Some(crypto);
+3761        debug_assert!(space as usize > self.highest_space as usize);
+3762        self.highest_space = space;
+3763        if space == SpaceId::Data && self.side.is_client() {
+3764            // Discard 0-RTT keys because 1-RTT keys are available.
+3765            self.zero_rtt_crypto = None;
+3766        }
+3767    }
+3768
+3769    fn discard_space(&mut self, now: Instant, space_id: SpaceId) {
+3770        debug_assert!(space_id != SpaceId::Data);
+3771        trace!("discarding {:?} keys", space_id);
+3772        if space_id == SpaceId::Initial {
+3773            // No longer needed
+3774            if let ConnectionSide::Client { token, .. } = &mut self.side {
+3775                *token = Bytes::new();
+3776            }
+3777        }
+3778        let space = &mut self.spaces[space_id];
+3779        space.crypto = None;
+3780        let pns = space.for_path(PathId::ZERO);
+3781        pns.time_of_last_ack_eliciting_packet = None;
+3782        pns.loss_time = None;
+3783        pns.loss_probes = 0;
+3784        let sent_packets = mem::take(&mut pns.sent_packets);
+3785        let path = self.paths.get_mut(&PathId::ZERO).unwrap();
+3786        for (_, packet) in sent_packets.into_iter() {
+3787            path.data.remove_in_flight(&packet);
+3788        }
+3789
+3790        self.set_loss_detection_timer(now, PathId::ZERO)
+3791    }
+3792
+3793    fn handle_coalesced(
+3794        &mut self,
+3795        now: Instant,
+3796        network_path: FourTuple,
+3797        path_id: PathId,
+3798        ecn: Option<EcnCodepoint>,
+3799        data: BytesMut,
+3800    ) {
+3801        self.path_data_mut(path_id)
+3802            .inc_total_recvd(data.len() as u64);
+3803        let mut remaining = Some(data);
+3804        let cid_len = self
+3805            .local_cid_state
+3806            .values()
+3807            .map(|cid_state| cid_state.cid_len())
+3808            .next()
+3809            .expect("one cid_state must exist");
+3810        while let Some(data) = remaining {
+3811            match PartialDecode::new(
+3812                data,
+3813                &FixedLengthConnectionIdParser::new(cid_len),
+3814                &[self.version],
+3815                self.endpoint_config.grease_quic_bit,
+3816            ) {
+3817                Ok((partial_decode, rest)) => {
+3818                    remaining = rest;
+3819                    self.handle_decode(now, network_path, path_id, ecn, partial_decode);
+3820                }
+3821                Err(e) => {
+3822                    trace!("malformed header: {}", e);
+3823                    return;
+3824                }
+3825            }
+3826        }
+3827    }
+3828
+3829    fn handle_decode(
+3830        &mut self,
+3831        now: Instant,
+3832        network_path: FourTuple,
+3833        path_id: PathId,
+3834        ecn: Option<EcnCodepoint>,
+3835        partial_decode: PartialDecode,
+3836    ) {
+3837        let qlog = QlogRecvPacket::new(partial_decode.len());
+3838        if let Some(decoded) = packet_crypto::unprotect_header(
+3839            partial_decode,
+3840            &self.spaces,
+3841            self.zero_rtt_crypto.as_ref(),
+3842            self.peer_params.stateless_reset_token,
+3843        ) {
+3844            self.handle_packet(
+3845                now,
+3846                network_path,
+3847                path_id,
+3848                ecn,
+3849                decoded.packet,
+3850                decoded.stateless_reset,
+3851                qlog,
+3852            );
+3853        }
+3854    }
+3855
+3856    fn handle_packet(
+3857        &mut self,
+3858        now: Instant,
+3859        network_path: FourTuple,
+3860        path_id: PathId,
+3861        ecn: Option<EcnCodepoint>,
+3862        packet: Option<Packet>,
+3863        stateless_reset: bool,
+3864        mut qlog: QlogRecvPacket,
+3865    ) {
+3866        self.stats.udp_rx.ios += 1;
+3867        self.path_stats.entry(path_id).or_default().udp_rx.ios += 1;
+3868
+3869        if let Some(ref packet) = packet {
+3870            trace!(
+3871                "got {:?} packet ({} bytes) from {} using id {}",
+3872                packet.header.space(),
+3873                packet.payload.len() + packet.header_data.len(),
+3874                network_path,
+3875                packet.header.dst_cid(),
+3876            );
+3877        }
+3878
+3879        if self.is_handshaking() {
+3880            if path_id != PathId::ZERO {
+3881                debug!(%network_path, %path_id, "discarding multipath packet during handshake");
+3882                return;
+3883            }
+3884            if network_path != self.path_data_mut(path_id).network_path {
+3885                if let Some(hs) = self.state.as_handshake() {
+3886                    if hs.allow_server_migration {
+3887                        trace!(%network_path, prev = %self.path_data(path_id).network_path, "server migrated to new remote");
+3888                        self.path_data_mut(path_id).network_path = network_path;
+3889                        self.qlog.emit_tuple_assigned(path_id, network_path, now);
+3890                    } else {
+3891                        debug!("discarding packet with unexpected remote during handshake");
+3892                        return;
+3893                    }
+3894                } else {
+3895                    debug!("discarding packet with unexpected remote during handshake");
+3896                    return;
+3897                }
+3898            }
+3899        }
+3900
+3901        let was_closed = self.state.is_closed();
+3902        let was_drained = self.state.is_drained();
+3903
+3904        let decrypted = match packet {
+3905            None => Err(None),
+3906            Some(mut packet) => self
+3907                .decrypt_packet(now, path_id, &mut packet)
+3908                .map(move |number| (packet, number)),
+3909        };
+3910        let result = match decrypted {
+3911            _ if stateless_reset => {
+3912                debug!("got stateless reset");
+3913                Err(ConnectionError::Reset)
+3914            }
+3915            Err(Some(e)) => {
+3916                warn!("illegal packet: {}", e);
+3917                Err(e.into())
+3918            }
+3919            Err(None) => {
+3920                debug!("failed to authenticate packet");
+3921                self.authentication_failures += 1;
+3922                let integrity_limit = self.spaces[self.highest_space]
+3923                    .crypto
+3924                    .as_ref()
+3925                    .unwrap()
+3926                    .packet
+3927                    .local
+3928                    .integrity_limit();
+3929                if self.authentication_failures > integrity_limit {
+3930                    Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into())
+3931                } else {
+3932                    return;
+3933                }
+3934            }
+3935            Ok((packet, number)) => {
+3936                qlog.header(&packet.header, number, path_id);
+3937                let span = match number {
+3938                    Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn),
+3939                    None => trace_span!("recv", space = ?packet.header.space()),
+3940                };
+3941                let _guard = span.enter();
+3942
+3943                let dedup = self.spaces[packet.header.space()]
+3944                    .path_space_mut(path_id)
+3945                    .map(|pns| &mut pns.dedup);
+3946                if number.zip(dedup).is_some_and(|(n, d)| d.insert(n)) {
+3947                    debug!("discarding possible duplicate packet");
+3948                    self.qlog.emit_packet_received(qlog, now);
+3949                    return;
+3950                } else if self.state.is_handshake() && packet.header.is_short() {
+3951                    // TODO: SHOULD buffer these to improve reordering tolerance.
+3952                    trace!("dropping short packet during handshake");
+3953                    self.qlog.emit_packet_received(qlog, now);
+3954                    return;
+3955                } else {
+3956                    if let Header::Initial(InitialHeader { ref token, .. }) = packet.header
+3957                        && let Some(hs) = self.state.as_handshake()
+3958                        && self.side.is_server()
+3959                        && token != &hs.expected_token
+3960                    {
+3961                        // Clients must send the same retry token in every Initial. Initial
+3962                        // packets can be spoofed, so we discard rather than killing the
+3963                        // connection.
+3964                        warn!("discarding Initial with invalid retry token");
+3965                        self.qlog.emit_packet_received(qlog, now);
+3966                        return;
+3967                    }
+3968
+3969                    if !self.state.is_closed() {
+3970                        let spin = match packet.header {
+3971                            Header::Short { spin, .. } => spin,
+3972                            _ => false,
+3973                        };
+3974
+3975                        if self.side().is_server() && !self.abandoned_paths.contains(&path_id) {
+3976                            // Only the client is allowed to open paths
+3977                            self.ensure_path(path_id, network_path, now, number);
+3978                        }
+3979                        if self.paths.contains_key(&path_id) {
+3980                            self.on_packet_authenticated(
+3981                                now,
+3982                                packet.header.space(),
+3983                                path_id,
+3984                                ecn,
+3985                                number,
+3986                                spin,
+3987                                packet.header.is_1rtt(),
+3988                            );
+3989                        }
+3990                    }
+3991
+3992                    let res = self.process_decrypted_packet(
+3993                        now,
+3994                        network_path,
+3995                        path_id,
+3996                        number,
+3997                        packet,
+3998                        &mut qlog,
+3999                    );
+4000
+4001                    self.qlog.emit_packet_received(qlog, now);
+4002                    res
+4003                }
+4004            }
+4005        };
+4006
+4007        // State transitions for error cases
+4008        if let Err(conn_err) = result {
+4009            match conn_err {
+4010                ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason),
+4011                ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason),
+4012                ConnectionError::Reset
+4013                | ConnectionError::TransportError(TransportError {
+4014                    code: TransportErrorCode::AEAD_LIMIT_REACHED,
+4015                    ..
+4016                }) => {
+4017                    self.state.move_to_drained(Some(conn_err));
+4018                }
+4019                ConnectionError::TimedOut => {
+4020                    unreachable!("timeouts aren't generated by packet processing");
+4021                }
+4022                ConnectionError::TransportError(err) => {
+4023                    debug!("closing connection due to transport error: {}", err);
+4024                    self.state.move_to_closed(err);
+4025                }
+4026                ConnectionError::VersionMismatch => {
+4027                    self.state.move_to_draining(Some(conn_err));
+4028                }
+4029                ConnectionError::LocallyClosed => {
+4030                    unreachable!("LocallyClosed isn't generated by packet processing");
+4031                }
+4032                ConnectionError::CidsExhausted => {
+4033                    unreachable!("CidsExhausted isn't generated by packet processing");
+4034                }
+4035            };
+4036        }
+4037
+4038        if !was_closed && self.state.is_closed() {
+4039            self.close_common();
+4040            if !self.state.is_drained() {
+4041                self.set_close_timer(now);
+4042            }
+4043        }
+4044        if !was_drained && self.state.is_drained() {
+4045            self.endpoint_events.push_back(EndpointEventInner::Drained);
+4046            // Close timer may have been started previously, e.g. if we sent a close and got a
+4047            // stateless reset in response
+4048            self.timers
+4049                .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now));
+4050        }
+4051
+4052        // Transmit CONNECTION_CLOSE if necessary.
+4053        //
+4054        // If we received a valid packet and we are in the closed state we should respond
+4055        // with a CONNECTION_CLOSE frame.
+4056        // TODO: This SHOULD be rate-limited according to §10.2.1 of QUIC-TRANSPORT, but
+4057        //    that does not yet happen. This is triggered by each received packet.
+4058        if matches!(self.state.as_type(), StateType::Closed) {
+4059            // From https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1-7
+4060            //
+4061            // While in the closing state we must either:
+4062            // - discard packets coming from an un-validated remote OR
+4063            // - ensure we do not send more than 3 times the received data
+4064            //
+4065            // Doing the 2nd would mean we would be able to send CONNECTION_CLOSE to a peer
+4066            // who was (involuntary) migrated just at the time we initiated immediate
+4067            // close. It is a lot more work though. So while we would like to do this for
+4068            // now we only do 1.
+4069            //
+4070            // Another shortcoming of the current implementation is that when we have a
+4071            // previous PathData which is validated and the remote matches that path, we
+4072            // should schedule CONNECTION_CLOSE on that path. However currently we can not
+4073            // schedule such a packet. We should also fix this some day. This makes us
+4074            // vulnerable to an attacker faking a migration at the right time and then we'd
+4075            // be unable to send the CONNECTION_CLOSE to the real remote.
+4076            if self
+4077                .paths
+4078                .get(&path_id)
+4079                .map(|p| p.data.validated && p.data.network_path == network_path)
+4080                .unwrap_or(false)
+4081            {
+4082                self.connection_close_pending = true;
+4083            }
+4084        }
+4085    }
+4086
+4087    fn process_decrypted_packet(
+4088        &mut self,
+4089        now: Instant,
+4090        network_path: FourTuple,
+4091        path_id: PathId,
+4092        number: Option<u64>,
+4093        packet: Packet,
+4094        qlog: &mut QlogRecvPacket,
+4095    ) -> Result<(), ConnectionError> {
+4096        if !self.paths.contains_key(&path_id) {
+4097            // There is a chance this is a server side, first (for this path) packet, which would
+4098            // be a protocol violation. It's more likely, however, that this is a packet of a
+4099            // pruned path
+4100            trace!(%path_id, ?number, "discarding packet for unknown path");
+4101            return Ok(());
+4102        }
+4103        let state = match self.state.as_type() {
+4104            StateType::Established => {
+4105                match packet.header.space() {
+4106                    SpaceId::Data => self.process_payload(
+4107                        now,
+4108                        network_path,
+4109                        path_id,
+4110                        number.unwrap(),
+4111                        packet,
+4112                        qlog,
+4113                    )?,
+4114                    _ if packet.header.has_frames() => {
+4115                        self.process_early_payload(now, path_id, packet, qlog)?
+4116                    }
+4117                    _ => {
+4118                        trace!("discarding unexpected pre-handshake packet");
+4119                    }
+4120                }
+4121                return Ok(());
+4122            }
+4123            StateType::Closed => {
+4124                for result in frame::Iter::new(packet.payload.freeze())? {
+4125                    let frame = match result {
+4126                        Ok(frame) => frame,
+4127                        Err(err) => {
+4128                            debug!("frame decoding error: {err:?}");
+4129                            continue;
+4130                        }
+4131                    };
+4132                    qlog.frame(&frame);
+4133
+4134                    if let Frame::Padding = frame {
+4135                        continue;
+4136                    };
+4137
+4138                    self.stats.frame_rx.record(frame.ty());
+4139
+4140                    if let Frame::Close(_error) = frame {
+4141                        self.state.move_to_draining(None);
+4142                        break;
+4143                    }
+4144                }
+4145                return Ok(());
+4146            }
+4147            StateType::Draining | StateType::Drained => return Ok(()),
+4148            StateType::Handshake => self.state.as_handshake_mut().expect("checked"),
+4149        };
+4150
+4151        match packet.header {
+4152            Header::Retry {
+4153                src_cid: remote_cid,
+4154                ..
+4155            } => {
+4156                debug_assert_eq!(path_id, PathId::ZERO);
+4157                if self.side.is_server() {
+4158                    return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into());
+4159                }
+4160
+4161                let is_valid_retry = self
+4162                    .remote_cids
+4163                    .get(&path_id)
+4164                    .map(|cids| cids.active())
+4165                    .map(|orig_dst_cid| {
+4166                        self.crypto.is_valid_retry(
+4167                            orig_dst_cid,
+4168                            &packet.header_data,
+4169                            &packet.payload,
+4170                        )
+4171                    })
+4172                    .unwrap_or_default();
+4173                if self.total_authed_packets > 1
+4174                            || packet.payload.len() <= 16 // token + 16 byte tag
+4175                            || !is_valid_retry
+4176                {
+4177                    trace!("discarding invalid Retry");
+4178                    // - After the client has received and processed an Initial or Retry
+4179                    //   packet from the server, it MUST discard any subsequent Retry
+4180                    //   packets that it receives.
+4181                    // - A client MUST discard a Retry packet with a zero-length Retry Token
+4182                    //   field.
+4183                    // - Clients MUST discard Retry packets that have a Retry Integrity Tag
+4184                    //   that cannot be validated
+4185                    return Ok(());
+4186                }
+4187
+4188                trace!("retrying with CID {}", remote_cid);
+4189                let client_hello = state.client_hello.take().unwrap();
+4190                self.retry_src_cid = Some(remote_cid);
+4191                self.remote_cids
+4192                    .get_mut(&path_id)
+4193                    .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false")
+4194                    .update_initial_cid(remote_cid);
+4195                self.remote_handshake_cid = remote_cid;
+4196
+4197                let space = &mut self.spaces[SpaceId::Initial];
+4198                if let Some(info) = space.for_path(PathId::ZERO).take(0) {
+4199                    self.on_packet_acked(now, PathId::ZERO, info);
+4200                };
+4201
+4202                self.discard_space(now, SpaceId::Initial); // Make sure we clean up after
+4203                // any retransmitted Initials
+4204                self.spaces[SpaceId::Initial] = {
+4205                    let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng);
+4206                    space.crypto = Some(self.crypto.initial_keys(remote_cid, self.side.side()));
+4207                    space.crypto_offset = client_hello.len() as u64;
+4208                    space.for_path(path_id).next_packet_number = self.spaces[SpaceId::Initial]
+4209                        .for_path(path_id)
+4210                        .next_packet_number;
+4211                    space.pending.crypto.push_back(frame::Crypto {
+4212                        offset: 0,
+4213                        data: client_hello,
+4214                    });
+4215                    space
+4216                };
+4217
+4218                // Retransmit all 0-RTT data
+4219                let zero_rtt = mem::take(
+4220                    &mut self.spaces[SpaceId::Data]
+4221                        .for_path(PathId::ZERO)
+4222                        .sent_packets,
+4223                );
+4224                for (_, info) in zero_rtt.into_iter() {
+4225                    self.paths
+4226                        .get_mut(&PathId::ZERO)
+4227                        .unwrap()
+4228                        .remove_in_flight(&info);
+4229                    self.spaces[SpaceId::Data].pending |= info.retransmits;
+4230                }
+4231                self.streams.retransmit_all_for_0rtt();
+4232
+4233                let token_len = packet.payload.len() - 16;
+4234                let ConnectionSide::Client { ref mut token, .. } = self.side else {
+4235                    unreachable!("we already short-circuited if we're server");
+4236                };
+4237                *token = packet.payload.freeze().split_to(token_len);
+4238
+4239                self.state = State::handshake(state::Handshake {
+4240                    expected_token: Bytes::new(),
+4241                    remote_cid_set: false,
+4242                    client_hello: None,
+4243                    allow_server_migration: true,
+4244                });
+4245                Ok(())
+4246            }
+4247            Header::Long {
+4248                ty: LongType::Handshake,
+4249                src_cid: remote_cid,
+4250                dst_cid: local_cid,
+4251                ..
+4252            } => {
+4253                debug_assert_eq!(path_id, PathId::ZERO);
+4254                if remote_cid != self.remote_handshake_cid {
+4255                    debug!(
+4256                        "discarding packet with mismatched remote CID: {} != {}",
+4257                        self.remote_handshake_cid, remote_cid
+4258                    );
+4259                    return Ok(());
+4260                }
+4261                self.on_path_validated(path_id);
+4262
+4263                self.process_early_payload(now, path_id, packet, qlog)?;
+4264                if self.state.is_closed() {
+4265                    return Ok(());
+4266                }
+4267
+4268                if self.crypto.is_handshaking() {
+4269                    trace!("handshake ongoing");
+4270                    return Ok(());
+4271                }
+4272
+4273                if self.side.is_client() {
+4274                    // Client-only because server params were set from the client's Initial
+4275                    let params = self.crypto.transport_parameters()?.ok_or_else(|| {
+4276                        TransportError::new(
+4277                            TransportErrorCode::crypto(0x6d),
+4278                            "transport parameters missing".to_owned(),
+4279                        )
+4280                    })?;
+4281
+4282                    if self.has_0rtt() {
+4283                        if !self.crypto.early_data_accepted().unwrap() {
+4284                            debug_assert!(self.side.is_client());
+4285                            debug!("0-RTT rejected");
+4286                            self.accepted_0rtt = false;
+4287                            self.streams.zero_rtt_rejected();
+4288
+4289                            // Discard already-queued frames
+4290                            self.spaces[SpaceId::Data].pending = Retransmits::default();
+4291
+4292                            // Discard 0-RTT packets
+4293                            let sent_packets = mem::take(
+4294                                &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets,
+4295                            );
+4296                            for (_, packet) in sent_packets.into_iter() {
+4297                                self.paths
+4298                                    .get_mut(&path_id)
+4299                                    .unwrap()
+4300                                    .remove_in_flight(&packet);
+4301                            }
+4302                        } else {
+4303                            self.accepted_0rtt = true;
+4304                            params.validate_resumption_from(&self.peer_params)?;
+4305                        }
+4306                    }
+4307                    if let Some(token) = params.stateless_reset_token {
+4308                        // TODO(matheus23): Reset token for a remote, or for a 4-tuple?
+4309                        let remote = self.path_data(path_id).network_path.remote;
+4310                        self.endpoint_events
+4311                            .push_back(EndpointEventInner::ResetToken(path_id, remote, token));
+4312                    }
+4313                    self.handle_peer_params(params, local_cid, remote_cid, now)?;
+4314                    self.issue_first_cids(now);
+4315                } else {
+4316                    // Server-only
+4317                    self.spaces[SpaceId::Data].pending.handshake_done = true;
+4318                    self.discard_space(now, SpaceId::Handshake);
+4319                    self.events.push_back(Event::HandshakeConfirmed);
+4320                    trace!("handshake confirmed");
+4321                }
+4322
+4323                self.events.push_back(Event::Connected);
+4324                self.state.move_to_established();
+4325                trace!("established");
+4326
+4327                // Multipath can only be enabled after the state has reached Established.
+4328                // So this can not happen any earlier.
+4329                self.issue_first_path_cids(now);
+4330                Ok(())
+4331            }
+4332            Header::Initial(InitialHeader {
+4333                src_cid: remote_cid,
+4334                dst_cid: local_cid,
+4335                ..
+4336            }) => {
+4337                debug_assert_eq!(path_id, PathId::ZERO);
+4338                if !state.remote_cid_set {
+4339                    trace!("switching remote CID to {}", remote_cid);
+4340                    let mut state = state.clone();
+4341                    self.remote_cids
+4342                        .get_mut(&path_id)
+4343                        .expect("PathId::ZERO not yet abandoned")
+4344                        .update_initial_cid(remote_cid);
+4345                    self.remote_handshake_cid = remote_cid;
+4346                    self.original_remote_cid = remote_cid;
+4347                    state.remote_cid_set = true;
+4348                    self.state.move_to_handshake(state);
+4349                } else if remote_cid != self.remote_handshake_cid {
+4350                    debug!(
+4351                        "discarding packet with mismatched remote CID: {} != {}",
+4352                        self.remote_handshake_cid, remote_cid
+4353                    );
+4354                    return Ok(());
+4355                }
+4356
+4357                let starting_space = self.highest_space;
+4358                self.process_early_payload(now, path_id, packet, qlog)?;
+4359
+4360                if self.side.is_server()
+4361                    && starting_space == SpaceId::Initial
+4362                    && self.highest_space != SpaceId::Initial
+4363                {
+4364                    let params = self.crypto.transport_parameters()?.ok_or_else(|| {
+4365                        TransportError::new(
+4366                            TransportErrorCode::crypto(0x6d),
+4367                            "transport parameters missing".to_owned(),
+4368                        )
+4369                    })?;
+4370                    self.handle_peer_params(params, local_cid, remote_cid, now)?;
+4371                    self.issue_first_cids(now);
+4372                    self.init_0rtt(now);
+4373                }
+4374                Ok(())
+4375            }
+4376            Header::Long {
+4377                ty: LongType::ZeroRtt,
+4378                ..
+4379            } => {
+4380                self.process_payload(now, network_path, path_id, number.unwrap(), packet, qlog)?;
+4381                Ok(())
+4382            }
+4383            Header::VersionNegotiate { .. } => {
+4384                if self.total_authed_packets > 1 {
+4385                    return Ok(());
+4386                }
+4387                let supported = packet
+4388                    .payload
+4389                    .chunks(4)
+4390                    .any(|x| match <[u8; 4]>::try_from(x) {
+4391                        Ok(version) => self.version == u32::from_be_bytes(version),
+4392                        Err(_) => false,
+4393                    });
+4394                if supported {
+4395                    return Ok(());
+4396                }
+4397                debug!("remote doesn't support our version");
+4398                Err(ConnectionError::VersionMismatch)
+4399            }
+4400            Header::Short { .. } => unreachable!(
+4401                "short packets received during handshake are discarded in handle_packet"
+4402            ),
+4403        }
+4404    }
+4405
+4406    /// Process an Initial or Handshake packet payload
+4407    fn process_early_payload(
+4408        &mut self,
+4409        now: Instant,
+4410        path_id: PathId,
+4411        packet: Packet,
+4412        #[allow(unused)] qlog: &mut QlogRecvPacket,
+4413    ) -> Result<(), TransportError> {
+4414        debug_assert_ne!(packet.header.space(), SpaceId::Data);
+4415        debug_assert_eq!(path_id, PathId::ZERO);
+4416        let payload_len = packet.payload.len();
+4417        let mut ack_eliciting = false;
+4418        for result in frame::Iter::new(packet.payload.freeze())? {
+4419            let frame = result?;
+4420            qlog.frame(&frame);
+4421            let span = match frame {
+4422                Frame::Padding => continue,
+4423                _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)),
+4424            };
+4425
+4426            self.stats.frame_rx.record(frame.ty());
+4427
+4428            let _guard = span.as_ref().map(|x| x.enter());
+4429            ack_eliciting |= frame.is_ack_eliciting();
+4430
+4431            // Process frames
+4432            if frame.is_1rtt() && packet.header.space() != SpaceId::Data {
+4433                return Err(TransportError::PROTOCOL_VIOLATION(
+4434                    "illegal frame type in handshake",
+4435                ));
+4436            }
+4437
+4438            match frame {
+4439                Frame::Padding | Frame::Ping => {}
+4440                Frame::Crypto(frame) => {
+4441                    self.read_crypto(packet.header.space(), &frame, payload_len)?;
+4442                }
+4443                Frame::Ack(ack) => {
+4444                    self.on_ack_received(now, packet.header.space(), ack)?;
+4445                }
+4446                Frame::PathAck(ack) => {
+4447                    span.as_ref()
+4448                        .map(|span| span.record("path", tracing::field::debug(&ack.path_id)));
+4449                    self.on_path_ack_received(now, packet.header.space(), ack)?;
+4450                }
+4451                Frame::Close(reason) => {
+4452                    self.state.move_to_draining(Some(reason.into()));
+4453                    return Ok(());
+4454                }
+4455                _ => {
+4456                    let mut err =
+4457                        TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake");
+4458                    err.frame = frame::MaybeFrame::Known(frame.ty());
+4459                    return Err(err);
+4460                }
+4461            }
+4462        }
+4463
+4464        if ack_eliciting {
+4465            // In the initial and handshake spaces, ACKs must be sent immediately
+4466            self.spaces[packet.header.space()]
+4467                .for_path(path_id)
+4468                .pending_acks
+4469                .set_immediate_ack_required();
+4470        }
+4471
+4472        self.write_crypto();
+4473        Ok(())
+4474    }
+4475
+4476    /// Processes the packet payload, always in the data space.
+4477    fn process_payload(
+4478        &mut self,
+4479        now: Instant,
+4480        network_path: FourTuple,
+4481        path_id: PathId,
+4482        number: u64,
+4483        packet: Packet,
+4484        #[allow(unused)] qlog: &mut QlogRecvPacket,
+4485    ) -> Result<(), TransportError> {
+4486        let payload = packet.payload.freeze();
+4487        let mut is_probing_packet = true;
+4488        let mut close = None;
+4489        let payload_len = payload.len();
+4490        let mut ack_eliciting = false;
+4491        // if this packet triggers a path migration and includes a observed address frame, it's
+4492        // stored here
+4493        let mut migration_observed_addr = None;
+4494        for result in frame::Iter::new(payload)? {
+4495            let frame = result?;
+4496            qlog.frame(&frame);
+4497            let span = match frame {
+4498                Frame::Padding => continue,
+4499                _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty),
+4500            };
+4501
+4502            self.stats.frame_rx.record(frame.ty());
+4503            // Crypto, Stream and Datagram frames are special cased in order no pollute
+4504            // the log with payload data
+4505            match &frame {
+4506                Frame::Crypto(f) => {
+4507                    trace!(offset = f.offset, len = f.data.len(), "got frame CRYPTO");
+4508                }
+4509                Frame::Stream(f) => {
+4510                    trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got frame STREAM");
+4511                }
+4512                Frame::Datagram(f) => {
+4513                    trace!(len = f.data.len(), "got frame DATAGRAM");
+4514                }
+4515                f => {
+4516                    trace!("got frame {f}");
+4517                }
+4518            }
+4519
+4520            let _guard = span.enter();
+4521            if packet.header.is_0rtt() {
+4522                match frame {
+4523                    Frame::Crypto(_) | Frame::Close(Close::Application(_)) => {
+4524                        return Err(TransportError::PROTOCOL_VIOLATION(
+4525                            "illegal frame type in 0-RTT",
+4526                        ));
+4527                    }
+4528                    _ => {
+4529                        if frame.is_1rtt() {
+4530                            return Err(TransportError::PROTOCOL_VIOLATION(
+4531                                "illegal frame type in 0-RTT",
+4532                            ));
+4533                        }
+4534                    }
+4535                }
+4536            }
+4537            ack_eliciting |= frame.is_ack_eliciting();
+4538
+4539            // Check whether this could be a probing packet
+4540            match frame {
+4541                Frame::Padding
+4542                | Frame::PathChallenge(_)
+4543                | Frame::PathResponse(_)
+4544                | Frame::NewConnectionId(_)
+4545                | Frame::ObservedAddr(_) => {}
+4546                _ => {
+4547                    is_probing_packet = false;
+4548                }
+4549            }
+4550
+4551            match frame {
+4552                Frame::Crypto(frame) => {
+4553                    self.read_crypto(SpaceId::Data, &frame, payload_len)?;
+4554                }
+4555                Frame::Stream(frame) => {
+4556                    if self.streams.received(frame, payload_len)?.should_transmit() {
+4557                        self.spaces[SpaceId::Data].pending.max_data = true;
+4558                    }
+4559                }
+4560                Frame::Ack(ack) => {
+4561                    self.on_ack_received(now, SpaceId::Data, ack)?;
+4562                }
+4563                Frame::PathAck(ack) => {
+4564                    span.record("path", tracing::field::debug(&ack.path_id));
+4565                    self.on_path_ack_received(now, SpaceId::Data, ack)?;
+4566                }
+4567                Frame::Padding | Frame::Ping => {}
+4568                Frame::Close(reason) => {
+4569                    close = Some(reason);
+4570                }
+4571                Frame::PathChallenge(challenge) => {
+4572                    let path = &mut self
+4573                        .path_mut(path_id)
+4574                        .expect("payload is processed only after the path becomes known");
+4575                    path.path_responses.push(number, challenge.0, network_path);
+4576                    // At this point, update_network_path_or_discard was already called, so
+4577                    // we don't need to be lenient about `local_ip` possibly mis-matching.
+4578                    if network_path == path.network_path {
+4579                        // PATH_CHALLENGE on active path, possible off-path packet forwarding
+4580                        // attack. Send a non-probing packet to recover the active path.
+4581                        // TODO(flub): No longer true! We now path_challege also to validate
+4582                        //    the path if the path is new, without an RFC9000-style
+4583                        //    migration involved. This means we add in an extra
+4584                        //    IMMEDIATE_ACK on some challenges. It isn't really wrong to do
+4585                        //    so, but it still is something untidy. We should instead
+4586                        //    suppress this when we know the remote is still validating the
+4587                        //    path.
+4588                        match self.peer_supports_ack_frequency() {
+4589                            true => self.immediate_ack(path_id),
+4590                            false => {
+4591                                self.ping_path(path_id).ok();
+4592                            }
+4593                        }
+4594                    }
+4595                }
+4596                Frame::PathResponse(response) => {
+4597                    let path = self
+4598                        .paths
+4599                        .get_mut(&path_id)
+4600                        .expect("payload is processed only after the path becomes known");
+4601
+4602                    use PathTimer::*;
+4603                    use paths::OnPathResponseReceived::*;
+4604                    match path
+4605                        .data
+4606                        .on_path_response_received(now, response.0, network_path)
+4607                    {
+4608                        OnPath { was_open } => {
+4609                            let qlog = self.qlog.with_time(now);
+4610
+4611                            self.timers
+4612                                .stop(Timer::PerPath(path_id, PathValidation), qlog.clone());
+4613                            self.timers
+4614                                .stop(Timer::PerPath(path_id, PathOpen), qlog.clone());
+4615
+4616                            let next_challenge = path
+4617                                .data
+4618                                .earliest_expiring_challenge()
+4619                                .map(|time| time + self.ack_frequency.max_ack_delay_for_pto());
+4620                            self.timers.set_or_stop(
+4621                                Timer::PerPath(path_id, PathChallengeLost),
+4622                                next_challenge,
+4623                                qlog,
+4624                            );
+4625
+4626                            if !was_open {
+4627                                self.events
+4628                                    .push_back(Event::Path(PathEvent::Opened { id: path_id }));
+4629                                if let Some(observed) = path.data.last_observed_addr_report.as_ref()
+4630                                {
+4631                                    self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+4632                                        id: path_id,
+4633                                        addr: observed.socket_addr(),
+4634                                    }));
+4635                                }
+4636                            }
+4637                            if let Some((_, ref mut prev)) = path.prev {
+4638                                prev.challenges_sent.clear();
+4639                                prev.send_new_challenge = false;
+4640                            }
+4641                        }
+4642                        OffPath => {
+4643                            debug!("Response to off-path PathChallenge!");
+4644                            let next_challenge = path
+4645                                .data
+4646                                .earliest_expiring_challenge()
+4647                                .map(|time| time + self.ack_frequency.max_ack_delay_for_pto());
+4648                            self.timers.set_or_stop(
+4649                                Timer::PerPath(path_id, PathChallengeLost),
+4650                                next_challenge,
+4651                                self.qlog.with_time(now),
+4652                            );
+4653                        }
+4654                        Invalid { expected } => {
+4655                            debug!(%response, %network_path, %expected, "ignoring invalid PATH_RESPONSE")
+4656                        }
+4657                        Unknown => debug!(%response, "ignoring invalid PATH_RESPONSE"),
+4658                    }
+4659                }
+4660                Frame::MaxData(frame::MaxData(bytes)) => {
+4661                    self.streams.received_max_data(bytes);
+4662                }
+4663                Frame::MaxStreamData(frame::MaxStreamData { id, offset }) => {
+4664                    self.streams.received_max_stream_data(id, offset)?;
+4665                }
+4666                Frame::MaxStreams(frame::MaxStreams { dir, count }) => {
+4667                    self.streams.received_max_streams(dir, count)?;
+4668                }
+4669                Frame::ResetStream(frame) => {
+4670                    if self.streams.received_reset(frame)?.should_transmit() {
+4671                        self.spaces[SpaceId::Data].pending.max_data = true;
+4672                    }
+4673                }
+4674                Frame::DataBlocked(DataBlocked(offset)) => {
+4675                    debug!(offset, "peer claims to be blocked at connection level");
+4676                }
+4677                Frame::StreamDataBlocked(StreamDataBlocked { id, offset }) => {
+4678                    if id.initiator() == self.side.side() && id.dir() == Dir::Uni {
+4679                        debug!("got STREAM_DATA_BLOCKED on send-only {}", id);
+4680                        return Err(TransportError::STREAM_STATE_ERROR(
+4681                            "STREAM_DATA_BLOCKED on send-only stream",
+4682                        ));
+4683                    }
+4684                    debug!(
+4685                        stream = %id,
+4686                        offset, "peer claims to be blocked at stream level"
+4687                    );
+4688                }
+4689                Frame::StreamsBlocked(StreamsBlocked { dir, limit }) => {
+4690                    if limit > MAX_STREAM_COUNT {
+4691                        return Err(TransportError::FRAME_ENCODING_ERROR(
+4692                            "unrepresentable stream limit",
+4693                        ));
+4694                    }
+4695                    debug!(
+4696                        "peer claims to be blocked opening more than {} {} streams",
+4697                        limit, dir
+4698                    );
+4699                }
+4700                Frame::StopSending(frame::StopSending { id, error_code }) => {
+4701                    if id.initiator() != self.side.side() {
+4702                        if id.dir() == Dir::Uni {
+4703                            debug!("got STOP_SENDING on recv-only {}", id);
+4704                            return Err(TransportError::STREAM_STATE_ERROR(
+4705                                "STOP_SENDING on recv-only stream",
+4706                            ));
+4707                        }
+4708                    } else if self.streams.is_local_unopened(id) {
+4709                        return Err(TransportError::STREAM_STATE_ERROR(
+4710                            "STOP_SENDING on unopened stream",
+4711                        ));
+4712                    }
+4713                    self.streams.received_stop_sending(id, error_code);
+4714                }
+4715                Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => {
+4716                    if let Some(ref path_id) = path_id {
+4717                        span.record("path", tracing::field::debug(&path_id));
+4718                    }
+4719                    let path_id = path_id.unwrap_or_default();
+4720                    match self.local_cid_state.get_mut(&path_id) {
+4721                        None => error!(?path_id, "RETIRE_CONNECTION_ID for unknown path"),
+4722                        Some(cid_state) => {
+4723                            let allow_more_cids = cid_state
+4724                                .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?;
+4725
+4726                            // If the path has closed, we do not issue more CIDs for this path
+4727                            // For details see  https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2
+4728                            // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames.
+4729                            let has_path = !self.abandoned_paths.contains(&path_id);
+4730                            let allow_more_cids = allow_more_cids && has_path;
+4731
+4732                            self.endpoint_events
+4733                                .push_back(EndpointEventInner::RetireConnectionId(
+4734                                    now,
+4735                                    path_id,
+4736                                    sequence,
+4737                                    allow_more_cids,
+4738                                ));
+4739                        }
+4740                    }
+4741                }
+4742                Frame::NewConnectionId(frame) => {
+4743                    let path_id = if let Some(path_id) = frame.path_id {
+4744                        if !self.is_multipath_negotiated() {
+4745                            return Err(TransportError::PROTOCOL_VIOLATION(
+4746                                "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated",
+4747                            ));
+4748                        }
+4749                        if path_id > self.local_max_path_id {
+4750                            return Err(TransportError::PROTOCOL_VIOLATION(
+4751                                "PATH_NEW_CONNECTION_ID contains path_id exceeding current max",
+4752                            ));
+4753                        }
+4754                        path_id
+4755                    } else {
+4756                        PathId::ZERO
+4757                    };
+4758
+4759                    if self.abandoned_paths.contains(&path_id) {
+4760                        trace!("ignoring issued CID for abandoned path");
+4761                        continue;
+4762                    }
+4763                    if let Some(ref path_id) = frame.path_id {
+4764                        span.record("path", tracing::field::debug(&path_id));
+4765                    }
+4766                    let remote_cids = self
+4767                        .remote_cids
+4768                        .entry(path_id)
+4769                        .or_insert_with(|| CidQueue::new(frame.id));
+4770                    if remote_cids.active().is_empty() {
+4771                        return Err(TransportError::PROTOCOL_VIOLATION(
+4772                            "NEW_CONNECTION_ID when CIDs aren't in use",
+4773                        ));
+4774                    }
+4775                    if frame.retire_prior_to > frame.sequence {
+4776                        return Err(TransportError::PROTOCOL_VIOLATION(
+4777                            "NEW_CONNECTION_ID retiring unissued CIDs",
+4778                        ));
+4779                    }
+4780
+4781                    use crate::cid_queue::InsertError;
+4782                    match remote_cids.insert(frame) {
+4783                        Ok(None) if self.path(path_id).is_none() => {
+4784                            // if this gives us CIDs to open a new path and a nat traversal attempt
+4785                            // is underway we could try to probe a pending remote
+4786                            self.continue_nat_traversal_round(now);
+4787                        }
+4788                        Ok(None) => {}
+4789                        Ok(Some((retired, reset_token))) => {
+4790                            let pending_retired =
+4791                                &mut self.spaces[SpaceId::Data].pending.retire_cids;
+4792                            /// Ensure `pending_retired` cannot grow without bound. Limit is
+4793                            /// somewhat arbitrary but very permissive.
+4794                            const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10;
+4795                            // We don't bother counting in-flight frames because those are bounded
+4796                            // by congestion control.
+4797                            if (pending_retired.len() as u64)
+4798                                .saturating_add(retired.end.saturating_sub(retired.start))
+4799                                > MAX_PENDING_RETIRED_CIDS
+4800                            {
+4801                                return Err(TransportError::CONNECTION_ID_LIMIT_ERROR(
+4802                                    "queued too many retired CIDs",
+4803                                ));
+4804                            }
+4805                            pending_retired.extend(retired.map(|seq| (path_id, seq)));
+4806                            // TODO(matheus23): Reset token for a remote or a full 4-tuple?
+4807                            self.set_reset_token(path_id, network_path.remote, reset_token);
+4808                        }
+4809                        Err(InsertError::ExceedsLimit) => {
+4810                            return Err(TransportError::CONNECTION_ID_LIMIT_ERROR(""));
+4811                        }
+4812                        Err(InsertError::Retired) => {
+4813                            trace!("discarding already-retired");
+4814                            // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a
+4815                            // range of connection IDs larger than the active connection ID limit
+4816                            // was retired all at once via retire_prior_to.
+4817                            self.spaces[SpaceId::Data]
+4818                                .pending
+4819                                .retire_cids
+4820                                .push((path_id, frame.sequence));
+4821                            continue;
+4822                        }
+4823                    };
+4824
+4825                    if self.side.is_server()
+4826                        && path_id == PathId::ZERO
+4827                        && self
+4828                            .remote_cids
+4829                            .get(&PathId::ZERO)
+4830                            .map(|cids| cids.active_seq() == 0)
+4831                            .unwrap_or_default()
+4832                    {
+4833                        // We're a server still using the initial remote CID for the client, so
+4834                        // let's switch immediately to enable clientside stateless resets.
+4835                        self.update_remote_cid(PathId::ZERO);
+4836                    }
+4837                }
+4838                Frame::NewToken(NewToken { token }) => {
+4839                    let ConnectionSide::Client {
+4840                        token_store,
+4841                        server_name,
+4842                        ..
+4843                    } = &self.side
+4844                    else {
+4845                        return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN"));
+4846                    };
+4847                    if token.is_empty() {
+4848                        return Err(TransportError::FRAME_ENCODING_ERROR("empty token"));
+4849                    }
+4850                    trace!("got new token");
+4851                    token_store.insert(server_name, token);
+4852                }
+4853                Frame::Datagram(datagram) => {
+4854                    if self
+4855                        .datagrams
+4856                        .received(datagram, &self.config.datagram_receive_buffer_size)?
+4857                    {
+4858                        self.events.push_back(Event::DatagramReceived);
+4859                    }
+4860                }
+4861                Frame::AckFrequency(ack_frequency) => {
+4862                    // This frame can only be sent in the Data space
+4863
+4864                    if !self.ack_frequency.ack_frequency_received(&ack_frequency)? {
+4865                        // The AckFrequency frame is stale (we have already received a more
+4866                        // recent one)
+4867                        continue;
+4868                    }
+4869
+4870                    // Update the params for all of our paths
+4871                    for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() {
+4872                        space.pending_acks.set_ack_frequency_params(&ack_frequency);
+4873
+4874                        // Our `max_ack_delay` has been updated, so we may need to adjust
+4875                        // its associated timeout
+4876                        if let Some(timeout) = space
+4877                            .pending_acks
+4878                            .max_ack_delay_timeout(self.ack_frequency.max_ack_delay)
+4879                        {
+4880                            self.timers.set(
+4881                                Timer::PerPath(*path_id, PathTimer::MaxAckDelay),
+4882                                timeout,
+4883                                self.qlog.with_time(now),
+4884                            );
+4885                        }
+4886                    }
+4887                }
+4888                Frame::ImmediateAck => {
+4889                    // This frame can only be sent in the Data space
+4890                    for pns in self.spaces[SpaceId::Data].iter_paths_mut() {
+4891                        pns.pending_acks.set_immediate_ack_required();
+4892                    }
+4893                }
+4894                Frame::HandshakeDone => {
+4895                    if self.side.is_server() {
+4896                        return Err(TransportError::PROTOCOL_VIOLATION(
+4897                            "client sent HANDSHAKE_DONE",
+4898                        ));
+4899                    }
+4900                    if self.spaces[SpaceId::Handshake].crypto.is_some() {
+4901                        self.discard_space(now, SpaceId::Handshake);
+4902                    }
+4903                    self.events.push_back(Event::HandshakeConfirmed);
+4904                    trace!("handshake confirmed");
+4905                }
+4906                Frame::ObservedAddr(observed) => {
+4907                    // check if params allows the peer to send report and this node to receive it
+4908                    trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port);
+4909                    if !self
+4910                        .peer_params
+4911                        .address_discovery_role
+4912                        .should_report(&self.config.address_discovery_role)
+4913                    {
+4914                        return Err(TransportError::PROTOCOL_VIOLATION(
+4915                            "received OBSERVED_ADDRESS frame when not negotiated",
+4916                        ));
+4917                    }
+4918                    // must only be sent in data space
+4919                    if packet.header.space() != SpaceId::Data {
+4920                        return Err(TransportError::PROTOCOL_VIOLATION(
+4921                            "OBSERVED_ADDRESS frame outside data space",
+4922                        ));
+4923                    }
+4924
+4925                    let path = self.path_data_mut(path_id);
+4926                    if network_path == path.network_path {
+4927                        if let Some(updated) = path.update_observed_addr_report(observed)
+4928                            && path.open
+4929                        {
+4930                            self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+4931                                id: path_id,
+4932                                addr: updated,
+4933                            }));
+4934                            // otherwise the event is reported when the path is deemed open
+4935                        }
+4936                    } else {
+4937                        // include in migration
+4938                        migration_observed_addr = Some(observed)
+4939                    }
+4940                }
+4941                Frame::PathAbandon(frame::PathAbandon {
+4942                    path_id,
+4943                    error_code,
+4944                }) => {
+4945                    span.record("path", tracing::field::debug(&path_id));
+4946                    // TODO(flub): don't really know which error code to use here.
+4947                    match self.close_path(now, path_id, error_code.into()) {
+4948                        Ok(()) => {
+4949                            trace!("peer abandoned path");
+4950                        }
+4951                        Err(ClosePathError::LastOpenPath) => {
+4952                            trace!("peer abandoned last path, closing connection");
+4953                            return Err(TransportError::NO_VIABLE_PATH(
+4954                                "last path abandoned by peer",
+4955                            ));
+4956                        }
+4957                        Err(ClosePathError::ClosedPath) => {
+4958                            trace!("peer abandoned already closed path");
+4959                        }
+4960                    };
+4961                    // If we receive a retransmit of PATH_ABANDON then we may already have
+4962                    // abandoned this path locally.  In that case the DiscardPath timer
+4963                    // may already have fired and we no longer have any state for this path.
+4964                    // Only set this timer if we still have path state.
+4965                    if let Some(path) = self.paths.get_mut(&path_id)
+4966                        && !matches!(path.data.abandon_state, AbandonState::ReceivedPathAbandon)
+4967                    {
+4968                        let ack_delay = self.ack_frequency.max_ack_delay_for_pto();
+4969                        let pto = path.data.rtt.pto_base() + ack_delay;
+4970                        self.timers.set(
+4971                            Timer::PerPath(path_id, PathTimer::DiscardPath),
+4972                            now + 3 * pto,
+4973                            self.qlog.with_time(now),
+4974                        );
+4975                        // We received a PATH_ABANDON, we don't expect another one by a certain time.
+4976                        path.data.abandon_state = AbandonState::ReceivedPathAbandon;
+4977                    }
+4978                }
+4979                Frame::PathStatusAvailable(info) => {
+4980                    span.record("path", tracing::field::debug(&info.path_id));
+4981                    if self.is_multipath_negotiated() {
+4982                        self.on_path_status(
+4983                            info.path_id,
+4984                            PathStatus::Available,
+4985                            info.status_seq_no,
+4986                        );
+4987                    } else {
+4988                        return Err(TransportError::PROTOCOL_VIOLATION(
+4989                            "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated",
+4990                        ));
+4991                    }
+4992                }
+4993                Frame::PathStatusBackup(info) => {
+4994                    span.record("path", tracing::field::debug(&info.path_id));
+4995                    if self.is_multipath_negotiated() {
+4996                        self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no);
+4997                    } else {
+4998                        return Err(TransportError::PROTOCOL_VIOLATION(
+4999                            "received PATH_STATUS_BACKUP frame when multipath was not negotiated",
+5000                        ));
+5001                    }
+5002                }
+5003                Frame::MaxPathId(frame::MaxPathId(path_id)) => {
+5004                    span.record("path", tracing::field::debug(&path_id));
+5005                    if !self.is_multipath_negotiated() {
+5006                        return Err(TransportError::PROTOCOL_VIOLATION(
+5007                            "received MAX_PATH_ID frame when multipath was not negotiated",
+5008                        ));
+5009                    }
+5010                    // frames that do not increase the path id are ignored
+5011                    if path_id > self.remote_max_path_id {
+5012                        self.remote_max_path_id = path_id;
+5013                        self.issue_first_path_cids(now);
+5014                        while let Some(true) = self.continue_nat_traversal_round(now) {}
+5015                    }
+5016                }
+5017                Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => {
+5018                    // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST
+5019                    // be treated as a connection error of type PROTOCOL_VIOLATION.
+5020                    // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids>
+5021                    if self.is_multipath_negotiated() {
+5022                        if max_path_id > self.local_max_path_id {
+5023                            return Err(TransportError::PROTOCOL_VIOLATION(
+5024                                "PATHS_BLOCKED maximum path identifier was larger than local maximum",
+5025                            ));
+5026                        }
+5027                        debug!("received PATHS_BLOCKED({:?})", max_path_id);
+5028                        // TODO(@divma): ensure max concurrent paths
+5029                    } else {
+5030                        return Err(TransportError::PROTOCOL_VIOLATION(
+5031                            "received PATHS_BLOCKED frame when not multipath was not negotiated",
+5032                        ));
+5033                    }
+5034                }
+5035                Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => {
+5036                    // Nothing to do.  This is recorded in the frame stats, but otherwise we
+5037                    // always issue all CIDs we're allowed to issue, so either this is an
+5038                    // impatient peer or a bug on our side.
+5039
+5040                    // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST
+5041                    // be treated as a connection error of type PROTOCOL_VIOLATION.
+5042                    // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids>
+5043                    if self.is_multipath_negotiated() {
+5044                        if path_id > self.local_max_path_id {
+5045                            return Err(TransportError::PROTOCOL_VIOLATION(
+5046                                "PATH_CIDS_BLOCKED path identifier was larger than local maximum",
+5047                            ));
+5048                        }
+5049                        if next_seq.0
+5050                            > self
+5051                                .local_cid_state
+5052                                .get(&path_id)
+5053                                .map(|cid_state| cid_state.active_seq().1 + 1)
+5054                                .unwrap_or_default()
+5055                        {
+5056                            return Err(TransportError::PROTOCOL_VIOLATION(
+5057                                "PATH_CIDS_BLOCKED next sequence number larger than in local state",
+5058                            ));
+5059                        }
+5060                        debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED");
+5061                    } else {
+5062                        return Err(TransportError::PROTOCOL_VIOLATION(
+5063                            "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated",
+5064                        ));
+5065                    }
+5066                }
+5067                Frame::AddAddress(addr) => {
+5068                    let client_state = match self.iroh_hp.client_side_mut() {
+5069                        Ok(state) => state,
+5070                        Err(err) => {
+5071                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+5072                                "Nat traversal(ADD_ADDRESS): {err}"
+5073                            )));
+5074                        }
+5075                    };
+5076
+5077                    if !client_state.check_remote_address(&addr) {
+5078                        // if the address is not valid we flag it, but update anyway
+5079                        warn!(?addr, "server sent illegal ADD_ADDRESS frame");
+5080                    }
+5081
+5082                    match client_state.add_remote_address(addr) {
+5083                        Ok(maybe_added) => {
+5084                            if let Some(added) = maybe_added {
+5085                                self.events.push_back(Event::NatTraversal(
+5086                                    iroh_hp::Event::AddressAdded(added),
+5087                                ));
+5088                            }
+5089                        }
+5090                        Err(e) => {
+5091                            warn!(%e, "failed to add remote address")
+5092                        }
+5093                    }
+5094                }
+5095                Frame::RemoveAddress(addr) => {
+5096                    let client_state = match self.iroh_hp.client_side_mut() {
+5097                        Ok(state) => state,
+5098                        Err(err) => {
+5099                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+5100                                "Nat traversal(REMOVE_ADDRESS): {err}"
+5101                            )));
+5102                        }
+5103                    };
+5104                    if let Some(removed_addr) = client_state.remove_remote_address(addr) {
+5105                        self.events
+5106                            .push_back(Event::NatTraversal(iroh_hp::Event::AddressRemoved(
+5107                                removed_addr,
+5108                            )));
+5109                    }
+5110                }
+5111                Frame::ReachOut(reach_out) => {
+5112                    let ipv6 = self.is_ipv6();
+5113                    let server_state = match self.iroh_hp.server_side_mut() {
+5114                        Ok(state) => state,
+5115                        Err(err) => {
+5116                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+5117                                "Nat traversal(REACH_OUT): {err}"
+5118                            )));
+5119                        }
+5120                    };
+5121
+5122                    if let Err(err) = server_state.handle_reach_out(reach_out, ipv6) {
+5123                        return Err(TransportError::PROTOCOL_VIOLATION(format!(
+5124                            "Nat traversal(REACH_OUT): {err}"
+5125                        )));
+5126                    }
+5127                }
+5128            }
+5129        }
+5130
+5131        let space = self.spaces[SpaceId::Data].for_path(path_id);
+5132        if space
+5133            .pending_acks
+5134            .packet_received(now, number, ack_eliciting, &space.dedup)
+5135        {
+5136            if self.abandoned_paths.contains(&path_id) {
+5137                // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from
+5138                // abandoned paths.
+5139                space.pending_acks.set_immediate_ack_required();
+5140            } else {
+5141                self.timers.set(
+5142                    Timer::PerPath(path_id, PathTimer::MaxAckDelay),
+5143                    now + self.ack_frequency.max_ack_delay,
+5144                    self.qlog.with_time(now),
+5145                );
+5146            }
+5147        }
+5148
+5149        // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets
+5150        // on stopped streams. Incoming finishes/resets on open streams are not handled here as they
+5151        // are only freed, and hence only issue credit, once the application has been notified
+5152        // during a read on the stream.
+5153        let pending = &mut self.spaces[SpaceId::Data].pending;
+5154        self.streams.queue_max_stream_id(pending);
+5155
+5156        if let Some(reason) = close {
+5157            self.state.move_to_draining(Some(reason.into()));
+5158            self.connection_close_pending = true;
+5159        }
+5160
+5161        if Some(number) == self.spaces[SpaceId::Data].for_path(path_id).rx_packet
+5162            && !is_probing_packet
+5163            && network_path != self.path_data(path_id).network_path
+5164        {
+5165            let ConnectionSide::Server { ref server_config } = self.side else {
+5166                panic!("packets from unknown remote should be dropped by clients");
+5167            };
+5168            debug_assert!(
+5169                server_config.migration,
+5170                "migration-initiating packets should have been dropped immediately"
+5171            );
+5172            self.migrate(path_id, now, network_path, migration_observed_addr);
+5173            // Break linkability, if possible
+5174            self.update_remote_cid(path_id);
+5175            self.spin = false;
+5176        }
+5177
+5178        Ok(())
+5179    }
+5180
+5181    fn migrate(
+5182        &mut self,
+5183        path_id: PathId,
+5184        now: Instant,
+5185        network_path: FourTuple,
+5186        observed_addr: Option<ObservedAddr>,
+5187    ) {
+5188        trace!(%network_path, %path_id, "migration initiated");
+5189        self.path_generation_counter = self.path_generation_counter.wrapping_add(1);
+5190        // TODO(@divma): conditions for path migration in multipath are very specific, check them
+5191        // again to prevent path migrations that should actually create a new path
+5192
+5193        // Reset rtt/congestion state for new path unless it looks like a NAT rebinding.
+5194        // Note that the congestion window will not grow until validation terminates. Helps mitigate
+5195        // amplification attacks performed by spoofing source addresses.
+5196        let prev_pto = self.pto(SpaceId::Data, path_id);
+5197        let path = self.paths.get_mut(&path_id).expect("known path");
+5198        let mut new_path_data = if network_path.remote.is_ipv4()
+5199            && network_path.remote.ip() == path.data.network_path.remote.ip()
+5200        {
+5201            PathData::from_previous(network_path, &path.data, self.path_generation_counter, now)
+5202        } else {
+5203            let peer_max_udp_payload_size =
+5204                u16::try_from(self.peer_params.max_udp_payload_size.into_inner())
+5205                    .unwrap_or(u16::MAX);
+5206            PathData::new(
+5207                network_path,
+5208                self.allow_mtud,
+5209                Some(peer_max_udp_payload_size),
+5210                self.path_generation_counter,
+5211                now,
+5212                &self.config,
+5213            )
+5214        };
+5215        new_path_data.last_observed_addr_report = path.data.last_observed_addr_report.clone();
+5216        if let Some(report) = observed_addr
+5217            && let Some(updated) = new_path_data.update_observed_addr_report(report)
+5218        {
+5219            tracing::info!("adding observed addr event from migration");
+5220            self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+5221                id: path_id,
+5222                addr: updated,
+5223            }));
+5224        }
+5225        new_path_data.send_new_challenge = true;
+5226
+5227        let mut prev_path_data = mem::replace(&mut path.data, new_path_data);
+5228
+5229        // Only store this as previous path if it was validated. For all we know there could
+5230        // already be a previous path stored which might have been validated in the past,
+5231        // which is more valuable than one that's not yet validated.
+5232        //
+5233        // With multipath it is possible that there are no remote CIDs for the path ID
+5234        // yet. In this case we would never have sent on this path yet and would not be able
+5235        // to send a PATH_CHALLENGE either, which is currently a fire-and-forget affair
+5236        // anyway. So don't store such a path either.
+5237        if !prev_path_data.validated
+5238            && let Some(cid) = self.remote_cids.get(&path_id).map(CidQueue::active)
+5239        {
+5240            prev_path_data.send_new_challenge = true;
+5241            // We haven't updated the remote CID yet, this captures the remote CID we were using on
+5242            // the previous path.
+5243            path.prev = Some((cid, prev_path_data));
+5244        }
+5245
+5246        // We need to re-assign the correct remote to this path in qlog
+5247        self.qlog.emit_tuple_assigned(path_id, network_path, now);
+5248
+5249        self.timers.set(
+5250            Timer::PerPath(path_id, PathTimer::PathValidation),
+5251            now + 3 * cmp::max(self.pto(SpaceId::Data, path_id), prev_pto),
+5252            self.qlog.with_time(now),
+5253        );
+5254    }
+5255
+5256    /// Handle a change in the local address, i.e. an active migration
+5257    pub fn local_address_changed(&mut self) {
+5258        // TODO(flub): if multipath is enabled this needs to create a new path entirely.
+5259        self.update_remote_cid(PathId::ZERO);
+5260        self.ping();
+5261    }
+5262
+5263    /// Switch to a previously unused remote connection ID, if possible
+5264    fn update_remote_cid(&mut self, path_id: PathId) {
+5265        let Some((reset_token, retired)) = self
+5266            .remote_cids
+5267            .get_mut(&path_id)
+5268            .and_then(|cids| cids.next())
+5269        else {
+5270            return;
+5271        };
+5272
+5273        // Retire the current remote CID and any CIDs we had to skip.
+5274        self.spaces[SpaceId::Data]
+5275            .pending
+5276            .retire_cids
+5277            .extend(retired.map(|seq| (path_id, seq)));
+5278        let remote = self.path_data(path_id).network_path.remote;
+5279        self.set_reset_token(path_id, remote, reset_token);
+5280    }
+5281
+5282    /// Sends this reset token to the endpoint
+5283    ///
+5284    /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer
+5285    /// sends a reset token it knows to route it to this connection. See RFC 9000 section
+5286    /// 10.3. Stateless Reset.
+5287    ///
+5288    /// Reset tokens are different for each path, the endpoint identifies paths by peer
+5289    /// socket address however, not by path ID.
+5290    fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) {
+5291        self.endpoint_events
+5292            .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token));
+5293
+5294        // During the handshake the server sends a reset token in the transport
+5295        // parameters. When we are the client and we receive the reset token during the
+5296        // handshake we want this to affect our peer transport parameters.
+5297        // TODO(flub): Pretty sure this is pointless, the entire params is overwritten
+5298        //    shortly after this was called.  And then the params don't have this anymore.
+5299        if path_id == PathId::ZERO {
+5300            self.peer_params.stateless_reset_token = Some(reset_token);
+5301        }
+5302    }
+5303
+5304    /// Issue an initial set of connection IDs to the peer upon connection
+5305    fn issue_first_cids(&mut self, now: Instant) {
+5306        if self
+5307            .local_cid_state
+5308            .get(&PathId::ZERO)
+5309            .expect("PathId::ZERO exists when the connection is created")
+5310            .cid_len()
+5311            == 0
+5312        {
+5313            return;
+5314        }
+5315
+5316        // Subtract 1 to account for the CID we supplied while handshaking
+5317        let mut n = self.peer_params.issue_cids_limit() - 1;
+5318        if let ConnectionSide::Server { server_config } = &self.side
+5319            && server_config.has_preferred_address()
+5320        {
+5321            // We also sent a CID in the transport parameters
+5322            n -= 1;
+5323        }
+5324        self.endpoint_events
+5325            .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n));
+5326    }
+5327
+5328    /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued
+5329    ///
+5330    /// Later CIDs are issued when CIDs expire or are retired by the peer.
+5331    fn issue_first_path_cids(&mut self, now: Instant) {
+5332        if let Some(max_path_id) = self.max_path_id() {
+5333            let mut path_id = self.max_path_id_with_cids.next();
+5334            while path_id <= max_path_id {
+5335                self.endpoint_events
+5336                    .push_back(EndpointEventInner::NeedIdentifiers(
+5337                        path_id,
+5338                        now,
+5339                        self.peer_params.issue_cids_limit(),
+5340                    ));
+5341                path_id = path_id.next();
+5342            }
+5343            self.max_path_id_with_cids = max_path_id;
+5344        }
+5345    }
+5346
+5347    /// Populates a packet with frames
+5348    ///
+5349    /// This tries to fit as many frames as possible into the packet.
+5350    ///
+5351    /// *path_exclusive_only* means to only build frames which can only be sent on this
+5352    /// *path.  This is used in multipath for backup paths while there is still an active
+5353    /// *path.
+5354    fn populate_packet<'a, 'b>(
+5355        &mut self,
+5356        now: Instant,
+5357        space_id: SpaceId,
+5358        path_id: PathId,
+5359        path_exclusive_only: bool,
+5360        builder: &mut PacketBuilder<'a, 'b>,
+5361    ) {
+5362        let pn = builder.packet_number;
+5363        let is_multipath_negotiated = self.is_multipath_negotiated();
+5364        let stats = &mut self.stats.frame_tx;
+5365        let space = &mut self.spaces[space_id];
+5366        let path = &mut self.paths.get_mut(&path_id).expect("known path").data;
+5367        let is_0rtt = space_id == SpaceId::Data && space.crypto.is_none();
+5368        space
+5369            .for_path(path_id)
+5370            .pending_acks
+5371            .maybe_ack_non_eliciting();
+5372
+5373        // HANDSHAKE_DONE
+5374        if !is_0rtt
+5375            && !path_exclusive_only
+5376            && mem::replace(&mut space.pending.handshake_done, false)
+5377        {
+5378            builder.write_frame(frame::HandshakeDone, stats);
+5379        }
+5380
+5381        // REACH_OUT
+5382        if let Some((round, addresses)) = space.pending.reach_out.as_mut()
+5383            && !path_exclusive_only
+5384        {
+5385            while let Some(local_addr) = addresses.pop() {
+5386                let reach_out = frame::ReachOut::new(*round, local_addr);
+5387                if builder.frame_space_remaining() > reach_out.size() {
+5388                    builder.write_frame(reach_out, stats);
+5389                } else {
+5390                    addresses.push(local_addr);
+5391                    break;
+5392                }
+5393            }
+5394            if addresses.is_empty() {
+5395                space.pending.reach_out = None;
+5396            }
+5397        }
+5398
+5399        // OBSERVED_ADDR
+5400        if !path_exclusive_only
+5401            && space_id == SpaceId::Data
+5402            && self
+5403                .config
+5404                .address_discovery_role
+5405                .should_report(&self.peer_params.address_discovery_role)
+5406            && (!path.observed_addr_sent || space.pending.observed_addr)
+5407        {
+5408            let frame =
+5409                frame::ObservedAddr::new(path.network_path.remote, self.next_observed_addr_seq_no);
+5410            if builder.frame_space_remaining() > frame.size() {
+5411                builder.write_frame(frame, stats);
+5412
+5413                self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8);
+5414                path.observed_addr_sent = true;
+5415
+5416                space.pending.observed_addr = false;
+5417            }
+5418        }
+5419
+5420        // PING
+5421        if mem::replace(&mut space.for_path(path_id).ping_pending, false) {
+5422            builder.write_frame(frame::Ping, stats);
+5423        }
+5424
+5425        // IMMEDIATE_ACK
+5426        if mem::replace(&mut space.for_path(path_id).immediate_ack_pending, false) {
+5427            debug_assert_eq!(
+5428                space_id,
+5429                SpaceId::Data,
+5430                "immediate acks must be sent in the data space"
+5431            );
+5432            builder.write_frame(frame::ImmediateAck, stats);
+5433        }
+5434
+5435        // ACK
+5436        // TODO(flub): Should this send acks for this path anyway?
+5437
+5438        if !path_exclusive_only {
+5439            for path_id in space
+5440                .number_spaces
+5441                .iter_mut()
+5442                .filter(|(_, pns)| pns.pending_acks.can_send())
+5443                .map(|(&path_id, _)| path_id)
+5444                .collect::<Vec<_>>()
+5445            {
+5446                Self::populate_acks(
+5447                    now,
+5448                    self.receiving_ecn,
+5449                    path_id,
+5450                    space_id,
+5451                    space,
+5452                    is_multipath_negotiated,
+5453                    builder,
+5454                    stats,
+5455                );
+5456            }
+5457        }
+5458
+5459        // ACK_FREQUENCY
+5460        if !path_exclusive_only && mem::replace(&mut space.pending.ack_frequency, false) {
+5461            let sequence_number = self.ack_frequency.next_sequence_number();
+5462
+5463            // Safe to unwrap because this is always provided when ACK frequency is enabled
+5464            let config = self.config.ack_frequency_config.as_ref().unwrap();
+5465
+5466            // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error
+5467            let max_ack_delay = self.ack_frequency.candidate_max_ack_delay(
+5468                path.rtt.get(),
+5469                config,
+5470                &self.peer_params,
+5471            );
+5472
+5473            let frame = frame::AckFrequency {
+5474                sequence: sequence_number,
+5475                ack_eliciting_threshold: config.ack_eliciting_threshold,
+5476                request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX),
+5477                reordering_threshold: config.reordering_threshold,
+5478            };
+5479            builder.write_frame(frame, stats);
+5480
+5481            self.ack_frequency
+5482                .ack_frequency_sent(path_id, pn, max_ack_delay);
+5483        }
+5484
+5485        // PATH_CHALLENGE
+5486        if builder.frame_space_remaining() > frame::PathChallenge::SIZE_BOUND
+5487            && space_id == SpaceId::Data
+5488            && path.send_new_challenge
+5489            && !self.state.is_closed()
+5490        // we don't want to send new challenges if we are already closing
+5491        {
+5492            path.send_new_challenge = false;
+5493
+5494            // Generate a new challenge every time we send a new PATH_CHALLENGE
+5495            let token = self.rng.random();
+5496            let info = paths::SentChallengeInfo {
+5497                sent_instant: now,
+5498                network_path: path.network_path,
+5499            };
+5500            path.challenges_sent.insert(token, info);
+5501            let challenge = frame::PathChallenge(token);
+5502            trace!(frame = %challenge);
+5503            builder.write_frame(challenge, stats);
+5504            builder.require_padding();
+5505            let pto = self.ack_frequency.max_ack_delay_for_pto() + path.rtt.pto_base();
+5506            self.timers.set(
+5507                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+5508                now + pto,
+5509                self.qlog.with_time(now),
+5510            );
+5511
+5512            if is_multipath_negotiated && !path.validated && path.send_new_challenge {
+5513                // queue informing the path status along with the challenge
+5514                space.pending.path_status.insert(path_id);
+5515            }
+5516
+5517            // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless
+5518            // of whether one has already been sent on this path.
+5519            if space_id == SpaceId::Data
+5520                && self
+5521                    .config
+5522                    .address_discovery_role
+5523                    .should_report(&self.peer_params.address_discovery_role)
+5524            {
+5525                let frame = frame::ObservedAddr::new(
+5526                    path.network_path.remote,
+5527                    self.next_observed_addr_seq_no,
+5528                );
+5529                if builder.frame_space_remaining() > frame.size() {
+5530                    builder.write_frame(frame, stats);
+5531
+5532                    self.next_observed_addr_seq_no =
+5533                        self.next_observed_addr_seq_no.saturating_add(1u8);
+5534                    path.observed_addr_sent = true;
+5535
+5536                    space.pending.observed_addr = false;
+5537                }
+5538            }
+5539        }
+5540
+5541        // PATH_RESPONSE
+5542        if builder.frame_space_remaining() > frame::PathResponse::SIZE_BOUND
+5543            && space_id == SpaceId::Data
+5544            && let Some(token) = path.path_responses.pop_on_path(path.network_path)
+5545        {
+5546            let response = frame::PathResponse(token);
+5547            trace!(frame = %response);
+5548            builder.write_frame(response, stats);
+5549            builder.require_padding();
+5550
+5551            // NOTE: this is technically not required but might be useful to ride the
+5552            // request/response nature of path challenges to refresh an observation
+5553            // Since PATH_RESPONSE is a probing frame, this is allowed by the spec.
+5554            if space_id == SpaceId::Data
+5555                && self
+5556                    .config
+5557                    .address_discovery_role
+5558                    .should_report(&self.peer_params.address_discovery_role)
+5559            {
+5560                let frame = frame::ObservedAddr::new(
+5561                    path.network_path.remote,
+5562                    self.next_observed_addr_seq_no,
+5563                );
+5564                if builder.frame_space_remaining() > frame.size() {
+5565                    builder.write_frame(frame, stats);
+5566
+5567                    self.next_observed_addr_seq_no =
+5568                        self.next_observed_addr_seq_no.saturating_add(1u8);
+5569                    path.observed_addr_sent = true;
+5570
+5571                    space.pending.observed_addr = false;
+5572                }
+5573            }
+5574        }
+5575
+5576        // CRYPTO
+5577        while !path_exclusive_only
+5578            && builder.frame_space_remaining() > frame::Crypto::SIZE_BOUND
+5579            && !is_0rtt
+5580        {
+5581            let mut frame = match space.pending.crypto.pop_front() {
+5582                Some(x) => x,
+5583                None => break,
+5584            };
+5585
+5586            // Calculate the maximum amount of crypto data we can store in the buffer.
+5587            // Since the offset is known, we can reserve the exact size required to encode it.
+5588            // For length we reserve 2bytes which allows to encode up to 2^14,
+5589            // which is more than what fits into normally sized QUIC frames.
+5590            let max_crypto_data_size = builder.frame_space_remaining()
+5591                - 1 // Frame Type
+5592                - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) })
+5593                - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes
+5594
+5595            let len = frame
+5596                .data
+5597                .len()
+5598                .min(2usize.pow(14) - 1)
+5599                .min(max_crypto_data_size);
+5600
+5601            let data = frame.data.split_to(len);
+5602            let offset = frame.offset;
+5603            let truncated = frame::Crypto { offset, data };
+5604            builder.write_frame(truncated, stats);
+5605
+5606            if !frame.data.is_empty() {
+5607                frame.offset += len as u64;
+5608                space.pending.crypto.push_front(frame);
+5609            }
+5610        }
+5611
+5612        // TODO(flub): maybe this is much higher priority?
+5613        // PATH_ABANDON
+5614        while !path_exclusive_only
+5615            && space_id == SpaceId::Data
+5616            && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining()
+5617        {
+5618            let Some((abandoned_path_id, error_code)) = space.pending.path_abandon.pop_first()
+5619            else {
+5620                break;
+5621            };
+5622            let frame = frame::PathAbandon {
+5623                path_id: abandoned_path_id,
+5624                error_code,
+5625            };
+5626            builder.write_frame(frame, stats);
+5627
+5628            let ack_delay = self.ack_frequency.max_ack_delay_for_pto();
+5629            // We can't access path here anymore due to borrowing issues.
+5630            let send_pto = self.paths.get(&path_id).unwrap().data.rtt.pto_base() + ack_delay;
+5631            if let Some(abandoned_path) = self.paths.get_mut(&abandoned_path_id) {
+5632                // We only want to set the deadline on the *first* PATH_ABANDON we send.
+5633                // Retransmits shouldn't run this code again
+5634                if matches!(
+5635                    abandoned_path.data.abandon_state,
+5636                    AbandonState::NotAbandoned
+5637                ) {
+5638                    // The peer MUST respond with a corresponding PATH_ABANDON frame.
+5639                    // The other peer has 3 * PTO to do that.
+5640                    // This uses the PTO of the path we send on!
+5641                    // If the PATH_ABANDON comes in within the deadline we're giving here, then this
+5642                    // state will be set to `AbandonState::ReceivedPathAbandon`, essentially clearing
+5643                    // the deadline. If we receive a frame after the deadline, we error out with a
+5644                    // protocol violation.
+5645                    // Receiving other frames before the deadline is fine, as those might be packets
+5646                    // that were still in-flight.
+5647                    abandoned_path.data.abandon_state = AbandonState::ExpectingPathAbandon {
+5648                        deadline: now + 3 * send_pto,
+5649                    };
+5650
+5651                    // At some point, we need to forget about the path.
+5652                    // If we do so too early, then we'll have discarded the CIDs of that path and won't
+5653                    // handle incoming packets on that path correctly.
+5654                    // To give this path enough time, we assume that the peer will have received our
+5655                    // PATH_ABANDON within 3 * PTO of the path we sent the abandon on,
+5656                    // and then we give the path 3 * PTO time to make it very unlikely that there will
+5657                    // still be packets incoming on the path at that point.
+5658                    // This timer will actually get reset to a value that's likely to be even earlier
+5659                    // once we actually receive the PATH_ABANDON frame itself.
+5660                    let abandoned_pto =
+5661                        self.paths.get(&path_id).unwrap().data.rtt.pto_base() + ack_delay;
+5662                    self.timers.set(
+5663                        Timer::PerPath(abandoned_path_id, PathTimer::DiscardPath),
+5664                        now + 3 * send_pto + 3 * abandoned_pto,
+5665                        self.qlog.with_time(now),
+5666                    );
+5667                }
+5668            } else {
+5669                warn!("sent PATH_ABANDON after path was already discarded");
+5670            }
+5671        }
+5672
+5673        // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP
+5674        while !path_exclusive_only
+5675            && space_id == SpaceId::Data
+5676            && frame::PathStatusAvailable::SIZE_BOUND <= builder.frame_space_remaining()
+5677        {
+5678            let Some(path_id) = space.pending.path_status.pop_first() else {
+5679                break;
+5680            };
+5681            let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else {
+5682                trace!(%path_id, "discarding queued path status for unknown path");
+5683                continue;
+5684            };
+5685
+5686            let seq = path.status.seq();
+5687            match path.local_status() {
+5688                PathStatus::Available => {
+5689                    let frame = frame::PathStatusAvailable {
+5690                        path_id,
+5691                        status_seq_no: seq,
+5692                    };
+5693                    builder.write_frame(frame, stats);
+5694                }
+5695                PathStatus::Backup => {
+5696                    let frame = frame::PathStatusBackup {
+5697                        path_id,
+5698                        status_seq_no: seq,
+5699                    };
+5700                    builder.write_frame(frame, stats);
+5701                }
+5702            }
+5703        }
+5704
+5705        // MAX_PATH_ID
+5706        if space_id == SpaceId::Data
+5707            && !path_exclusive_only
+5708            && space.pending.max_path_id
+5709            && frame::MaxPathId::SIZE_BOUND <= builder.frame_space_remaining()
+5710        {
+5711            let frame = frame::MaxPathId(self.local_max_path_id);
+5712            builder.write_frame(frame, stats);
+5713            space.pending.max_path_id = false;
+5714        }
+5715
+5716        // PATHS_BLOCKED
+5717        if space_id == SpaceId::Data
+5718            && !path_exclusive_only
+5719            && space.pending.paths_blocked
+5720            && frame::PathsBlocked::SIZE_BOUND <= builder.frame_space_remaining()
+5721        {
+5722            let frame = frame::PathsBlocked(self.remote_max_path_id);
+5723            builder.write_frame(frame, stats);
+5724            space.pending.paths_blocked = false;
+5725        }
+5726
+5727        // PATH_CIDS_BLOCKED
+5728        while space_id == SpaceId::Data
+5729            && !path_exclusive_only
+5730            && frame::PathCidsBlocked::SIZE_BOUND <= builder.frame_space_remaining()
+5731        {
+5732            let Some(path_id) = space.pending.path_cids_blocked.pop_first() else {
+5733                break;
+5734            };
+5735            let next_seq = match self.remote_cids.get(&path_id) {
+5736                Some(cid_queue) => VarInt(cid_queue.active_seq() + 1),
+5737                None => VarInt(0),
+5738            };
+5739            let frame = frame::PathCidsBlocked { path_id, next_seq };
+5740            builder.write_frame(frame, stats);
+5741        }
+5742
+5743        // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS
+5744        if space_id == SpaceId::Data && !path_exclusive_only {
+5745            self.streams
+5746                .write_control_frames(builder, &mut space.pending, stats);
+5747        }
+5748
+5749        // NEW_CONNECTION_ID
+5750        let cid_len = self
+5751            .local_cid_state
+5752            .values()
+5753            .map(|cid_state| cid_state.cid_len())
+5754            .max()
+5755            .expect("some local CID state must exist");
+5756        let new_cid_size_bound =
+5757            frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len);
+5758        while !path_exclusive_only && builder.frame_space_remaining() > new_cid_size_bound {
+5759            let issued = match space.pending.new_cids.pop() {
+5760                Some(x) => x,
+5761                None => break,
+5762            };
+5763            let retire_prior_to = self
+5764                .local_cid_state
+5765                .get(&issued.path_id)
+5766                .map(|cid_state| cid_state.retire_prior_to())
+5767                .unwrap_or_else(|| panic!("missing local CID state for path={}", issued.path_id));
+5768
+5769            let cid_path_id = match is_multipath_negotiated {
+5770                true => Some(issued.path_id),
+5771                false => {
+5772                    debug_assert_eq!(issued.path_id, PathId::ZERO);
+5773                    None
+5774                }
+5775            };
+5776            let frame = frame::NewConnectionId {
+5777                path_id: cid_path_id,
+5778                sequence: issued.sequence,
+5779                retire_prior_to,
+5780                id: issued.id,
+5781                reset_token: issued.reset_token,
+5782            };
+5783            builder.write_frame(frame, stats);
+5784        }
+5785
+5786        // RETIRE_CONNECTION_ID
+5787        let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated);
+5788        while !path_exclusive_only && builder.frame_space_remaining() > retire_cid_bound {
+5789            let (path_id, sequence) = match space.pending.retire_cids.pop() {
+5790                Some((PathId::ZERO, seq)) if !is_multipath_negotiated => (None, seq),
+5791                Some((path_id, seq)) => (Some(path_id), seq),
+5792                None => break,
+5793            };
+5794            let frame = frame::RetireConnectionId { path_id, sequence };
+5795            builder.write_frame(frame, stats);
+5796        }
+5797
+5798        // DATAGRAM
+5799        let mut sent_datagrams = false;
+5800        while !path_exclusive_only
+5801            && builder.frame_space_remaining() > Datagram::SIZE_BOUND
+5802            && space_id == SpaceId::Data
+5803        {
+5804            match self.datagrams.write(builder, stats) {
+5805                true => {
+5806                    sent_datagrams = true;
+5807                }
+5808                false => break,
+5809            }
+5810        }
+5811        if self.datagrams.send_blocked && sent_datagrams {
+5812            self.events.push_back(Event::DatagramsUnblocked);
+5813            self.datagrams.send_blocked = false;
+5814        }
+5815
+5816        let path = &mut self.paths.get_mut(&path_id).expect("known path").data;
+5817
+5818        // NEW_TOKEN
+5819        if !path_exclusive_only {
+5820            while let Some(network_path) = space.pending.new_tokens.pop() {
+5821                debug_assert_eq!(space_id, SpaceId::Data);
+5822                let ConnectionSide::Server { server_config } = &self.side else {
+5823                    panic!("NEW_TOKEN frames should not be enqueued by clients");
+5824                };
+5825
+5826                if !network_path.is_probably_same_path(&path.network_path) {
+5827                    // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only
+5828                    // useful if used from the same IP address.  Thus, we abandon enqueued NEW_TOKEN
+5829                    // frames upon an path change. Instead, when the new path becomes validated,
+5830                    // NEW_TOKEN frames may be enqueued for the new path instead.
+5831                    continue;
+5832                }
+5833
+5834                let token = Token::new(
+5835                    TokenPayload::Validation {
+5836                        ip: network_path.remote.ip(),
+5837                        issued: server_config.time_source.now(),
+5838                    },
+5839                    &mut self.rng,
+5840                );
+5841                let new_token = NewToken {
+5842                    token: token.encode(&*server_config.token_key).into(),
+5843                };
+5844
+5845                if builder.frame_space_remaining() < new_token.size() {
+5846                    space.pending.new_tokens.push(network_path);
+5847                    break;
+5848                }
+5849
+5850                builder.write_frame(new_token, stats);
+5851                builder.retransmits_mut().new_tokens.push(network_path);
+5852            }
+5853        }
+5854
+5855        // STREAM
+5856        if !path_exclusive_only && space_id == SpaceId::Data {
+5857            self.streams
+5858                .write_stream_frames(builder, self.config.send_fairness, stats);
+5859        }
+5860
+5861        // ADD_ADDRESS
+5862        while space_id == SpaceId::Data
+5863            && !path_exclusive_only
+5864            && frame::AddAddress::SIZE_BOUND <= builder.frame_space_remaining()
+5865        {
+5866            if let Some(added_address) = space.pending.add_address.pop_last() {
+5867                builder.write_frame(added_address, stats);
+5868            } else {
+5869                break;
+5870            }
+5871        }
+5872
+5873        // REMOVE_ADDRESS
+5874        while space_id == SpaceId::Data
+5875            && !path_exclusive_only
+5876            && frame::RemoveAddress::SIZE_BOUND <= builder.frame_space_remaining()
+5877        {
+5878            if let Some(removed_address) = space.pending.remove_address.pop_last() {
+5879                builder.write_frame(removed_address, stats);
+5880            } else {
+5881                break;
+5882            }
+5883        }
+5884    }
+5885
+5886    /// Write pending ACKs into a buffer
+5887    fn populate_acks<'a, 'b>(
+5888        now: Instant,
+5889        receiving_ecn: bool,
+5890        path_id: PathId,
+5891        space_id: SpaceId,
+5892        space: &mut PacketSpace,
+5893        is_multipath_negotiated: bool,
+5894        builder: &mut PacketBuilder<'a, 'b>,
+5895        stats: &mut FrameStats,
+5896    ) {
+5897        // 0-RTT packets must never carry acks (which would have to be of handshake packets)
+5898        debug_assert!(space.crypto.is_some(), "tried to send ACK in 0-RTT");
+5899
+5900        debug_assert!(
+5901            is_multipath_negotiated || path_id == PathId::ZERO,
+5902            "Only PathId::ZERO allowed without multipath (have {path_id:?})"
+5903        );
+5904        if is_multipath_negotiated {
+5905            debug_assert!(
+5906                space_id == SpaceId::Data || path_id == PathId::ZERO,
+5907                "path acks must be sent in 1RTT space (have {space_id:?})"
+5908            );
+5909        }
+5910
+5911        let pns = space.for_path(path_id);
+5912        let ranges = pns.pending_acks.ranges();
+5913        debug_assert!(!ranges.is_empty(), "can not send empty ACK range");
+5914        let ecn = if receiving_ecn {
+5915            Some(&pns.ecn_counters)
+5916        } else {
+5917            None
+5918        };
+5919
+5920        let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64;
+5921        // TODO: This should come from `TransportConfig` if that gets configurable.
+5922        let ack_delay_exp = TransportParameters::default().ack_delay_exponent;
+5923        let delay = delay_micros >> ack_delay_exp.into_inner();
+5924
+5925        if is_multipath_negotiated && space_id == SpaceId::Data {
+5926            if !ranges.is_empty() {
+5927                let frame = frame::PathAck::encoder(path_id, delay, ranges, ecn);
+5928                builder.write_frame(frame, stats);
+5929            }
+5930        } else {
+5931            builder.write_frame(frame::Ack::encoder(delay, ranges, ecn), stats);
+5932        }
+5933    }
+5934
+5935    fn close_common(&mut self) {
+5936        trace!("connection closed");
+5937        self.timers.reset();
+5938    }
+5939
+5940    fn set_close_timer(&mut self, now: Instant) {
+5941        // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO using the max PTO of
+5942        // all paths.
+5943        let pto_max = self.max_pto_all_paths(self.highest_space);
+5944        self.timers.set(
+5945            Timer::Conn(ConnTimer::Close),
+5946            now + 3 * pto_max,
+5947            self.qlog.with_time(now),
+5948        );
+5949    }
+5950
+5951    /// Handle transport parameters received from the peer
+5952    ///
+5953    /// *remote_cid* and *local_cid* are the source and destination CIDs respectively of the
+5954    /// *packet into which the transport parameters arrived.
+5955    fn handle_peer_params(
+5956        &mut self,
+5957        params: TransportParameters,
+5958        local_cid: ConnectionId,
+5959        remote_cid: ConnectionId,
+5960        now: Instant,
+5961    ) -> Result<(), TransportError> {
+5962        if Some(self.original_remote_cid) != params.initial_src_cid
+5963            || (self.side.is_client()
+5964                && (Some(self.initial_dst_cid) != params.original_dst_cid
+5965                    || self.retry_src_cid != params.retry_src_cid))
+5966        {
+5967            return Err(TransportError::TRANSPORT_PARAMETER_ERROR(
+5968                "CID authentication failure",
+5969            ));
+5970        }
+5971        if params.initial_max_path_id.is_some() && (local_cid.is_empty() || remote_cid.is_empty()) {
+5972            return Err(TransportError::PROTOCOL_VIOLATION(
+5973                "multipath must not use zero-length CIDs",
+5974            ));
+5975        }
+5976
+5977        self.set_peer_params(params);
+5978        self.qlog.emit_peer_transport_params_received(self, now);
+5979
+5980        Ok(())
+5981    }
+5982
+5983    fn set_peer_params(&mut self, params: TransportParameters) {
+5984        self.streams.set_params(&params);
+5985        self.idle_timeout =
+5986            negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout));
+5987        trace!("negotiated max idle timeout {:?}", self.idle_timeout);
+5988
+5989        if let Some(ref info) = params.preferred_address {
+5990            // During the handshake PathId::ZERO exists.
+5991            self.remote_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId {
+5992                path_id: None,
+5993                sequence: 1,
+5994                id: info.connection_id,
+5995                reset_token: info.stateless_reset_token,
+5996                retire_prior_to: 0,
+5997            })
+5998            .expect(
+5999                "preferred address CID is the first received, and hence is guaranteed to be legal",
+6000            );
+6001            let remote = self.path_data(PathId::ZERO).network_path.remote;
+6002            self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token);
+6003        }
+6004        self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params);
+6005
+6006        let mut multipath_enabled = None;
+6007        if let (Some(local_max_path_id), Some(remote_max_path_id)) = (
+6008            self.config.get_initial_max_path_id(),
+6009            params.initial_max_path_id,
+6010        ) {
+6011            // multipath is enabled, register the local and remote maximums
+6012            self.local_max_path_id = local_max_path_id;
+6013            self.remote_max_path_id = remote_max_path_id;
+6014            let initial_max_path_id = local_max_path_id.min(remote_max_path_id);
+6015            debug!(%initial_max_path_id, "multipath negotiated");
+6016            multipath_enabled = Some(initial_max_path_id);
+6017        }
+6018
+6019        if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) =
+6020            self.config
+6021                .max_remote_nat_traversal_addresses
+6022                .zip(params.max_remote_nat_traversal_addresses)
+6023        {
+6024            if let Some(max_initial_paths) =
+6025                multipath_enabled.map(|path_id| path_id.saturating_add(1u8))
+6026            {
+6027                let max_local_addresses = max_remotely_allowed_remote_addresses.get();
+6028                let max_remote_addresses = max_locally_allowed_remote_addresses.get();
+6029                self.iroh_hp =
+6030                    iroh_hp::State::new(max_remote_addresses, max_local_addresses, self.side());
+6031                debug!(
+6032                    %max_remote_addresses, %max_local_addresses,
+6033                    "iroh hole punching negotiated"
+6034                );
+6035
+6036                match self.side() {
+6037                    Side::Client => {
+6038                        if max_initial_paths.as_u32() < max_remote_addresses as u32 + 1 {
+6039                            // in this case the client might try to open `max_remote_addresses` new
+6040                            // paths, but the current multipath configuration will not allow it
+6041                            warn!(%max_initial_paths, %max_remote_addresses, "local client configuration might cause nat traversal issues")
+6042                        } else if max_local_addresses as u64
+6043                            > params.active_connection_id_limit.into_inner()
+6044                        {
+6045                            // the server allows us to send at most `params.active_connection_id_limit`
+6046                            // but they might need at least `max_local_addresses` to effectively send
+6047                            // `PATH_CHALLENGE` frames to each advertised local address
+6048                            warn!(%max_local_addresses, remote_cid_limit=%params.active_connection_id_limit.into_inner(), "remote server configuration might cause nat traversal issues")
+6049                        }
+6050                    }
+6051                    Side::Server => {
+6052                        if (max_initial_paths.as_u32() as u64) < crate::LOCAL_CID_COUNT {
+6053                            warn!(%max_initial_paths, local_cid_limit=%crate::LOCAL_CID_COUNT, "local server configuration might cause nat traversal issues")
+6054                        }
+6055                    }
+6056                }
+6057            } else {
+6058                debug!("iroh nat traversal enabled for both endpoints, but multipath is missing")
+6059            }
+6060        }
+6061
+6062        self.peer_params = params;
+6063        let peer_max_udp_payload_size =
+6064            u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX);
+6065        self.path_data_mut(PathId::ZERO)
+6066            .mtud
+6067            .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size);
+6068    }
+6069
+6070    /// Decrypts a packet, returning the packet number on success
+6071    fn decrypt_packet(
+6072        &mut self,
+6073        now: Instant,
+6074        path_id: PathId,
+6075        packet: &mut Packet,
+6076    ) -> Result<Option<u64>, Option<TransportError>> {
+6077        let result = packet_crypto::decrypt_packet_body(
+6078            packet,
+6079            path_id,
+6080            &self.spaces,
+6081            self.zero_rtt_crypto.as_ref(),
+6082            self.key_phase,
+6083            self.prev_crypto.as_ref(),
+6084            self.next_crypto.as_ref(),
+6085        )?;
+6086
+6087        let result = match result {
+6088            Some(r) => r,
+6089            None => return Ok(None),
+6090        };
+6091
+6092        if result.outgoing_key_update_acked
+6093            && let Some(prev) = self.prev_crypto.as_mut()
+6094        {
+6095            prev.end_packet = Some((result.number, now));
+6096            self.set_key_discard_timer(now, packet.header.space());
+6097        }
+6098
+6099        if result.incoming_key_update {
+6100            trace!("key update authenticated");
+6101            self.update_keys(Some((result.number, now)), true);
+6102            self.set_key_discard_timer(now, packet.header.space());
+6103        }
+6104
+6105        Ok(Some(result.number))
+6106    }
+6107
+6108    fn update_keys(&mut self, end_packet: Option<(u64, Instant)>, remote: bool) {
+6109        trace!("executing key update");
+6110        // Generate keys for the key phase after the one we're switching to, store them in
+6111        // `next_crypto`, make the contents of `next_crypto` current, and move the current keys into
+6112        // `prev_crypto`.
+6113        let new = self
+6114            .crypto
+6115            .next_1rtt_keys()
+6116            .expect("only called for `Data` packets");
+6117        self.key_phase_size = new
+6118            .local
+6119            .confidentiality_limit()
+6120            .saturating_sub(KEY_UPDATE_MARGIN);
+6121        let old = mem::replace(
+6122            &mut self.spaces[SpaceId::Data]
+6123                .crypto
+6124                .as_mut()
+6125                .unwrap() // safe because update_keys() can only be triggered by short packets
+6126                .packet,
+6127            mem::replace(self.next_crypto.as_mut().unwrap(), new),
+6128        );
+6129        self.spaces[SpaceId::Data]
+6130            .iter_paths_mut()
+6131            .for_each(|s| s.sent_with_keys = 0);
+6132        self.prev_crypto = Some(PrevCrypto {
+6133            crypto: old,
+6134            end_packet,
+6135            update_unacked: remote,
+6136        });
+6137        self.key_phase = !self.key_phase;
+6138    }
+6139
+6140    fn peer_supports_ack_frequency(&self) -> bool {
+6141        self.peer_params.min_ack_delay.is_some()
+6142    }
+6143
+6144    /// Send an IMMEDIATE_ACK frame to the remote endpoint
+6145    ///
+6146    /// According to the spec, this will result in an error if the remote endpoint does not support
+6147    /// the Acknowledgement Frequency extension
+6148    pub(crate) fn immediate_ack(&mut self, path_id: PathId) {
+6149        debug_assert_eq!(
+6150            self.highest_space,
+6151            SpaceId::Data,
+6152            "immediate ack must be written in the data space"
+6153        );
+6154        self.spaces[self.highest_space]
+6155            .for_path(path_id)
+6156            .immediate_ack_pending = true;
+6157    }
+6158
+6159    /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests
+6160    #[cfg(test)]
+6161    pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> {
+6162        let (path_id, first_decode, remaining) = match &event.0 {
+6163            ConnectionEventInner::Datagram(DatagramConnectionEvent {
+6164                path_id,
+6165                first_decode,
+6166                remaining,
+6167                ..
+6168            }) => (path_id, first_decode, remaining),
+6169            _ => return None,
+6170        };
+6171
+6172        if remaining.is_some() {
+6173            panic!("Packets should never be coalesced in tests");
+6174        }
+6175
+6176        let decrypted_header = packet_crypto::unprotect_header(
+6177            first_decode.clone(),
+6178            &self.spaces,
+6179            self.zero_rtt_crypto.as_ref(),
+6180            self.peer_params.stateless_reset_token,
+6181        )?;
+6182
+6183        let mut packet = decrypted_header.packet?;
+6184        packet_crypto::decrypt_packet_body(
+6185            &mut packet,
+6186            *path_id,
+6187            &self.spaces,
+6188            self.zero_rtt_crypto.as_ref(),
+6189            self.key_phase,
+6190            self.prev_crypto.as_ref(),
+6191            self.next_crypto.as_ref(),
+6192        )
+6193        .ok()?;
+6194
+6195        Some(packet.payload.to_vec())
+6196    }
+6197
+6198    /// The number of bytes of packets containing retransmittable frames that have not been
+6199    /// acknowledged or declared lost.
+6200    #[cfg(test)]
+6201    pub(crate) fn bytes_in_flight(&self) -> u64 {
+6202        // TODO(@divma): consider including for multipath?
+6203        self.path_data(PathId::ZERO).in_flight.bytes
+6204    }
+6205
+6206    /// Number of bytes worth of non-ack-only packets that may be sent
+6207    #[cfg(test)]
+6208    pub(crate) fn congestion_window(&self) -> u64 {
+6209        let path = self.path_data(PathId::ZERO);
+6210        path.congestion
+6211            .window()
+6212            .saturating_sub(path.in_flight.bytes)
+6213    }
+6214
+6215    /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running
+6216    #[cfg(test)]
+6217    pub(crate) fn is_idle(&self) -> bool {
+6218        let current_timers = self.timers.values();
+6219        current_timers
+6220            .into_iter()
+6221            .filter(|(timer, _)| {
+6222                !matches!(
+6223                    timer,
+6224                    Timer::Conn(ConnTimer::KeepAlive)
+6225                        | Timer::PerPath(_, PathTimer::PathKeepAlive)
+6226                        | Timer::Conn(ConnTimer::PushNewCid)
+6227                        | Timer::Conn(ConnTimer::KeyDiscard)
+6228                )
+6229            })
+6230            .min_by_key(|(_, time)| *time)
+6231            .is_none_or(|(timer, _)| timer == Timer::Conn(ConnTimer::Idle))
+6232    }
+6233
+6234    /// Whether explicit congestion notification is in use on outgoing packets.
+6235    #[cfg(test)]
+6236    pub(crate) fn using_ecn(&self) -> bool {
+6237        self.path_data(PathId::ZERO).sending_ecn
+6238    }
+6239
+6240    /// The number of received bytes in the current path
+6241    #[cfg(test)]
+6242    pub(crate) fn total_recvd(&self) -> u64 {
+6243        self.path_data(PathId::ZERO).total_recvd
+6244    }
+6245
+6246    #[cfg(test)]
+6247    pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) {
+6248        self.local_cid_state
+6249            .get(&PathId::ZERO)
+6250            .unwrap()
+6251            .active_seq()
+6252    }
+6253
+6254    #[cfg(test)]
+6255    #[track_caller]
+6256    pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) {
+6257        self.local_cid_state
+6258            .get(&PathId(path_id))
+6259            .unwrap()
+6260            .active_seq()
+6261    }
+6262
+6263    /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame
+6264    /// with updated `retire_prior_to` field set to `v`
+6265    #[cfg(test)]
+6266    pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) {
+6267        let n = self
+6268            .local_cid_state
+6269            .get_mut(&PathId::ZERO)
+6270            .unwrap()
+6271            .assign_retire_seq(v);
+6272        self.endpoint_events
+6273            .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n));
+6274    }
+6275
+6276    /// Check the current active remote CID sequence for `PathId::ZERO`
+6277    #[cfg(test)]
+6278    pub(crate) fn active_remote_cid_seq(&self) -> u64 {
+6279        self.remote_cids.get(&PathId::ZERO).unwrap().active_seq()
+6280    }
+6281
+6282    /// Returns the detected maximum udp payload size for the current path
+6283    #[cfg(test)]
+6284    pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 {
+6285        self.path_data(path_id).current_mtu()
+6286    }
+6287
+6288    /// Triggers path validation on all paths
+6289    #[cfg(test)]
+6290    pub(crate) fn trigger_path_validation(&mut self) {
+6291        for path in self.paths.values_mut() {
+6292            path.data.send_new_challenge = true;
+6293        }
+6294    }
+6295
+6296    /// Whether we have 1-RTT data to send
+6297    ///
+6298    /// This checks for frames that can only be sent in the data space (1-RTT):
+6299    /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated.
+6300    /// - Pending PATH_RESPONSE frames.
+6301    /// - Pending data to send in STREAM frames.
+6302    /// - Pending DATAGRAM frames to send.
+6303    ///
+6304    /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that
+6305    /// may need to be sent.
+6306    fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames {
+6307        let path_exclusive = self.paths.get(&path_id).is_some_and(|path| {
+6308            path.data.send_new_challenge
+6309                || path
+6310                    .prev
+6311                    .as_ref()
+6312                    .is_some_and(|(_, path)| path.send_new_challenge)
+6313                || !path.data.path_responses.is_empty()
+6314        });
+6315        let other = self.streams.can_send_stream_data()
+6316            || self
+6317                .datagrams
+6318                .outgoing
+6319                .front()
+6320                .is_some_and(|x| x.size(true) <= max_size);
+6321        SendableFrames {
+6322            acks: false,
+6323            other,
+6324            close: false,
+6325            path_exclusive,
+6326        }
+6327    }
+6328
+6329    /// Terminate the connection instantly, without sending a close packet
+6330    fn kill(&mut self, reason: ConnectionError) {
+6331        self.close_common();
+6332        self.state.move_to_drained(Some(reason));
+6333        self.endpoint_events.push_back(EndpointEventInner::Drained);
+6334    }
+6335
+6336    /// Storage size required for the largest packet that can be transmitted on all currently
+6337    /// available paths
+6338    ///
+6339    /// Buffers passed to [`Connection::poll_transmit`] should be at least this large.
+6340    ///
+6341    /// When multipath is enabled, this value is the minimum MTU across all available paths.
+6342    pub fn current_mtu(&self) -> u16 {
+6343        self.paths
+6344            .iter()
+6345            .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id))
+6346            .map(|(_path_id, path_state)| path_state.data.current_mtu())
+6347            .min()
+6348            .expect("There is always at least one available path")
+6349    }
+6350
+6351    /// Size of non-frame data for a 1-RTT packet
+6352    ///
+6353    /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are
+6354    /// frames. Changes if the length of the remote connection ID changes, which is expected to be
+6355    /// rare. If `pn` is specified, may additionally change unpredictably due to variations in
+6356    /// latency and packet loss.
+6357    fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize {
+6358        let pn_len = PacketNumber::new(
+6359            pn,
+6360            self.spaces[SpaceId::Data]
+6361                .for_path(path)
+6362                .largest_acked_packet
+6363                .unwrap_or(0),
+6364        )
+6365        .len();
+6366
+6367        // 1 byte for flags
+6368        1 + self
+6369            .remote_cids
+6370            .get(&path)
+6371            .map(|cids| cids.active().len())
+6372            .unwrap_or(20)      // Max CID len in QUIC v1
+6373            + pn_len
+6374            + self.tag_len_1rtt()
+6375    }
+6376
+6377    fn predict_1rtt_overhead_no_pn(&self) -> usize {
+6378        let pn_len = 4;
+6379
+6380        let cid_len = self
+6381            .remote_cids
+6382            .values()
+6383            .map(|cids| cids.active().len())
+6384            .max()
+6385            .unwrap_or(20); // Max CID len in QUIC v1
+6386
+6387        // 1 byte for flags
+6388        1 + cid_len + pn_len + self.tag_len_1rtt()
+6389    }
+6390
+6391    fn tag_len_1rtt(&self) -> usize {
+6392        let key = match self.spaces[SpaceId::Data].crypto.as_ref() {
+6393            Some(crypto) => Some(&*crypto.packet.local),
+6394            None => self.zero_rtt_crypto.as_ref().map(|x| &*x.packet),
+6395        };
+6396        // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of
+6397        // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead,
+6398        // but that would needlessly prevent sending datagrams during 0-RTT.
+6399        key.map_or(16, |x| x.tag_len())
+6400    }
+6401
+6402    /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate
+6403    fn on_path_validated(&mut self, path_id: PathId) {
+6404        self.path_data_mut(path_id).validated = true;
+6405        let ConnectionSide::Server { server_config } = &self.side else {
+6406            return;
+6407        };
+6408        let network_path = self.path_data(path_id).network_path;
+6409        let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens;
+6410        new_tokens.clear();
+6411        for _ in 0..server_config.validation_token.sent {
+6412            new_tokens.push(network_path);
+6413        }
+6414    }
+6415
+6416    /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP
+6417    fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) {
+6418        if let Some(path) = self.paths.get_mut(&path_id) {
+6419            path.data.status.remote_update(status, status_seq_no);
+6420        } else {
+6421            debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id);
+6422        }
+6423        self.events.push_back(
+6424            PathEvent::RemoteStatus {
+6425                id: path_id,
+6426                status,
+6427            }
+6428            .into(),
+6429        );
+6430    }
+6431
+6432    /// Returns the maximum [`PathId`] to be used for sending in this connection.
+6433    ///
+6434    /// This is calculated as minimum between the local and remote's maximums when multipath is
+6435    /// enabled, or `None` when disabled.
+6436    ///
+6437    /// For data that's received, we should use [`Self::local_max_path_id`] instead.
+6438    /// The reasoning is that the remote might already have updated to its own newer
+6439    /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered.
+6440    fn max_path_id(&self) -> Option<PathId> {
+6441        if self.is_multipath_negotiated() {
+6442            Some(self.remote_max_path_id.min(self.local_max_path_id))
+6443        } else {
+6444            None
+6445        }
+6446    }
+6447
+6448    /// Returns whether this connection has a socket that supports IPv6.
+6449    ///
+6450    /// TODO(matheus23): This is related to quinn endpoint state's `ipv6` bool. We should move that info
+6451    /// here instead of trying to hack around not knowing it exactly.
+6452    fn is_ipv6(&self) -> bool {
+6453        self.paths
+6454            .values()
+6455            .any(|p| p.data.network_path.remote.is_ipv6())
+6456    }
+6457
+6458    /// Add addresses the local endpoint considers are reachable for nat traversal.
+6459    pub fn add_nat_traversal_address(&mut self, address: SocketAddr) -> Result<(), iroh_hp::Error> {
+6460        if let Some(added) = self.iroh_hp.add_local_address(address)? {
+6461            self.spaces[SpaceId::Data].pending.add_address.insert(added);
+6462        };
+6463        Ok(())
+6464    }
+6465
+6466    /// Removes an address the endpoing no longer considers reachable for nat traversal
+6467    ///
+6468    /// Addresses not present in the set will be silently ignored.
+6469    pub fn remove_nat_traversal_address(
+6470        &mut self,
+6471        address: SocketAddr,
+6472    ) -> Result<(), iroh_hp::Error> {
+6473        if let Some(removed) = self.iroh_hp.remove_local_address(address)? {
+6474            self.spaces[SpaceId::Data]
+6475                .pending
+6476                .remove_address
+6477                .insert(removed);
+6478        }
+6479        Ok(())
+6480    }
+6481
+6482    /// Get the current local nat traversal addresses
+6483    pub fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+6484        self.iroh_hp.get_local_nat_traversal_addresses()
+6485    }
+6486
+6487    /// Get the currently advertised nat traversal addresses by the server
+6488    pub fn get_remote_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+6489        Ok(self
+6490            .iroh_hp
+6491            .client_side()?
+6492            .get_remote_nat_traversal_addresses())
+6493    }
+6494
+6495    /// Attempts to open a path for nat traversal.
+6496    ///
+6497    /// On success returns the [`PathId`] and remote address of the path.
+6498    fn open_nat_traversal_path(
+6499        &mut self,
+6500        now: Instant,
+6501        ip_port: (IpAddr, u16),
+6502    ) -> Result<Option<(PathId, SocketAddr)>, PathError> {
+6503        let remote = ip_port.into();
+6504        // TODO(matheus23): Probe the correct 4-tuple, instead of only a remote address?
+6505        // By specifying None for `local_ip`, we do two things: 1. open_path_ensure won't
+6506        // generate two paths to the same remote and 2. we let the OS choose which
+6507        // interface to use for sending on that path.
+6508        let network_path = FourTuple {
+6509            remote,
+6510            local_ip: None,
+6511        };
+6512        match self.open_path_ensure(network_path, PathStatus::Backup, now) {
+6513            Ok((path_id, path_was_known)) => {
+6514                if path_was_known {
+6515                    trace!(%path_id, %remote, "nat traversal: path existed for remote");
+6516                }
+6517                Ok(Some((path_id, remote)))
+6518            }
+6519            Err(e) => {
+6520                debug!(%remote, %e, "nat traversal: failed to probe remote");
+6521                Err(e)
+6522            }
+6523        }
+6524    }
+6525
+6526    /// Initiates a new nat traversal round
+6527    ///
+6528    /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT`
+6529    /// frames, and initiating probing of the known remote addresses. When a new round is
+6530    /// initiated, the previous one is cancelled, and paths that have not been opened are closed.
+6531    ///
+6532    /// Returns the server addresses that are now being probed.
+6533    /// If addresses fail due to spurious errors, these might succeed later and not be returned in
+6534    /// this set.
+6535    pub fn initiate_nat_traversal_round(
+6536        &mut self,
+6537        now: Instant,
+6538    ) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+6539        if self.state.is_closed() {
+6540            return Err(iroh_hp::Error::Closed);
+6541        }
+6542
+6543        let ipv6 = self.is_ipv6();
+6544        let client_state = self.iroh_hp.client_side_mut()?;
+6545        let iroh_hp::NatTraversalRound {
+6546            new_round,
+6547            reach_out_at,
+6548            addresses_to_probe,
+6549            prev_round_path_ids,
+6550        } = client_state.initiate_nat_traversal_round(ipv6)?;
+6551
+6552        self.spaces[SpaceId::Data].pending.reach_out = Some((new_round, reach_out_at));
+6553
+6554        for path_id in prev_round_path_ids {
+6555            let Some(path) = self.path(path_id) else {
+6556                continue;
+6557            };
+6558            let ip = path.network_path.remote.ip();
+6559            let port = path.network_path.remote.port();
+6560
+6561            // We only close paths that aren't validated (thus are working) that we opened
+6562            // in a previous round.
+6563            // And we only close paths that we don't want to probe anyways.
+6564            if !addresses_to_probe
+6565                .iter()
+6566                .any(|(_, probe)| *probe == (ip, port))
+6567                && !path.validated
+6568                && !self.abandoned_paths.contains(&path_id)
+6569            {
+6570                trace!(%path_id, "closing path from previous round");
+6571                let _ = self.close_path(
+6572                    now,
+6573                    path_id,
+6574                    TransportErrorCode::APPLICATION_ABANDON_PATH.into(),
+6575                );
+6576            }
+6577        }
+6578
+6579        let mut err = None;
+6580
+6581        let mut path_ids = Vec::with_capacity(addresses_to_probe.len());
+6582        let mut probed_addresses = Vec::with_capacity(addresses_to_probe.len());
+6583
+6584        for (id, address) in addresses_to_probe {
+6585            match self.open_nat_traversal_path(now, address) {
+6586                Ok(None) => {}
+6587                Ok(Some((path_id, remote))) => {
+6588                    path_ids.push(path_id);
+6589                    probed_addresses.push(remote);
+6590                }
+6591                Err(e) => {
+6592                    self.iroh_hp
+6593                        .client_side_mut()
+6594                        .expect("validated")
+6595                        .report_in_continuation(id, e);
+6596                    err.get_or_insert(e);
+6597                }
+6598            }
+6599        }
+6600
+6601        if let Some(err) = err {
+6602            // We failed to probe any addresses, bail out
+6603            if probed_addresses.is_empty() {
+6604                return Err(iroh_hp::Error::Multipath(err));
+6605            }
+6606        }
+6607
+6608        self.iroh_hp
+6609            .client_side_mut()
+6610            .expect("connection side validated")
+6611            .set_round_path_ids(path_ids);
+6612
+6613        Ok(probed_addresses)
+6614    }
+6615
+6616    /// Attempts to continue a nat traversal round by trying to open paths for pending client probes.
+6617    ///
+6618    /// If there was nothing to do, it returns `None`. Otherwise it returns whether the path was
+6619    /// successfully open.
+6620    fn continue_nat_traversal_round(&mut self, now: Instant) -> Option<bool> {
+6621        let ipv6 = self.is_ipv6();
+6622        let client_state = self.iroh_hp.client_side_mut().ok()?;
+6623        let (id, address) = client_state.continue_nat_traversal_round(ipv6)?;
+6624        let open_result = self.open_nat_traversal_path(now, address);
+6625        let client_state = self.iroh_hp.client_side_mut().expect("validated");
+6626        match open_result {
+6627            Ok(None) => Some(true),
+6628            Ok(Some((path_id, _remote))) => {
+6629                client_state.add_round_path_id(path_id);
+6630                Some(true)
+6631            }
+6632            Err(e) => {
+6633                client_state.report_in_continuation(id, e);
+6634                Some(false)
+6635            }
+6636        }
+6637    }
+6638}
+6639
+6640impl fmt::Debug for Connection {
+6641    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+6642        f.debug_struct("Connection")
+6643            .field("handshake_cid", &self.handshake_cid)
+6644            .finish()
+6645    }
+6646}
+6647
+6648#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+6649enum PathBlocked {
+6650    No,
+6651    AntiAmplification,
+6652    Congestion,
+6653    Pacing,
+6654}
+6655
+6656/// Fields of `Connection` specific to it being client-side or server-side
+6657enum ConnectionSide {
+6658    Client {
+6659        /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded
+6660        token: Bytes,
+6661        token_store: Arc<dyn TokenStore>,
+6662        server_name: String,
+6663    },
+6664    Server {
+6665        server_config: Arc<ServerConfig>,
+6666    },
+6667}
+6668
+6669impl ConnectionSide {
+6670    fn remote_may_migrate(&self, state: &State) -> bool {
+6671        match self {
+6672            Self::Server { server_config } => server_config.migration,
+6673            Self::Client { .. } => {
+6674                if let Some(hs) = state.as_handshake() {
+6675                    hs.allow_server_migration
+6676                } else {
+6677                    false
+6678                }
+6679            }
+6680        }
+6681    }
+6682
+6683    fn is_client(&self) -> bool {
+6684        self.side().is_client()
+6685    }
+6686
+6687    fn is_server(&self) -> bool {
+6688        self.side().is_server()
+6689    }
+6690
+6691    fn side(&self) -> Side {
+6692        match *self {
+6693            Self::Client { .. } => Side::Client,
+6694            Self::Server { .. } => Side::Server,
+6695        }
+6696    }
+6697}
+6698
+6699impl From<SideArgs> for ConnectionSide {
+6700    fn from(side: SideArgs) -> Self {
+6701        match side {
+6702            SideArgs::Client {
+6703                token_store,
+6704                server_name,
+6705            } => Self::Client {
+6706                token: token_store.take(&server_name).unwrap_or_default(),
+6707                token_store,
+6708                server_name,
+6709            },
+6710            SideArgs::Server {
+6711                server_config,
+6712                pref_addr_cid: _,
+6713                path_validated: _,
+6714            } => Self::Server { server_config },
+6715        }
+6716    }
+6717}
+6718
+6719/// Parameters to `Connection::new` specific to it being client-side or server-side
+6720pub(crate) enum SideArgs {
+6721    Client {
+6722        token_store: Arc<dyn TokenStore>,
+6723        server_name: String,
+6724    },
+6725    Server {
+6726        server_config: Arc<ServerConfig>,
+6727        pref_addr_cid: Option<ConnectionId>,
+6728        path_validated: bool,
+6729    },
+6730}
+6731
+6732impl SideArgs {
+6733    pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> {
+6734        match *self {
+6735            Self::Client { .. } => None,
+6736            Self::Server { pref_addr_cid, .. } => pref_addr_cid,
+6737        }
+6738    }
+6739
+6740    pub(crate) fn path_validated(&self) -> bool {
+6741        match *self {
+6742            Self::Client { .. } => true,
+6743            Self::Server { path_validated, .. } => path_validated,
+6744        }
+6745    }
+6746
+6747    pub(crate) fn side(&self) -> Side {
+6748        match *self {
+6749            Self::Client { .. } => Side::Client,
+6750            Self::Server { .. } => Side::Server,
+6751        }
+6752    }
+6753}
+6754
+6755/// Reasons why a connection might be lost
+6756#[derive(Debug, Error, Clone, PartialEq, Eq)]
+6757pub enum ConnectionError {
+6758    /// The peer doesn't implement any supported version
+6759    #[error("peer doesn't implement any supported version")]
+6760    VersionMismatch,
+6761    /// The peer violated the QUIC specification as understood by this implementation
+6762    #[error(transparent)]
+6763    TransportError(#[from] TransportError),
+6764    /// The peer's QUIC stack aborted the connection automatically
+6765    #[error("aborted by peer: {0}")]
+6766    ConnectionClosed(frame::ConnectionClose),
+6767    /// The peer closed the connection
+6768    #[error("closed by peer: {0}")]
+6769    ApplicationClosed(frame::ApplicationClose),
+6770    /// The peer is unable to continue processing this connection, usually due to having restarted
+6771    #[error("reset by peer")]
+6772    Reset,
+6773    /// Communication with the peer has lapsed for longer than the negotiated idle timeout
+6774    ///
+6775    /// If neither side is sending keep-alives, a connection will time out after a long enough idle
+6776    /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`]
+6777    /// and [`TransportConfig::keep_alive_interval()`].
+6778    #[error("timed out")]
+6779    TimedOut,
+6780    /// The local application closed the connection
+6781    #[error("closed")]
+6782    LocallyClosed,
+6783    /// The connection could not be created because not enough of the CID space is available
+6784    ///
+6785    /// Try using longer connection IDs.
+6786    #[error("CIDs exhausted")]
+6787    CidsExhausted,
+6788}
+6789
+6790impl From<Close> for ConnectionError {
+6791    fn from(x: Close) -> Self {
+6792        match x {
+6793            Close::Connection(reason) => Self::ConnectionClosed(reason),
+6794            Close::Application(reason) => Self::ApplicationClosed(reason),
+6795        }
+6796    }
+6797}
+6798
+6799// For compatibility with API consumers
+6800impl From<ConnectionError> for io::Error {
+6801    fn from(x: ConnectionError) -> Self {
+6802        use ConnectionError::*;
+6803        let kind = match x {
+6804            TimedOut => io::ErrorKind::TimedOut,
+6805            Reset => io::ErrorKind::ConnectionReset,
+6806            ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted,
+6807            TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => {
+6808                io::ErrorKind::Other
+6809            }
+6810        };
+6811        Self::new(kind, x)
+6812    }
+6813}
+6814
+6815/// Errors that might trigger a path being closed
+6816// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API
+6817#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)]
+6818pub enum PathError {
+6819    /// The extension was not negotiated with the peer
+6820    #[error("multipath extension not negotiated")]
+6821    MultipathNotNegotiated,
+6822    /// Paths can only be opened client-side
+6823    #[error("the server side may not open a path")]
+6824    ServerSideNotAllowed,
+6825    /// Current limits do not allow us to open more paths
+6826    #[error("maximum number of concurrent paths reached")]
+6827    MaxPathIdReached,
+6828    /// No remote CIDs available to open a new path
+6829    #[error("remoted CIDs exhausted")]
+6830    RemoteCidsExhausted,
+6831    /// Path could not be validated and will be abandoned
+6832    #[error("path validation failed")]
+6833    ValidationFailed,
+6834    /// The remote address for the path is not supported by the endpoint
+6835    #[error("invalid remote address")]
+6836    InvalidRemoteAddress(SocketAddr),
+6837}
+6838
+6839/// Errors triggered when abandoning a path
+6840#[derive(Debug, Error, Clone, Eq, PartialEq)]
+6841pub enum ClosePathError {
+6842    /// The path is already closed or was never opened
+6843    #[error("closed path")]
+6844    ClosedPath,
+6845    /// This is the last path, which can not be abandoned
+6846    #[error("last open path")]
+6847    LastOpenPath,
+6848}
+6849
+6850/// Error when the multipath extension was not negotiated, but attempted to be used.
+6851#[derive(Debug, Error, Clone, Copy)]
+6852#[error("Multipath extension not negotiated")]
+6853pub struct MultipathNotNegotiated {
+6854    _private: (),
+6855}
+6856
+6857/// Events of interest to the application
+6858#[derive(Debug)]
+6859pub enum Event {
+6860    /// The connection's handshake data is ready
+6861    HandshakeDataReady,
+6862    /// The connection was successfully established
+6863    Connected,
+6864    /// The TLS handshake was confirmed
+6865    HandshakeConfirmed,
+6866    /// The connection was lost
+6867    ///
+6868    /// Emitted if the peer closes the connection or an error is encountered.
+6869    ConnectionLost {
+6870        /// Reason that the connection was closed
+6871        reason: ConnectionError,
+6872    },
+6873    /// Stream events
+6874    Stream(StreamEvent),
+6875    /// One or more application datagrams have been received
+6876    DatagramReceived,
+6877    /// One or more application datagrams have been sent after blocking
+6878    DatagramsUnblocked,
+6879    /// (Multi)Path events
+6880    Path(PathEvent),
+6881    /// Iroh's nat traversal events
+6882    NatTraversal(iroh_hp::Event),
+6883}
+6884
+6885impl From<PathEvent> for Event {
+6886    fn from(source: PathEvent) -> Self {
+6887        Self::Path(source)
+6888    }
+6889}
+6890
+6891fn get_max_ack_delay(params: &TransportParameters) -> Duration {
+6892    Duration::from_micros(params.max_ack_delay.0 * 1000)
+6893}
+6894
+6895// Prevents overflow and improves behavior in extreme circumstances
+6896const MAX_BACKOFF_EXPONENT: u32 = 16;
+6897
+6898/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag
+6899///
+6900/// This must be at least as large as the header for a well-formed empty packet to be coalesced,
+6901/// plus some space for frames. We only care about handshake headers because short header packets
+6902/// necessarily have smaller headers, and initial packets are only ever the first packet in a
+6903/// datagram (because we coalesce in ascending packet space order and the only reason to split a
+6904/// packet is when packet space changes).
+6905const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32;
+6906
+6907/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header
+6908///
+6909/// Excludes packet-type-specific fields such as packet number or Initial token
+6910// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid +
+6911// scid len + scid + length + pn
+6912const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize =
+6913    1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4;
+6914
+6915/// Perform key updates this many packets before the AEAD confidentiality limit.
+6916///
+6917/// Chosen arbitrarily, intended to be large enough to prevent spurious connection loss.
+6918const KEY_UPDATE_MARGIN: u64 = 10_000;
+6919
+6920#[derive(Default)]
+6921struct SentFrames {
+6922    retransmits: ThinRetransmits,
+6923    /// The packet number of the largest acknowledged packet for each path
+6924    largest_acked: FxHashMap<PathId, u64>,
+6925    stream_frames: StreamMetaVec,
+6926    /// Whether the packet contains non-retransmittable frames (like datagrams)
+6927    non_retransmits: bool,
+6928    /// If the datagram containing these frames should be padded to the min MTU
+6929    requires_padding: bool,
+6930}
+6931
+6932impl SentFrames {
+6933    /// Returns whether the packet contains only ACKs
+6934    fn is_ack_only(&self, streams: &StreamsState) -> bool {
+6935        !self.largest_acked.is_empty()
+6936            && !self.non_retransmits
+6937            && self.stream_frames.is_empty()
+6938            && self.retransmits.is_empty(streams)
+6939    }
+6940
+6941    fn retransmits_mut(&mut self) -> &mut Retransmits {
+6942        self.retransmits.get_or_create()
+6943    }
+6944
+6945    fn record_sent_frame(&mut self, frame: frame::EncodableFrame<'_>) {
+6946        use frame::EncodableFrame::*;
+6947        match frame {
+6948            PathAck(path_ack_encoder) => {
+6949                if let Some(max) = path_ack_encoder.ranges.max() {
+6950                    self.largest_acked.insert(path_ack_encoder.path_id, max);
+6951                }
+6952            }
+6953            Ack(ack_encoder) => {
+6954                if let Some(max) = ack_encoder.ranges.max() {
+6955                    self.largest_acked.insert(PathId::ZERO, max);
+6956                }
+6957            }
+6958            Close(_) => { /* non retransmittable, but after this we don't really care */ }
+6959            PathResponse(_) => self.non_retransmits = true,
+6960            HandshakeDone(_) => self.retransmits_mut().handshake_done = true,
+6961            ReachOut(frame::ReachOut { round, ip, port }) => self
+6962                .retransmits_mut()
+6963                .reach_out
+6964                .get_or_insert_with(|| (round, Vec::new()))
+6965                .1
+6966                .push((ip, port)),
+6967            ObservedAddr(_) => self.retransmits_mut().observed_addr = true,
+6968            Ping(_) => self.non_retransmits = true,
+6969            ImmediateAck(_) => self.non_retransmits = true,
+6970            AckFrequency(_) => self.retransmits_mut().ack_frequency = true,
+6971            PathChallenge(_) => self.non_retransmits = true,
+6972            Crypto(crypto) => self.retransmits_mut().crypto.push_back(crypto),
+6973            PathAbandon(path_abandon) => {
+6974                self.retransmits_mut()
+6975                    .path_abandon
+6976                    .entry(path_abandon.path_id)
+6977                    .or_insert(path_abandon.error_code);
+6978            }
+6979            PathStatusAvailable(frame::PathStatusAvailable { path_id, .. })
+6980            | PathStatusBackup(frame::PathStatusBackup { path_id, .. }) => {
+6981                self.retransmits_mut().path_status.insert(path_id);
+6982            }
+6983            MaxPathId(_) => self.retransmits_mut().max_path_id = true,
+6984            PathsBlocked(_) => self.retransmits_mut().paths_blocked = true,
+6985            PathCidsBlocked(path_cids_blocked) => {
+6986                self.retransmits_mut()
+6987                    .path_cids_blocked
+6988                    .insert(path_cids_blocked.path_id);
+6989            }
+6990            ResetStream(reset) => self
+6991                .retransmits_mut()
+6992                .reset_stream
+6993                .push((reset.id, reset.error_code)),
+6994            StopSending(stop_sending) => self.retransmits_mut().stop_sending.push(stop_sending),
+6995            NewConnectionId(new_cid) => self.retransmits_mut().new_cids.push(new_cid.issued()),
+6996            RetireConnectionId(retire_cid) => self
+6997                .retransmits_mut()
+6998                .retire_cids
+6999                .push((retire_cid.path_id.unwrap_or_default(), retire_cid.sequence)),
+7000            Datagram(_) => self.non_retransmits = true,
+7001            NewToken(_) => {}
+7002            AddAddress(add_address) => {
+7003                self.retransmits_mut().add_address.insert(add_address);
+7004            }
+7005            RemoveAddress(remove_address) => {
+7006                self.retransmits_mut().remove_address.insert(remove_address);
+7007            }
+7008            StreamMeta(stream_meta_encoder) => self.stream_frames.push(stream_meta_encoder.meta),
+7009            MaxData(_) => self.retransmits_mut().max_data = true,
+7010            MaxStreamData(max) => {
+7011                self.retransmits_mut().max_stream_data.insert(max.id);
+7012            }
+7013            MaxStreams(max_streams) => {
+7014                self.retransmits_mut().max_stream_id[max_streams.dir as usize] = true
+7015            }
+7016        }
+7017    }
+7018}
+7019
+7020/// Compute the negotiated idle timeout based on local and remote max_idle_timeout transport parameters.
+7021///
+7022/// According to the definition of max_idle_timeout, a value of `0` means the timeout is disabled; see <https://www.rfc-editor.org/rfc/rfc9000#section-18.2-4.4.1.>
+7023///
+7024/// According to the negotiation procedure, either the minimum of the timeouts or one specified is used as the negotiated value; see <https://www.rfc-editor.org/rfc/rfc9000#section-10.1-2.>
+7025///
+7026/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have opted out of idle timeout.
+7027fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> {
+7028    match (x, y) {
+7029        (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None,
+7030        (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)),
+7031        (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)),
+7032        (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)),
+7033    }
+7034}
+7035
+7036#[cfg(test)]
+7037mod tests {
+7038    use super::*;
+7039
+7040    #[test]
+7041    fn negotiate_max_idle_timeout_commutative() {
+7042        let test_params = [
+7043            (None, None, None),
+7044            (None, Some(VarInt(0)), None),
+7045            (None, Some(VarInt(2)), Some(Duration::from_millis(2))),
+7046            (Some(VarInt(0)), Some(VarInt(0)), None),
+7047            (
+7048                Some(VarInt(2)),
+7049                Some(VarInt(0)),
+7050                Some(Duration::from_millis(2)),
+7051            ),
+7052            (
+7053                Some(VarInt(1)),
+7054                Some(VarInt(4)),
+7055                Some(Duration::from_millis(1)),
+7056            ),
+7057        ];
+7058
+7059        for (left, right, result) in test_params {
+7060            assert_eq!(negotiate_max_idle_timeout(left, right), result);
+7061            assert_eq!(negotiate_max_idle_timeout(right, left), result);
+7062        }
+7063    }
+7064}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/mtud.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/mtud.rs.html new file mode 100644 index 000000000..e94f6f998 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/mtud.rs.html @@ -0,0 +1,980 @@ +mtud.rs - source

iroh_quinn_proto/connection/
mtud.rs

1use crate::{Instant, MAX_UDP_PAYLOAD, MtuDiscoveryConfig, packet::SpaceId};
+2use std::cmp;
+3use tracing::trace;
+4
+5/// Implements Datagram Packetization Layer Path Maximum Transmission Unit Discovery
+6///
+7/// See [`MtuDiscoveryConfig`] for details
+8#[derive(Clone, Debug)]
+9pub(crate) struct MtuDiscovery {
+10    /// Detected MTU for the path
+11    current_mtu: u16,
+12    /// The state of the MTU discovery, if enabled
+13    state: Option<EnabledMtuDiscovery>,
+14    /// The state of the black hole detector
+15    black_hole_detector: BlackHoleDetector,
+16}
+17
+18impl MtuDiscovery {
+19    pub(crate) fn new(
+20        initial_plpmtu: u16,
+21        min_mtu: u16,
+22        peer_max_udp_payload_size: Option<u16>,
+23        config: MtuDiscoveryConfig,
+24    ) -> Self {
+25        debug_assert!(
+26            initial_plpmtu >= min_mtu,
+27            "initial_max_udp_payload_size must be at least {min_mtu}"
+28        );
+29
+30        let mut mtud = Self::with_state(
+31            initial_plpmtu,
+32            min_mtu,
+33            Some(EnabledMtuDiscovery::new(config)),
+34        );
+35
+36        // We might be migrating an existing connection to a new path, in which case the transport
+37        // parameters have already been transmitted, and we already know the value of
+38        // `peer_max_udp_payload_size`
+39        if let Some(peer_max_udp_payload_size) = peer_max_udp_payload_size {
+40            mtud.on_peer_max_udp_payload_size_received(peer_max_udp_payload_size);
+41        }
+42
+43        mtud
+44    }
+45
+46    /// MTU discovery will be disabled and the current MTU will be fixed to the provided value
+47    pub(crate) fn disabled(plpmtu: u16, min_mtu: u16) -> Self {
+48        Self::with_state(plpmtu, min_mtu, None)
+49    }
+50
+51    fn with_state(current_mtu: u16, min_mtu: u16, state: Option<EnabledMtuDiscovery>) -> Self {
+52        Self {
+53            current_mtu,
+54            state,
+55            black_hole_detector: BlackHoleDetector::new(min_mtu),
+56        }
+57    }
+58
+59    /// Returns the current MTU
+60    pub(crate) fn current_mtu(&self) -> u16 {
+61        self.current_mtu
+62    }
+63
+64    /// Returns the amount of bytes that should be sent as an MTU probe, if any.
+65    ///
+66    /// Returns [`None`] if MTUD discovery is disabled. Otherwise delegates to
+67    /// [`EnabledMtuDiscovery::poll_transmit`].
+68    pub(crate) fn poll_transmit(&mut self, now: Instant, next_pn: u64) -> Option<u16> {
+69        self.state
+70            .as_mut()
+71            .and_then(|state| state.poll_transmit(now, self.current_mtu, next_pn))
+72    }
+73
+74    /// Notifies the [`MtuDiscovery`] that the peer's `max_udp_payload_size` transport parameter has
+75    /// been received
+76    pub(crate) fn on_peer_max_udp_payload_size_received(&mut self, peer_max_udp_payload_size: u16) {
+77        self.current_mtu = self.current_mtu.min(peer_max_udp_payload_size);
+78
+79        if let Some(state) = self.state.as_mut() {
+80            // It is possible for black hole detection to trigger before the connection has been
+81            // fully established, if the initial MTU is greater the minimum MTU. We should never
+82            // send probes before the connection has been fully established and we have received
+83            // the peer's transport parameters though.
+84            debug_assert!(
+85                !matches!(state.phase, Phase::Searching(_)),
+86                "Transport parameters received after MTU probing started"
+87            );
+88            state.peer_max_udp_payload_size = peer_max_udp_payload_size;
+89        }
+90    }
+91
+92    /// Notifies the [`MtuDiscovery`] that a packet has been ACKed
+93    ///
+94    /// Returns true if the packet was an MTU probe
+95    pub(crate) fn on_acked(&mut self, space: SpaceId, pn: u64, len: u16) -> bool {
+96        // MTU probes are only sent in application data space
+97        if space != SpaceId::Data {
+98            return false;
+99        }
+100
+101        // Update the state of the MTU search
+102        if let Some(new_mtu) = self
+103            .state
+104            .as_mut()
+105            .and_then(|state| state.on_probe_acked(pn))
+106        {
+107            self.current_mtu = new_mtu;
+108            trace!(current_mtu = self.current_mtu, "new MTU detected");
+109
+110            self.black_hole_detector.on_probe_acked(pn, len);
+111            true
+112        } else {
+113            self.black_hole_detector.on_non_probe_acked(pn, len);
+114            false
+115        }
+116    }
+117
+118    /// Returns the packet number of the in-flight MTU probe, if any
+119    pub(crate) fn in_flight_mtu_probe(&self) -> Option<u64> {
+120        match &self.state {
+121            Some(EnabledMtuDiscovery {
+122                phase: Phase::Searching(search_state),
+123                ..
+124            }) => search_state.in_flight_probe,
+125            _ => None,
+126        }
+127    }
+128
+129    /// Notifies the [`MtuDiscovery`] that the in-flight MTU probe was lost
+130    pub(crate) fn on_probe_lost(&mut self) {
+131        if let Some(state) = &mut self.state {
+132            state.on_probe_lost();
+133        }
+134    }
+135
+136    /// Notifies the [`MtuDiscovery`] that a non-probe packet was lost
+137    ///
+138    /// When done notifying of lost packets, [`MtuDiscovery::black_hole_detected`] must be called, to
+139    /// ensure the last loss burst is properly processed and to trigger black hole recovery logic if
+140    /// necessary.
+141    pub(crate) fn on_non_probe_lost(&mut self, pn: u64, len: u16) {
+142        self.black_hole_detector.on_non_probe_lost(pn, len);
+143    }
+144
+145    /// Returns true if a black hole was detected
+146    ///
+147    /// Calling this function will close the previous loss burst. If a black hole is detected, the
+148    /// current MTU will be reset to `min_mtu`.
+149    pub(crate) fn black_hole_detected(&mut self, now: Instant) -> bool {
+150        if !self.black_hole_detector.black_hole_detected() {
+151            return false;
+152        }
+153
+154        self.current_mtu = self.black_hole_detector.min_mtu;
+155
+156        if let Some(state) = &mut self.state {
+157            state.on_black_hole_detected(now);
+158        }
+159
+160        true
+161    }
+162}
+163
+164/// Additional state for enabled MTU discovery
+165#[derive(Debug, Clone)]
+166struct EnabledMtuDiscovery {
+167    phase: Phase,
+168    peer_max_udp_payload_size: u16,
+169    config: MtuDiscoveryConfig,
+170}
+171
+172impl EnabledMtuDiscovery {
+173    fn new(config: MtuDiscoveryConfig) -> Self {
+174        Self {
+175            phase: Phase::Initial,
+176            peer_max_udp_payload_size: MAX_UDP_PAYLOAD,
+177            config,
+178        }
+179    }
+180
+181    /// Returns the amount of bytes that should be sent as an MTU probe, if any.
+182    ///
+183    /// A probe only needs to be sent if:
+184    ///
+185    /// - There is no current in-flight probe.
+186    /// - A search for a new MTU is in progress.
+187    /// - The MTU discovery was completed but the [`MtuDiscoveryConfig::interval`] expired,
+188    ///   this re-starts a n MTU search.
+189    fn poll_transmit(&mut self, now: Instant, current_mtu: u16, next_pn: u64) -> Option<u16> {
+190        if let Phase::Initial = &self.phase {
+191            // Start the first search
+192            self.phase = Phase::Searching(SearchState::new(
+193                current_mtu,
+194                self.peer_max_udp_payload_size,
+195                &self.config,
+196            ));
+197        } else if let Phase::Complete(next_mtud_activation) = &self.phase {
+198            if now < *next_mtud_activation {
+199                return None;
+200            }
+201
+202            // Start a new search (we have reached the next activation time)
+203            self.phase = Phase::Searching(SearchState::new(
+204                current_mtu,
+205                self.peer_max_udp_payload_size,
+206                &self.config,
+207            ));
+208        }
+209
+210        if let Phase::Searching(state) = &mut self.phase {
+211            // Nothing to do while there is a probe in flight
+212            if state.in_flight_probe.is_some() {
+213                return None;
+214            }
+215
+216            // Retransmit lost probes, if any
+217            if 0 < state.lost_probe_count && state.lost_probe_count < MAX_PROBE_RETRANSMITS {
+218                state.in_flight_probe = Some(next_pn);
+219                return Some(state.last_probed_mtu);
+220            }
+221
+222            let last_probe_succeeded = state.lost_probe_count == 0;
+223
+224            // The probe is definitely lost (we reached the MAX_PROBE_RETRANSMITS threshold)
+225            if !last_probe_succeeded {
+226                state.lost_probe_count = 0;
+227                state.in_flight_probe = None;
+228            }
+229
+230            if let Some(probe_udp_payload_size) = state.next_mtu_to_probe(last_probe_succeeded) {
+231                state.in_flight_probe = Some(next_pn);
+232                state.last_probed_mtu = probe_udp_payload_size;
+233                return Some(probe_udp_payload_size);
+234            } else {
+235                let next_mtud_activation = now + self.config.interval;
+236                self.phase = Phase::Complete(next_mtud_activation);
+237                return None;
+238            }
+239        }
+240
+241        None
+242    }
+243
+244    /// Called when a packet is acknowledged in [`SpaceId::Data`]
+245    ///
+246    /// Returns the new `current_mtu` if the packet number corresponds to the in-flight MTU probe
+247    fn on_probe_acked(&mut self, pn: u64) -> Option<u16> {
+248        match &mut self.phase {
+249            Phase::Searching(state) if state.in_flight_probe == Some(pn) => {
+250                state.in_flight_probe = None;
+251                state.lost_probe_count = 0;
+252                Some(state.last_probed_mtu)
+253            }
+254            _ => None,
+255        }
+256    }
+257
+258    /// Called when the in-flight MTU probe was lost
+259    fn on_probe_lost(&mut self) {
+260        // We might no longer be searching, e.g. if a black hole was detected
+261        if let Phase::Searching(state) = &mut self.phase {
+262            state.in_flight_probe = None;
+263            state.lost_probe_count += 1;
+264        }
+265    }
+266
+267    /// Called when a black hole is detected
+268    fn on_black_hole_detected(&mut self, now: Instant) {
+269        // Stop searching, if applicable, and reset the timer
+270        let next_mtud_activation = now + self.config.black_hole_cooldown;
+271        self.phase = Phase::Complete(next_mtud_activation);
+272    }
+273}
+274
+275#[derive(Debug, Clone, Copy)]
+276enum Phase {
+277    /// We haven't started polling yet
+278    Initial,
+279    /// We are currently searching for a higher PMTU
+280    Searching(SearchState),
+281    /// Searching has completed and will be triggered again at the provided instant
+282    Complete(Instant),
+283}
+284
+285#[derive(Debug, Clone, Copy)]
+286struct SearchState {
+287    /// The lower bound for the current binary search
+288    lower_bound: u16,
+289    /// The upper bound for the current binary search
+290    upper_bound: u16,
+291    /// The minimum change to stop the current binary search
+292    minimum_change: u16,
+293    /// The UDP payload size we last sent a probe for
+294    last_probed_mtu: u16,
+295    /// Packet number of an in-flight probe (if any)
+296    in_flight_probe: Option<u64>,
+297    /// Lost probes at the current probe size
+298    lost_probe_count: usize,
+299}
+300
+301impl SearchState {
+302    /// Creates a new search state, with the specified lower bound (the upper bound is derived from
+303    /// the config and the peer's `max_udp_payload_size` transport parameter)
+304    fn new(
+305        mut lower_bound: u16,
+306        peer_max_udp_payload_size: u16,
+307        config: &MtuDiscoveryConfig,
+308    ) -> Self {
+309        lower_bound = lower_bound.min(peer_max_udp_payload_size);
+310        let upper_bound = config
+311            .upper_bound
+312            .clamp(lower_bound, peer_max_udp_payload_size);
+313
+314        Self {
+315            in_flight_probe: None,
+316            lost_probe_count: 0,
+317            lower_bound,
+318            upper_bound,
+319            minimum_change: config.minimum_change,
+320            // During initialization, we consider the lower bound to have already been
+321            // successfully probed
+322            last_probed_mtu: lower_bound,
+323        }
+324    }
+325
+326    /// Determines the next MTU to probe using binary search
+327    fn next_mtu_to_probe(&mut self, last_probe_succeeded: bool) -> Option<u16> {
+328        debug_assert_eq!(self.in_flight_probe, None);
+329
+330        if last_probe_succeeded {
+331            self.lower_bound = self.last_probed_mtu;
+332        } else {
+333            self.upper_bound = self.last_probed_mtu - 1;
+334        }
+335
+336        let next_mtu = (self.lower_bound as i32 + self.upper_bound as i32) / 2;
+337
+338        // Binary search stopping condition
+339        if ((next_mtu - self.last_probed_mtu as i32).unsigned_abs() as u16) < self.minimum_change {
+340            // Special case: if the upper bound is far enough, we want to probe it as a last
+341            // step (otherwise we will never achieve the upper bound)
+342            if self.upper_bound.saturating_sub(self.last_probed_mtu) >= self.minimum_change {
+343                return Some(self.upper_bound);
+344            }
+345
+346            return None;
+347        }
+348
+349        Some(next_mtu as u16)
+350    }
+351}
+352
+353/// Judges whether packet loss might indicate a drop in MTU
+354///
+355/// Our MTU black hole detection scheme is a heuristic based on the order in which packets were sent
+356/// (the packet number order), their sizes, and which are deemed lost.
+357///
+358/// First, contiguous groups of lost packets ("loss bursts") are aggregated, because a group of
+359/// packets all lost together were probably lost for the same reason.
+360///
+361/// A loss burst is deemed "suspicious" if it contains no packets that are (a) smaller than the
+362/// minimum MTU or (b) smaller than a more recent acknowledged packet, because such a burst could be
+363/// fully explained by a reduction in MTU.
+364///
+365/// When the number of suspicious loss bursts exceeds [`BLACK_HOLE_THRESHOLD`], we judge the
+366/// evidence for an MTU black hole to be sufficient.
+367#[derive(Clone, Debug)]
+368struct BlackHoleDetector {
+369    /// Packet loss bursts currently considered suspicious
+370    suspicious_loss_bursts: Vec<LossBurst>,
+371    /// Loss burst currently being aggregated, if any
+372    current_loss_burst: Option<CurrentLossBurst>,
+373    /// Packet number of the biggest packet larger than `min_mtu` which we've received
+374    /// acknowledgment of more recently than any suspicious loss burst, if any
+375    largest_post_loss_packet: u64,
+376    /// The maximum of `min_mtu` and the size of `largest_post_loss_packet`, or exactly `min_mtu` if
+377    /// no larger packets have been received since the most recent loss burst.
+378    acked_mtu: u16,
+379    /// The UDP payload size guaranteed to be supported by the network
+380    min_mtu: u16,
+381}
+382
+383impl BlackHoleDetector {
+384    fn new(min_mtu: u16) -> Self {
+385        Self {
+386            suspicious_loss_bursts: Vec::with_capacity(BLACK_HOLE_THRESHOLD + 1),
+387            current_loss_burst: None,
+388            largest_post_loss_packet: 0,
+389            acked_mtu: min_mtu,
+390            min_mtu,
+391        }
+392    }
+393
+394    fn on_probe_acked(&mut self, pn: u64, len: u16) {
+395        // MTU probes are always larger than the previous MTU, so no previous loss bursts are
+396        // suspicious. At most one MTU probe is in flight at a time, so we don't need to worry about
+397        // reordering between them.
+398        self.suspicious_loss_bursts.clear();
+399        self.acked_mtu = len;
+400        // This might go backwards, but that's okay: a successful ACK means we haven't yet judged a
+401        // more recently sent packet lost, and we just want to track the largest packet that's been
+402        // successfully delivered more recently than a loss.
+403        self.largest_post_loss_packet = pn;
+404    }
+405
+406    fn on_non_probe_acked(&mut self, pn: u64, len: u16) {
+407        if len <= self.acked_mtu {
+408            // We've already seen a larger packet since the most recent suspicious loss burst;
+409            // nothing to do.
+410            return;
+411        }
+412        self.acked_mtu = len;
+413        // This might go backwards, but that's okay as described in `on_probe_acked`.
+414        self.largest_post_loss_packet = pn;
+415        // Loss bursts packets smaller than this are retroactively deemed non-suspicious.
+416        self.suspicious_loss_bursts
+417            .retain(|burst| burst.smallest_packet_size > len);
+418    }
+419
+420    fn on_non_probe_lost(&mut self, pn: u64, len: u16) {
+421        // A loss burst is a group of consecutive packets that are declared lost, so a distance
+422        // greater than 1 indicates a new burst
+423        let end_last_burst = self
+424            .current_loss_burst
+425            .as_ref()
+426            .is_some_and(|current| pn - current.latest_non_probe != 1);
+427
+428        if end_last_burst {
+429            self.finish_loss_burst();
+430        }
+431
+432        self.current_loss_burst = Some(CurrentLossBurst {
+433            latest_non_probe: pn,
+434            smallest_packet_size: self
+435                .current_loss_burst
+436                .map_or(len, |prev| cmp::min(prev.smallest_packet_size, len)),
+437        });
+438    }
+439
+440    fn black_hole_detected(&mut self) -> bool {
+441        self.finish_loss_burst();
+442
+443        if self.suspicious_loss_bursts.len() <= BLACK_HOLE_THRESHOLD {
+444            return false;
+445        }
+446
+447        self.suspicious_loss_bursts.clear();
+448
+449        true
+450    }
+451
+452    /// Marks the end of the current loss burst, checking whether it was suspicious
+453    fn finish_loss_burst(&mut self) {
+454        let Some(burst) = self.current_loss_burst.take() else {
+455            return;
+456        };
+457        // If a loss burst contains a packet smaller than the minimum MTU or a more recently
+458        // transmitted packet, it is not suspicious.
+459        if burst.smallest_packet_size <= self.min_mtu
+460            || (burst.latest_non_probe < self.largest_post_loss_packet
+461                && burst.smallest_packet_size <= self.acked_mtu)
+462        {
+463            return;
+464        }
+465        // The loss burst is now deemed suspicious.
+466
+467        // A suspicious loss burst more recent than `largest_post_loss_packet` invalidates it. This
+468        // makes `acked_mtu` a conservative approximation. Ideally we'd update `safe_mtu` and
+469        // `largest_post_loss_packet` to describe the largest acknowledged packet sent later than
+470        // this burst, but that would require tracking the size of an unpredictable number of
+471        // recently acknowledged packets, and erring on the side of false positives is safe.
+472        if burst.latest_non_probe > self.largest_post_loss_packet {
+473            self.acked_mtu = self.min_mtu;
+474        }
+475
+476        let burst = LossBurst {
+477            smallest_packet_size: burst.smallest_packet_size,
+478        };
+479
+480        if self.suspicious_loss_bursts.len() <= BLACK_HOLE_THRESHOLD {
+481            self.suspicious_loss_bursts.push(burst);
+482            return;
+483        }
+484
+485        // To limit memory use, only track the most suspicious loss bursts.
+486        let smallest = self
+487            .suspicious_loss_bursts
+488            .iter_mut()
+489            .min_by_key(|prev| prev.smallest_packet_size)
+490            .filter(|prev| prev.smallest_packet_size < burst.smallest_packet_size);
+491        if let Some(smallest) = smallest {
+492            *smallest = burst;
+493        }
+494    }
+495
+496    #[cfg(test)]
+497    fn suspicious_loss_burst_count(&self) -> usize {
+498        self.suspicious_loss_bursts.len()
+499    }
+500
+501    #[cfg(test)]
+502    fn largest_non_probe_lost(&self) -> Option<u64> {
+503        self.current_loss_burst.as_ref().map(|x| x.latest_non_probe)
+504    }
+505}
+506
+507#[derive(Copy, Clone, Debug)]
+508struct LossBurst {
+509    smallest_packet_size: u16,
+510}
+511
+512#[derive(Copy, Clone, Debug)]
+513struct CurrentLossBurst {
+514    smallest_packet_size: u16,
+515    latest_non_probe: u64,
+516}
+517
+518// Corresponds to the RFC's `MAX_PROBES` constant (see
+519// https://www.rfc-editor.org/rfc/rfc8899#section-5.1.2)
+520const MAX_PROBE_RETRANSMITS: usize = 3;
+521/// Maximum number of suspicious loss bursts that will not trigger black hole detection
+522const BLACK_HOLE_THRESHOLD: usize = 3;
+523
+524#[cfg(test)]
+525mod tests {
+526    use super::*;
+527    use crate::Duration;
+528    use crate::MAX_UDP_PAYLOAD;
+529    use crate::packet::SpaceId;
+530    use assert_matches::assert_matches;
+531
+532    fn default_mtud() -> MtuDiscovery {
+533        let config = MtuDiscoveryConfig::default();
+534        MtuDiscovery::new(1_200, 1_200, None, config)
+535    }
+536
+537    fn completed(mtud: &MtuDiscovery) -> bool {
+538        matches!(mtud.state.as_ref().unwrap().phase, Phase::Complete(_))
+539    }
+540
+541    /// Drives mtud until it reaches `Phase::Completed`
+542    fn drive_to_completion(
+543        mtud: &mut MtuDiscovery,
+544        now: Instant,
+545        link_payload_size_limit: u16,
+546    ) -> Vec<u16> {
+547        let mut probed_sizes = Vec::new();
+548        for probe_pn in 1..100 {
+549            let result = mtud.poll_transmit(now, probe_pn);
+550
+551            if completed(mtud) {
+552                break;
+553            }
+554
+555            // "Send" next probe
+556            assert!(result.is_some());
+557            let probe_size = result.unwrap();
+558            probed_sizes.push(probe_size);
+559
+560            if probe_size <= link_payload_size_limit {
+561                mtud.on_acked(SpaceId::Data, probe_pn, probe_size);
+562            } else {
+563                mtud.on_probe_lost();
+564            }
+565        }
+566        probed_sizes
+567    }
+568
+569    #[test]
+570    fn black_hole_detector_ignores_burst_containing_non_suspicious_packet() {
+571        let mut mtud = default_mtud();
+572        mtud.on_non_probe_lost(2, 1300);
+573        mtud.on_non_probe_lost(3, 1300);
+574        assert_eq!(mtud.black_hole_detector.largest_non_probe_lost(), Some(3));
+575        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 0);
+576
+577        mtud.on_non_probe_lost(4, 800);
+578        assert!(!mtud.black_hole_detected(Instant::now()));
+579        assert_eq!(mtud.black_hole_detector.largest_non_probe_lost(), None);
+580        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 0);
+581    }
+582
+583    #[test]
+584    fn black_hole_detector_counts_burst_containing_only_suspicious_packets() {
+585        let mut mtud = default_mtud();
+586        mtud.on_non_probe_lost(2, 1300);
+587        mtud.on_non_probe_lost(3, 1300);
+588        assert_eq!(mtud.black_hole_detector.largest_non_probe_lost(), Some(3));
+589        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 0);
+590
+591        assert!(!mtud.black_hole_detected(Instant::now()));
+592        assert_eq!(mtud.black_hole_detector.largest_non_probe_lost(), None);
+593        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 1);
+594    }
+595
+596    #[test]
+597    fn black_hole_detector_ignores_empty_burst() {
+598        let mut mtud = default_mtud();
+599        assert!(!mtud.black_hole_detected(Instant::now()));
+600        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 0);
+601    }
+602
+603    #[test]
+604    fn mtu_discovery_disabled_does_nothing() {
+605        let mut mtud = MtuDiscovery::disabled(1_200, 1_200);
+606        let probe_size = mtud.poll_transmit(Instant::now(), 0);
+607        assert_eq!(probe_size, None);
+608    }
+609
+610    #[test]
+611    fn mtu_discovery_disabled_lost_four_packet_bursts_triggers_black_hole_detection() {
+612        let mut mtud = MtuDiscovery::disabled(1_400, 1_250);
+613        let now = Instant::now();
+614
+615        for i in 0..4 {
+616            // The packets are never contiguous, so each one has its own burst
+617            mtud.on_non_probe_lost(i * 2, 1300);
+618        }
+619
+620        assert!(mtud.black_hole_detected(now));
+621        assert_eq!(mtud.current_mtu, 1250);
+622        assert_matches!(mtud.state, None);
+623    }
+624
+625    #[test]
+626    fn mtu_discovery_lost_two_packet_bursts_does_not_trigger_black_hole_detection() {
+627        let mut mtud = default_mtud();
+628        let now = Instant::now();
+629
+630        for i in 0..2 {
+631            mtud.on_non_probe_lost(i, 1300);
+632            assert!(!mtud.black_hole_detected(now));
+633        }
+634    }
+635
+636    #[test]
+637    fn mtu_discovery_lost_four_packet_bursts_triggers_black_hole_detection_and_resets_timer() {
+638        let mut mtud = default_mtud();
+639        let now = Instant::now();
+640
+641        for i in 0..4 {
+642            // The packets are never contiguous, so each one has its own burst
+643            mtud.on_non_probe_lost(i * 2, 1300);
+644        }
+645
+646        assert!(mtud.black_hole_detected(now));
+647        assert_eq!(mtud.current_mtu, 1200);
+648        if let Phase::Complete(next_mtud_activation) = mtud.state.unwrap().phase {
+649            assert_eq!(next_mtud_activation, now + Duration::from_secs(60));
+650        } else {
+651            panic!("Unexpected MTUD phase!");
+652        }
+653    }
+654
+655    #[test]
+656    fn mtu_discovery_after_complete_reactivates_when_interval_elapsed() {
+657        let mut config = MtuDiscoveryConfig::default();
+658        config.upper_bound(9_000);
+659        let mut mtud = MtuDiscovery::new(1_200, 1_200, None, config);
+660        let now = Instant::now();
+661        drive_to_completion(&mut mtud, now, 1_500);
+662
+663        // Polling right after completion does not cause new packets to be sent
+664        assert_eq!(mtud.poll_transmit(now, 42), None);
+665        assert!(completed(&mtud));
+666        assert_eq!(mtud.current_mtu, 1_471);
+667
+668        // Polling after the interval has passed does (taking the current mtu as lower bound)
+669        assert_eq!(
+670            mtud.poll_transmit(now + Duration::from_secs(600), 43),
+671            Some(5235)
+672        );
+673
+674        match mtud.state.unwrap().phase {
+675            Phase::Searching(state) => {
+676                assert_eq!(state.lower_bound, 1_471);
+677                assert_eq!(state.upper_bound, 9_000);
+678            }
+679            _ => {
+680                panic!("Unexpected MTUD phase!")
+681            }
+682        }
+683    }
+684
+685    #[test]
+686    fn mtu_discovery_lost_three_probes_lowers_probe_size() {
+687        let mut mtud = default_mtud();
+688
+689        let mut probe_sizes = (0..4).map(|i| {
+690            let probe_size = mtud.poll_transmit(Instant::now(), i);
+691            assert!(probe_size.is_some(), "no probe returned for packet {i}");
+692
+693            mtud.on_probe_lost();
+694            probe_size.unwrap()
+695        });
+696
+697        // After the first probe is lost, it gets retransmitted twice
+698        let first_probe_size = probe_sizes.next().unwrap();
+699        for _ in 0..2 {
+700            assert_eq!(probe_sizes.next().unwrap(), first_probe_size)
+701        }
+702
+703        // After the third probe is lost, we decrement our probe size
+704        let fourth_probe_size = probe_sizes.next().unwrap();
+705        assert!(fourth_probe_size < first_probe_size);
+706        assert_eq!(
+707            fourth_probe_size,
+708            first_probe_size - (first_probe_size - 1_200) / 2 - 1
+709        );
+710    }
+711
+712    #[test]
+713    fn mtu_discovery_with_peer_max_udp_payload_size_clamps_upper_bound() {
+714        let mut mtud = default_mtud();
+715
+716        mtud.on_peer_max_udp_payload_size_received(1300);
+717        let probed_sizes = drive_to_completion(&mut mtud, Instant::now(), 1500);
+718
+719        assert_eq!(mtud.state.as_ref().unwrap().peer_max_udp_payload_size, 1300);
+720        assert_eq!(mtud.current_mtu, 1300);
+721        let expected_probed_sizes = &[1250, 1275, 1300];
+722        assert_eq!(probed_sizes, expected_probed_sizes);
+723        assert!(completed(&mtud));
+724    }
+725
+726    #[test]
+727    fn mtu_discovery_with_previous_peer_max_udp_payload_size_clamps_upper_bound() {
+728        let mut mtud = MtuDiscovery::new(1500, 1_200, Some(1400), MtuDiscoveryConfig::default());
+729
+730        assert_eq!(mtud.current_mtu, 1400);
+731        assert_eq!(mtud.state.as_ref().unwrap().peer_max_udp_payload_size, 1400);
+732
+733        let probed_sizes = drive_to_completion(&mut mtud, Instant::now(), 1500);
+734
+735        assert_eq!(mtud.current_mtu, 1400);
+736        assert!(probed_sizes.is_empty());
+737        assert!(completed(&mtud));
+738    }
+739
+740    #[cfg(debug_assertions)]
+741    #[test]
+742    #[should_panic(expected = "Transport parameters received after MTU probing started")]
+743    fn mtu_discovery_with_peer_max_udp_payload_size_during_search_panics() {
+744        let mut mtud = default_mtud();
+745        assert!(mtud.poll_transmit(Instant::now(), 0).is_some());
+746        assert!(matches!(
+747            mtud.state.as_ref().unwrap().phase,
+748            Phase::Searching(_)
+749        ));
+750        mtud.on_peer_max_udp_payload_size_received(1300);
+751    }
+752
+753    #[test]
+754    fn mtu_discovery_with_1500_limit() {
+755        let mut mtud = default_mtud();
+756
+757        let probed_sizes = drive_to_completion(&mut mtud, Instant::now(), 1500);
+758
+759        let expected_probed_sizes = &[1326, 1389, 1420, 1452];
+760        assert_eq!(probed_sizes, expected_probed_sizes);
+761        assert_eq!(mtud.current_mtu, 1452);
+762        assert!(completed(&mtud));
+763    }
+764
+765    #[test]
+766    fn mtu_discovery_with_1500_limit_and_10000_upper_bound() {
+767        let mut config = MtuDiscoveryConfig::default();
+768        config.upper_bound(10_000);
+769        let mut mtud = MtuDiscovery::new(1_200, 1_200, None, config);
+770
+771        let probed_sizes = drive_to_completion(&mut mtud, Instant::now(), 1500);
+772
+773        let expected_probed_sizes = &[
+774            5600, 5600, 5600, 3399, 3399, 3399, 2299, 2299, 2299, 1749, 1749, 1749, 1474, 1611,
+775            1611, 1611, 1542, 1542, 1542, 1507, 1507, 1507,
+776        ];
+777        assert_eq!(probed_sizes, expected_probed_sizes);
+778        assert_eq!(mtud.current_mtu, 1474);
+779        assert!(completed(&mtud));
+780    }
+781
+782    #[test]
+783    fn mtu_discovery_no_lost_probes_finds_maximum_udp_payload() {
+784        let mut config = MtuDiscoveryConfig::default();
+785        config.upper_bound(MAX_UDP_PAYLOAD);
+786        let mut mtud = MtuDiscovery::new(1200, 1200, None, config);
+787
+788        drive_to_completion(&mut mtud, Instant::now(), u16::MAX);
+789
+790        assert_eq!(mtud.current_mtu, 65527);
+791        assert!(completed(&mtud));
+792    }
+793
+794    #[test]
+795    fn mtu_discovery_lost_half_of_probes_finds_maximum_udp_payload() {
+796        let mut config = MtuDiscoveryConfig::default();
+797        config.upper_bound(MAX_UDP_PAYLOAD);
+798        let mut mtud = MtuDiscovery::new(1200, 1200, None, config);
+799
+800        let now = Instant::now();
+801        let mut iterations = 0;
+802        for i in 1..100 {
+803            iterations += 1;
+804
+805            let probe_pn = i * 2 - 1;
+806            let other_pn = i * 2;
+807
+808            let result = mtud.poll_transmit(Instant::now(), probe_pn);
+809
+810            if completed(&mtud) {
+811                break;
+812            }
+813
+814            // "Send" next probe
+815            assert!(result.is_some());
+816            assert!(mtud.in_flight_mtu_probe().is_some());
+817
+818            // Nothing else to send while the probe is in-flight
+819            assert_matches!(mtud.poll_transmit(now, other_pn), None);
+820
+821            if i % 2 == 0 {
+822                // ACK probe and ensure it results in an increase of current_mtu
+823                let previous_max_size = mtud.current_mtu;
+824                mtud.on_acked(SpaceId::Data, probe_pn, result.unwrap());
+825                println!(
+826                    "ACK packet {}. Previous MTU = {previous_max_size}. New MTU = {}",
+827                    result.unwrap(),
+828                    mtud.current_mtu
+829                );
+830                // assert!(mtud.current_mtu > previous_max_size);
+831            } else {
+832                mtud.on_probe_lost();
+833            }
+834        }
+835
+836        assert_eq!(iterations, 25);
+837        assert_eq!(mtud.current_mtu, 65527);
+838        assert!(completed(&mtud));
+839    }
+840
+841    #[test]
+842    fn search_state_lower_bound_higher_than_upper_bound_clamps_upper_bound() {
+843        let mut config = MtuDiscoveryConfig::default();
+844        config.upper_bound(1400);
+845
+846        let state = SearchState::new(1500, u16::MAX, &config);
+847        assert_eq!(state.lower_bound, 1500);
+848        assert_eq!(state.upper_bound, 1500);
+849    }
+850
+851    #[test]
+852    fn search_state_lower_bound_higher_than_peer_max_udp_payload_size_clamps_lower_bound() {
+853        let mut config = MtuDiscoveryConfig::default();
+854        config.upper_bound(9000);
+855
+856        let state = SearchState::new(1500, 1300, &config);
+857        assert_eq!(state.lower_bound, 1300);
+858        assert_eq!(state.upper_bound, 1300);
+859    }
+860
+861    #[test]
+862    fn search_state_upper_bound_higher_than_peer_max_udp_payload_size_clamps_upper_bound() {
+863        let mut config = MtuDiscoveryConfig::default();
+864        config.upper_bound(9000);
+865
+866        let state = SearchState::new(1200, 1450, &config);
+867        assert_eq!(state.lower_bound, 1200);
+868        assert_eq!(state.upper_bound, 1450);
+869    }
+870
+871    // Loss of packets larger than have been acknowledged should indicate a black hole
+872    #[test]
+873    fn simple_black_hole_detection() {
+874        let mut bhd = BlackHoleDetector::new(1200);
+875        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 2, 1300);
+876        for i in 0..BLACK_HOLE_THRESHOLD {
+877            bhd.on_non_probe_lost(i as u64 * 2, 1400);
+878        }
+879        // But not before `BLACK_HOLE_THRESHOLD + 1` bursts
+880        assert!(!bhd.black_hole_detected());
+881        bhd.on_non_probe_lost(BLACK_HOLE_THRESHOLD as u64 * 2, 1400);
+882        assert!(bhd.black_hole_detected());
+883    }
+884
+885    // Loss of packets followed in transmission order by confirmation of a larger packet should not
+886    // indicate a black hole
+887    #[test]
+888    fn non_suspicious_bursts() {
+889        let mut bhd = BlackHoleDetector::new(1200);
+890        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 2, 1500);
+891        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+892            bhd.on_non_probe_lost(i as u64 * 2, 1400);
+893        }
+894        assert!(!bhd.black_hole_detected());
+895    }
+896
+897    // Loss of packets smaller than have been acknowledged previously should still indicate a black
+898    // hole
+899    #[test]
+900    fn dynamic_mtu_reduction() {
+901        let mut bhd = BlackHoleDetector::new(1200);
+902        bhd.on_non_probe_acked(0, 1500);
+903        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+904            bhd.on_non_probe_lost(i as u64 * 2, 1400);
+905        }
+906        assert!(bhd.black_hole_detected());
+907    }
+908
+909    // Bursts containing heterogeneous packets are judged based on the smallest
+910    #[test]
+911    fn mixed_non_suspicious_bursts() {
+912        let mut bhd = BlackHoleDetector::new(1200);
+913        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 3, 1400);
+914        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+915            bhd.on_non_probe_lost(i as u64 * 3, 1500);
+916            bhd.on_non_probe_lost(i as u64 * 3 + 1, 1300);
+917        }
+918        assert!(!bhd.black_hole_detected());
+919    }
+920
+921    // Multi-packet bursts are only counted once
+922    #[test]
+923    fn bursts_count_once() {
+924        let mut bhd = BlackHoleDetector::new(1200);
+925        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 3, 1400);
+926        for i in 0..(BLACK_HOLE_THRESHOLD) {
+927            bhd.on_non_probe_lost(i as u64 * 3, 1500);
+928            bhd.on_non_probe_lost(i as u64 * 3 + 1, 1500);
+929        }
+930        assert!(!bhd.black_hole_detected());
+931        bhd.on_non_probe_lost(BLACK_HOLE_THRESHOLD as u64 * 3, 1500);
+932        assert!(bhd.black_hole_detected());
+933    }
+934
+935    // Non-suspicious bursts don't interfere with detection of suspicious bursts
+936    #[test]
+937    fn interleaved_bursts() {
+938        let mut bhd = BlackHoleDetector::new(1200);
+939        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 4, 1400);
+940        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+941            bhd.on_non_probe_lost(i as u64 * 4, 1500);
+942            bhd.on_non_probe_lost(i as u64 * 4 + 2, 1300);
+943        }
+944        assert!(bhd.black_hole_detected());
+945    }
+946
+947    // Bursts that are non-suspicious before a delivered packet become suspicious past it
+948    #[test]
+949    fn suspicious_after_acked() {
+950        let mut bhd = BlackHoleDetector::new(1200);
+951        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 2, 1400);
+952        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+953            bhd.on_non_probe_lost(i as u64 * 2, 1300);
+954        }
+955        assert!(
+956            !bhd.black_hole_detected(),
+957            "1300 byte losses preceding a 1400 byte delivery are not suspicious"
+958        );
+959        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+960            bhd.on_non_probe_lost((BLACK_HOLE_THRESHOLD as u64 + 1 + i as u64) * 2, 1300);
+961        }
+962        assert!(
+963            bhd.black_hole_detected(),
+964            "1300 byte losses following a 1400 byte delivery are suspicious"
+965        );
+966    }
+967
+968    // Acknowledgment of a packet marks prior loss bursts with the same packet size as
+969    // non-suspicious
+970    #[test]
+971    fn retroactively_non_suspicious() {
+972        let mut bhd = BlackHoleDetector::new(1200);
+973        for i in 0..BLACK_HOLE_THRESHOLD {
+974            bhd.on_non_probe_lost(i as u64 * 2, 1400);
+975        }
+976        bhd.on_non_probe_acked(BLACK_HOLE_THRESHOLD as u64 * 2, 1400);
+977        bhd.on_non_probe_lost(BLACK_HOLE_THRESHOLD as u64 * 2 + 1, 1400);
+978        assert!(!bhd.black_hole_detected());
+979    }
+980}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/pacing.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/pacing.rs.html new file mode 100644 index 000000000..19fabe57a --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/pacing.rs.html @@ -0,0 +1,325 @@ +pacing.rs - source

iroh_quinn_proto/connection/
pacing.rs

1//! Pacing of packet transmissions.
+2
+3use crate::{Duration, Instant};
+4
+5use tracing::warn;
+6
+7/// A simple token-bucket pacer
+8///
+9/// The pacer's capacity is derived on a fraction of the congestion window
+10/// which can be sent in regular intervals
+11/// Once the bucket is empty, further transmission is blocked.
+12/// The bucket refills at a rate slightly faster
+13/// than one congestion window per RTT, as recommended in
+14/// <https://tools.ietf.org/html/draft-ietf-quic-recovery-34#section-7.7>
+15#[derive(Debug)]
+16pub(super) struct Pacer {
+17    capacity: u64,
+18    last_window: u64,
+19    last_mtu: u16,
+20    tokens: u64,
+21    prev: Instant,
+22}
+23
+24impl Pacer {
+25    /// Obtains a new [`Pacer`].
+26    pub(super) fn new(smoothed_rtt: Duration, window: u64, mtu: u16, now: Instant) -> Self {
+27        let capacity = optimal_capacity(smoothed_rtt, window, mtu);
+28        Self {
+29            capacity,
+30            last_window: window,
+31            last_mtu: mtu,
+32            tokens: capacity,
+33            prev: now,
+34        }
+35    }
+36
+37    /// Record that a packet has been transmitted.
+38    pub(super) fn on_transmit(&mut self, packet_length: u16) {
+39        self.tokens = self.tokens.saturating_sub(packet_length.into())
+40    }
+41
+42    /// Return how long we need to wait before sending `bytes_to_send`
+43    ///
+44    /// If we can send a packet right away, this returns `None`. Otherwise, returns `Some(d)`,
+45    /// where `d` is the time before this function should be called again.
+46    ///
+47    /// The 5/4 ratio used here comes from the suggestion that N = 1.25 in the draft IETF RFC for
+48    /// QUIC.
+49    pub(super) fn delay(
+50        &mut self,
+51        smoothed_rtt: Duration,
+52        bytes_to_send: u64,
+53        mtu: u16,
+54        window: u64,
+55        now: Instant,
+56    ) -> Option<Instant> {
+57        debug_assert_ne!(
+58            window, 0,
+59            "zero-sized congestion control window is nonsense"
+60        );
+61
+62        if window != self.last_window || mtu != self.last_mtu {
+63            self.capacity = optimal_capacity(smoothed_rtt, window, mtu);
+64
+65            // Clamp the tokens
+66            self.tokens = self.capacity.min(self.tokens);
+67            self.last_window = window;
+68            self.last_mtu = mtu;
+69        }
+70
+71        // if we can already send a packet, there is no need for delay
+72        if self.tokens >= bytes_to_send {
+73            return None;
+74        }
+75
+76        // we disable pacing for extremely large windows
+77        if window > u64::from(u32::MAX) {
+78            return None;
+79        }
+80
+81        let window = window as u32;
+82
+83        let time_elapsed = now.checked_duration_since(self.prev).unwrap_or_else(|| {
+84            warn!("received a timestamp early than a previous recorded time, ignoring");
+85            Default::default()
+86        });
+87
+88        if smoothed_rtt.as_nanos() == 0 {
+89            return None;
+90        }
+91
+92        let elapsed_rtts = time_elapsed.as_secs_f64() / smoothed_rtt.as_secs_f64();
+93        let new_tokens = (window as f64 * 1.25 * elapsed_rtts).round() as u64;
+94        self.tokens = self.tokens.saturating_add(new_tokens).min(self.capacity);
+95
+96        // In the unlikely event that we're getting polled faster than tokens are generated, ensure
+97        // that `elapsed_rtts` can grow until we make progress.
+98        if new_tokens > 0 {
+99            self.prev = now;
+100        }
+101
+102        // if we can already send a packet, there is no need for delay
+103        if self.tokens >= bytes_to_send {
+104            return None;
+105        }
+106
+107        let unscaled_delay = smoothed_rtt
+108            .checked_mul((bytes_to_send.max(self.capacity) - self.tokens) as _)
+109            .unwrap_or(Duration::MAX)
+110            / window;
+111
+112        // divisions come before multiplications to prevent overflow
+113        // this is the time at which the pacing window becomes empty
+114        Some(now + (unscaled_delay / 5) * 4)
+115    }
+116}
+117
+118/// Calculates a pacer capacity for a certain window and RTT
+119///
+120/// The goal is to emit a burst (of size `capacity`) in timer intervals
+121/// which compromise between
+122/// - ideally distributing datagrams over time
+123/// - constantly waking up the connection to produce additional datagrams
+124///
+125/// Too short burst intervals means we will never meet them since the timer
+126/// accuracy in user-space is not high enough. If we miss the interval by more
+127/// than 25%, we will lose that part of the congestion window since no additional
+128/// tokens for the extra-elapsed time can be stored.
+129///
+130/// Too long burst intervals make pacing less effective.
+131fn optimal_capacity(smoothed_rtt: Duration, window: u64, mtu: u16) -> u64 {
+132    let rtt = smoothed_rtt.as_nanos().max(1);
+133    let mtu = u64::from(mtu);
+134
+135    let target_capacity = ((window as u128 * TARGET_BURST_INTERVAL.as_nanos()) / rtt) as u64;
+136    // Never restrict capacity below one MTU.
+137    let max_capacity = Ord::max(
+138        ((window as u128 * MAX_BURST_INTERVAL.as_nanos()) / rtt) as u64,
+139        mtu,
+140    );
+141
+142    // Batch the greater of `TARGET_BURST_INTERVAL` or `MIN_BURST_SIZE` worth of traffic at a
+143    // time. To avoid inducing excessive latency, limit that result to at most `MAX_BURST_INTERVAL`
+144    // worth of traffic.
+145    Ord::min(
+146        max_capacity,
+147        target_capacity.clamp(MIN_BURST_SIZE * mtu, MAX_BURST_SIZE * mtu),
+148    )
+149}
+150
+151/// Period of traffic to batch together on a reasonably fast connection
+152const TARGET_BURST_INTERVAL: Duration = Duration::from_millis(2);
+153
+154/// Maximum period of traffic to batch together on a slow connection
+155///
+156/// Takes precedence over [`MIN_BURST_SIZE`].
+157const MAX_BURST_INTERVAL: Duration = Duration::from_millis(10);
+158
+159/// Minimum number of datagrams to batch together, so long as we won't have to wait for more than
+160/// [`MAX_BURST_INTERVAL`]
+161const MIN_BURST_SIZE: u64 = 10;
+162
+163/// Creating 256 packets took 1ms in a benchmark, so larger bursts don't make sense.
+164const MAX_BURST_SIZE: u64 = 256;
+165
+166#[cfg(test)]
+167mod tests {
+168    use super::*;
+169
+170    #[test]
+171    fn does_not_panic_on_bad_instant() {
+172        let old_instant = Instant::now();
+173        let new_instant = old_instant + Duration::from_micros(15);
+174        let rtt = Duration::from_micros(400);
+175
+176        assert!(
+177            Pacer::new(rtt, 30000, 1500, new_instant)
+178                .delay(Duration::from_micros(0), 0, 1500, 1, old_instant)
+179                .is_none()
+180        );
+181        assert!(
+182            Pacer::new(rtt, 30000, 1500, new_instant)
+183                .delay(Duration::from_micros(0), 1600, 1500, 1, old_instant)
+184                .is_none()
+185        );
+186        assert!(
+187            Pacer::new(rtt, 30000, 1500, new_instant)
+188                .delay(Duration::from_micros(0), 1500, 1500, 3000, old_instant)
+189                .is_none()
+190        );
+191    }
+192
+193    #[test]
+194    fn derives_initial_capacity() {
+195        let window = 2_000_000;
+196        let mtu = 1500;
+197        let rtt = Duration::from_millis(50);
+198        let now = Instant::now();
+199
+200        let pacer = Pacer::new(rtt, window, mtu, now);
+201        assert_eq!(
+202            pacer.capacity,
+203            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+204        );
+205        assert_eq!(pacer.tokens, pacer.capacity);
+206
+207        let pacer = Pacer::new(Duration::from_millis(0), window, mtu, now);
+208        assert_eq!(pacer.capacity, MAX_BURST_SIZE * mtu as u64);
+209        assert_eq!(pacer.tokens, pacer.capacity);
+210
+211        let pacer = Pacer::new(rtt, 1, mtu, now);
+212        assert_eq!(pacer.capacity, mtu as u64);
+213        assert_eq!(pacer.tokens, pacer.capacity);
+214    }
+215
+216    #[test]
+217    fn adjusts_capacity() {
+218        let window = 2_000_000;
+219        let mtu = 1500;
+220        let rtt = Duration::from_millis(50);
+221        let now = Instant::now();
+222
+223        let mut pacer = Pacer::new(rtt, window, mtu, now);
+224        assert_eq!(
+225            pacer.capacity,
+226            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+227        );
+228        assert_eq!(pacer.tokens, pacer.capacity);
+229        let initial_tokens = pacer.tokens;
+230
+231        pacer.delay(rtt, mtu as u64, mtu, window * 2, now);
+232        assert_eq!(
+233            pacer.capacity,
+234            (2 * window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+235        );
+236        assert_eq!(pacer.tokens, initial_tokens);
+237
+238        pacer.delay(rtt, mtu as u64, mtu, window / 2, now);
+239        assert_eq!(
+240            pacer.capacity,
+241            (window as u128 / 2 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+242        );
+243        assert_eq!(pacer.tokens, initial_tokens / 2);
+244
+245        pacer.delay(rtt, mtu as u64, mtu * 2, window, now);
+246        assert_eq!(
+247            pacer.capacity,
+248            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+249        );
+250
+251        pacer.delay(rtt, mtu as u64, 20_000, window, now);
+252        assert_eq!(pacer.capacity, 20_000_u64 * MIN_BURST_SIZE);
+253    }
+254
+255    #[test]
+256    fn computes_pause_correctly() {
+257        let window = 2_000_000u64;
+258        let mtu = 1000;
+259        let rtt = Duration::from_millis(50);
+260        let old_instant = Instant::now();
+261
+262        let mut pacer = Pacer::new(rtt, window, mtu, old_instant);
+263        let packet_capacity = pacer.capacity / mtu as u64;
+264
+265        for _ in 0..packet_capacity {
+266            assert_eq!(
+267                pacer.delay(rtt, mtu as u64, mtu, window, old_instant),
+268                None,
+269                "When capacity is available packets should be sent immediately"
+270            );
+271
+272            pacer.on_transmit(mtu);
+273        }
+274
+275        let pace_duration = Duration::from_nanos((TARGET_BURST_INTERVAL.as_nanos() * 4 / 5) as u64);
+276
+277        let actual_delay = pacer
+278            .delay(rtt, mtu as u64, mtu, window, old_instant)
+279            .expect("Send must be delayed")
+280            .duration_since(old_instant);
+281
+282        let diff = actual_delay.abs_diff(pace_duration);
+283
+284        // Allow up to 2ns difference due to rounding
+285        assert!(
+286            diff < Duration::from_nanos(2),
+287            "expected ≈ {pace_duration:?}, got {actual_delay:?} (diff {diff:?})"
+288        );
+289        // Refill half of the tokens
+290        assert_eq!(
+291            pacer.delay(
+292                rtt,
+293                mtu as u64,
+294                mtu,
+295                window,
+296                old_instant + pace_duration / 2
+297            ),
+298            None
+299        );
+300        assert_eq!(pacer.tokens, pacer.capacity / 2);
+301
+302        for _ in 0..packet_capacity / 2 {
+303            assert_eq!(
+304                pacer.delay(rtt, mtu as u64, mtu, window, old_instant),
+305                None,
+306                "When capacity is available packets should be sent immediately"
+307            );
+308
+309            pacer.on_transmit(mtu);
+310        }
+311
+312        // Refill all capacity by waiting more than the expected duration
+313        assert_eq!(
+314            pacer.delay(
+315                rtt,
+316                mtu as u64,
+317                mtu,
+318                window,
+319                old_instant + pace_duration * 3 / 2
+320            ),
+321            None
+322        );
+323        assert_eq!(pacer.tokens, pacer.capacity);
+324    }
+325}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html new file mode 100644 index 000000000..91bb75150 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html @@ -0,0 +1,449 @@ +packet_builder.rs - source

iroh_quinn_proto/connection/
packet_builder.rs

1use bytes::{BufMut, Bytes};
+2use rand::Rng;
+3use tracing::{debug, trace, trace_span};
+4
+5use super::{Connection, PathId, SentFrames, TransmitBuf, spaces::SentPacket};
+6use crate::{
+7    ConnectionId, FrameStats, Instant, MIN_INITIAL_SIZE, TransportError, TransportErrorCode,
+8    coding::Encodable,
+9    connection::{ConnectionSide, qlog::QlogSentPacket, spaces::Retransmits},
+10    frame::{self, Close, EncodableFrame},
+11    packet::{FIXED_BIT, Header, InitialHeader, LongType, PacketNumber, PartialEncode, SpaceId},
+12};
+13
+14/// QUIC packet builder
+15///
+16/// This allows building QUIC packets: it takes care of writing the header, allows writing
+17/// frames and on [`PacketBuilder::finish`] (or [`PacketBuilder::finish_and_track`]) it
+18/// encrypts the packet so it is ready to be sent on the wire.
+19///
+20/// The builder manages the write buffer into which the packet is written, and directly
+21/// implements [`BufMut`] to write frames into the packet.
+22pub(super) struct PacketBuilder<'a, 'b> {
+23    pub(super) buf: &'a mut TransmitBuf<'b>,
+24    pub(super) space: SpaceId,
+25    path: PathId,
+26    pub(super) partial_encode: PartialEncode,
+27    pub(super) ack_eliciting: bool,
+28    pub(super) packet_number: u64,
+29    /// Is this packet allowed to be coalesced?
+30    pub(super) can_coalesce: bool,
+31    /// Smallest absolute position in the associated buffer that must be occupied by this packet's
+32    /// frames
+33    pub(super) min_size: usize,
+34    pub(super) tag_len: usize,
+35    pub(super) _span: tracing::span::EnteredSpan,
+36    qlog: QlogSentPacket,
+37    sent_frames: SentFrames,
+38}
+39
+40impl<'a, 'b> PacketBuilder<'a, 'b> {
+41    /// Write a new packet header to `buffer` and determine the packet's properties
+42    ///
+43    /// Marks the connection drained and returns `None` if the confidentiality limit would be
+44    /// violated.
+45    pub(super) fn new(
+46        now: Instant,
+47        space_id: SpaceId,
+48        path_id: PathId,
+49        dst_cid: ConnectionId,
+50        buffer: &'a mut TransmitBuf<'b>,
+51        ack_eliciting: bool,
+52        conn: &mut Connection,
+53    ) -> Option<Self>
+54    where
+55        'b: 'a,
+56    {
+57        let mut qlog = QlogSentPacket::default();
+58
+59        let version = conn.version;
+60        // Initiate key update if we're approaching the confidentiality limit
+61        let sent_with_keys = conn.spaces[space_id].sent_with_keys();
+62        if space_id == SpaceId::Data {
+63            if sent_with_keys >= conn.key_phase_size {
+64                debug!("routine key update due to phase exhaustion");
+65                conn.force_key_update();
+66            }
+67        } else {
+68            let confidentiality_limit = conn.spaces[space_id]
+69                .crypto
+70                .as_ref()
+71                .map_or_else(
+72                    || &conn.zero_rtt_crypto.as_ref().unwrap().packet,
+73                    |keys| &keys.packet.local,
+74                )
+75                .confidentiality_limit();
+76            if sent_with_keys.saturating_add(1) == confidentiality_limit {
+77                // We still have time to attempt a graceful close
+78                conn.close_inner(
+79                    now,
+80                    Close::Connection(frame::ConnectionClose {
+81                        error_code: TransportErrorCode::AEAD_LIMIT_REACHED,
+82                        frame_type: frame::MaybeFrame::None,
+83                        reason: Bytes::from_static(b"confidentiality limit reached"),
+84                    }),
+85                )
+86            } else if sent_with_keys > confidentiality_limit {
+87                // Confidentiality limited violated and there's nothing we can do
+88                conn.kill(
+89                    TransportError::AEAD_LIMIT_REACHED("confidentiality limit reached").into(),
+90                );
+91                return None;
+92            }
+93        }
+94
+95        let space = &mut conn.spaces[space_id];
+96        let packet_number = space.for_path(path_id).get_tx_number(&mut conn.rng);
+97        let span = trace_span!("send", space = ?space_id, pn = packet_number, %path_id).entered();
+98
+99        let number = PacketNumber::new(
+100            packet_number,
+101            space.for_path(path_id).largest_acked_packet.unwrap_or(0),
+102        );
+103        let header = match space_id {
+104            SpaceId::Data if space.crypto.is_some() => Header::Short {
+105                dst_cid,
+106                number,
+107                spin: if conn.spin_enabled {
+108                    conn.spin
+109                } else {
+110                    conn.rng.random()
+111                },
+112                key_phase: conn.key_phase,
+113            },
+114            SpaceId::Data => Header::Long {
+115                ty: LongType::ZeroRtt,
+116                src_cid: conn.handshake_cid,
+117                dst_cid,
+118                number,
+119                version,
+120            },
+121            SpaceId::Handshake => Header::Long {
+122                ty: LongType::Handshake,
+123                src_cid: conn.handshake_cid,
+124                dst_cid,
+125                number,
+126                version,
+127            },
+128            SpaceId::Initial => Header::Initial(InitialHeader {
+129                src_cid: conn.handshake_cid,
+130                dst_cid,
+131                token: match &conn.side {
+132                    ConnectionSide::Client { token, .. } => token.clone(),
+133                    ConnectionSide::Server { .. } => Bytes::new(),
+134                },
+135                number,
+136                version,
+137            }),
+138        };
+139
+140        let partial_encode = header.encode(buffer);
+141        if conn.peer_params.grease_quic_bit && conn.rng.random() {
+142            buffer.as_mut_slice()[partial_encode.start] ^= FIXED_BIT;
+143        }
+144
+145        let (sample_size, tag_len) = if let Some(ref crypto) = space.crypto {
+146            (
+147                crypto.header.local.sample_size(),
+148                crypto.packet.local.tag_len(),
+149            )
+150        } else if space_id == SpaceId::Data {
+151            let zero_rtt = conn.zero_rtt_crypto.as_ref().unwrap();
+152            (zero_rtt.header.sample_size(), zero_rtt.packet.tag_len())
+153        } else {
+154            unreachable!();
+155        };
+156
+157        // Each packet must be large enough for header protection sampling, i.e. the combined
+158        // lengths of the encoded packet number and protected payload must be at least 4 bytes
+159        // longer than the sample required for header protection. Further, each packet should be at
+160        // least tag_len + 6 bytes larger than the destination CID on incoming packets so that the
+161        // peer may send stateless resets that are indistinguishable from regular traffic.
+162
+163        // pn_len + payload_len + tag_len >= sample_size + 4
+164        // payload_len >= sample_size + 4 - pn_len - tag_len
+165        let min_size = Ord::max(
+166            buffer.len() + (sample_size + 4).saturating_sub(number.len() + tag_len),
+167            partial_encode.start + dst_cid.len() + 6,
+168        );
+169        let max_size = buffer.datagram_max_offset() - tag_len;
+170        debug_assert!(max_size >= min_size);
+171
+172        qlog.header(
+173            &header,
+174            Some(packet_number),
+175            space_id,
+176            space_id == SpaceId::Data && conn.spaces[SpaceId::Data].crypto.is_none(),
+177            path_id,
+178        );
+179
+180        Some(Self {
+181            buf: buffer,
+182            space: space_id,
+183            path: path_id,
+184            partial_encode,
+185            packet_number,
+186            can_coalesce: header.can_coalesce(),
+187            min_size,
+188            tag_len,
+189            ack_eliciting,
+190            qlog,
+191            sent_frames: SentFrames::default(),
+192            _span: span,
+193        })
+194    }
+195
+196    #[cfg(test)]
+197    pub(crate) fn simple_data_buf(buf: &'a mut TransmitBuf<'b>) -> Self {
+198        Self {
+199            buf,
+200            space: SpaceId::Data,
+201            path: PathId::ZERO,
+202            partial_encode: PartialEncode::no_header(),
+203            ack_eliciting: true,
+204            packet_number: 0,
+205            can_coalesce: true,
+206            min_size: 0,
+207            tag_len: 0,
+208            _span: trace_span!("test").entered(),
+209            qlog: QlogSentPacket::default(),
+210            sent_frames: SentFrames::default(),
+211        }
+212    }
+213
+214    /// Append the minimum amount of padding to the packet such that, after encryption, the
+215    /// enclosing datagram will occupy at least `min_size` bytes
+216    pub(super) fn pad_to(&mut self, min_size: u16) {
+217        // The datagram might already have a larger minimum size than the caller is requesting, if
+218        // e.g. we're coalescing packets and have populated more than `min_size` bytes with packets
+219        // already.
+220        self.min_size = Ord::max(
+221            self.min_size,
+222            self.buf.datagram_start_offset() + (min_size as usize) - self.tag_len,
+223        );
+224    }
+225
+226    /// Writes a frame into the underlying buffer.
+227    ///
+228    /// It will also:
+229    /// - Track the frame so that it's registered with the path once [`Self::finish_and_track`] is
+230    ///   called.
+231    /// - Register the sent frame with the given [`FrameStats`].
+232    /// - If the qlog feature is enabled, register the frame.
+233    /// - Log the frame.
+234    pub(super) fn write_frame<'c>(
+235        &mut self,
+236        frame: impl Into<EncodableFrame<'c>>,
+237        stats: &mut FrameStats,
+238    ) {
+239        self.write_frame_with_log_msg(frame, stats, None);
+240    }
+241
+242    /// Writes a frame into the underlying buffer.
+243    ///
+244    /// It will also:
+245    /// - Track the frame so that it's registered with the path once [`Self::finish_and_track`] is
+246    ///   called.
+247    /// - Register the sent frame with the given [`FrameStats`].
+248    /// - If the qlog feature is enabled, register the frame.
+249    /// - Log the frame. If a `msg` is given, this will be added to the log.
+250    pub(super) fn write_frame_with_log_msg<'c>(
+251        &mut self,
+252        frame: impl Into<EncodableFrame<'c>>,
+253        stats: &mut FrameStats,
+254        msg: Option<&'static str>,
+255    ) {
+256        let frame = frame.into();
+257        frame.encode(&mut self.frame_space_mut());
+258        stats.record(frame.get_type());
+259        self.qlog.record(&frame);
+260        match msg {
+261            Some(msg) => trace!(%frame, msg),
+262            None => trace!(%frame),
+263        }
+264        self.sent_frames.record_sent_frame(frame);
+265    }
+266
+267    /// Returns a writable buffer limited to the remaining frame space
+268    ///
+269    /// The [`BufMut::remaining_mut`] call on the returned buffer indicates the amount of
+270    /// space available to write QUIC frames into.
+271    // In rust 1.82 we can use `-> impl BufMut + use<'_, 'a, 'b>`
+272    pub(super) fn frame_space_mut(&mut self) -> bytes::buf::Limit<&mut TransmitBuf<'b>> {
+273        self.buf.limit(self.frame_space_remaining())
+274    }
+275
+276    pub(super) fn sent_frames(&self) -> &SentFrames {
+277        &self.sent_frames
+278    }
+279
+280    pub(super) fn finish_and_track(
+281        mut self,
+282        now: Instant,
+283        conn: &mut Connection,
+284        path_id: PathId,
+285        pad_datagram: PadDatagram,
+286    ) {
+287        match pad_datagram {
+288            PadDatagram::No => (),
+289            PadDatagram::ToSize(size) => self.pad_to(size),
+290            PadDatagram::ToSegmentSize => self.pad_to(self.buf.segment_size() as u16),
+291            PadDatagram::ToMinMtu => self.pad_to(MIN_INITIAL_SIZE),
+292        }
+293        let ack_eliciting = self.ack_eliciting;
+294        let packet_number = self.packet_number;
+295        let space_id = self.space;
+296        let (size, padded, sent) = self.finish(conn, now);
+297
+298        let size = match padded || ack_eliciting {
+299            true => size as u16,
+300            false => 0,
+301        };
+302
+303        let packet = SentPacket {
+304            path_generation: conn.paths.get_mut(&path_id).unwrap().data.generation(),
+305            largest_acked: sent.largest_acked,
+306            time_sent: now,
+307            size,
+308            ack_eliciting,
+309            retransmits: sent.retransmits,
+310            stream_frames: sent.stream_frames,
+311        };
+312
+313        conn.paths.get_mut(&path_id).unwrap().data.sent(
+314            packet_number,
+315            packet,
+316            conn.spaces[space_id].for_path(path_id),
+317        );
+318        conn.reset_keep_alive(path_id, now);
+319        if size != 0 {
+320            if ack_eliciting {
+321                conn.spaces[space_id]
+322                    .for_path(path_id)
+323                    .time_of_last_ack_eliciting_packet = Some(now);
+324                if conn.permit_idle_reset {
+325                    conn.reset_idle_timeout(now, space_id, path_id);
+326                }
+327                conn.permit_idle_reset = false;
+328            }
+329            conn.set_loss_detection_timer(now, path_id);
+330            conn.path_data_mut(path_id).pacing.on_transmit(size);
+331        }
+332    }
+333
+334    /// Encrypt packet, returning the length of the packet and whether padding was added
+335    pub(super) fn finish(
+336        mut self,
+337        conn: &mut Connection,
+338        now: Instant,
+339    ) -> (usize, bool, SentFrames) {
+340        debug_assert!(
+341            self.buf.len() <= self.buf.datagram_max_offset() - self.tag_len,
+342            "packet exceeds maximum size"
+343        );
+344        let pad = self.buf.len() < self.min_size;
+345        if pad {
+346            let padding = self.min_size - self.buf.len();
+347            trace!("PADDING * {}", padding);
+348            self.buf.put_bytes(0, padding);
+349            self.qlog.frame_padding(padding);
+350        }
+351
+352        let space = &conn.spaces[self.space];
+353        let (header_crypto, packet_crypto) = if let Some(ref crypto) = space.crypto {
+354            (&*crypto.header.local, &*crypto.packet.local)
+355        } else if self.space == SpaceId::Data {
+356            let zero_rtt = conn.zero_rtt_crypto.as_ref().unwrap();
+357            (&*zero_rtt.header, &*zero_rtt.packet)
+358        } else {
+359            unreachable!("tried to send {:?} packet without keys", self.space);
+360        };
+361
+362        debug_assert_eq!(
+363            packet_crypto.tag_len(),
+364            self.tag_len,
+365            "Mismatching crypto tag len"
+366        );
+367
+368        self.buf.put_bytes(0, packet_crypto.tag_len());
+369        let encode_start = self.partial_encode.start;
+370        let packet_buf = &mut self.buf.as_mut_slice()[encode_start..];
+371        // for packet protection, PathId::ZERO and no path are equivalent.
+372        self.partial_encode.finish(
+373            packet_buf,
+374            header_crypto,
+375            Some((self.packet_number, self.path, packet_crypto)),
+376        );
+377
+378        let packet_len = self.buf.len() - encode_start;
+379        trace!(size = %packet_len, "wrote packet");
+380        self.qlog.finalize(packet_len);
+381        conn.qlog.emit_packet_sent(self.qlog, now);
+382        (packet_len, pad, self.sent_frames)
+383    }
+384
+385    /// The number of additional bytes the current packet would take up if it was finished now
+386    ///
+387    /// This will include any padding which is required to make the size large enough to be
+388    /// encrypted correctly.
+389    pub(super) fn predict_packet_end(&self) -> usize {
+390        self.buf.len().max(self.min_size) + self.tag_len - self.buf.len()
+391    }
+392
+393    /// Returns the remaining space in the packet that can be taken up by QUIC frames
+394    ///
+395    /// This leaves space in the datagram for the cryptographic tag that needs to be written
+396    /// when the packet is finished.
+397    pub(super) fn frame_space_remaining(&self) -> usize {
+398        let max_offset = self.buf.datagram_max_offset() - self.tag_len;
+399        max_offset.saturating_sub(self.buf.len())
+400    }
+401
+402    pub(crate) fn require_padding(&mut self) {
+403        self.sent_frames.requires_padding = true;
+404    }
+405
+406    pub(crate) fn retransmits_mut(&mut self) -> &mut Retransmits {
+407        self.sent_frames.retransmits_mut()
+408    }
+409}
+410
+411#[derive(Debug, Copy, Clone)]
+412pub(super) enum PadDatagram {
+413    /// Do not pad the datagram
+414    No,
+415    /// To a specific size
+416    ToSize(u16),
+417    /// Pad to the current MTU/segment size
+418    ///
+419    /// For the first datagram in a transmit the MTU is the same as the
+420    /// [`TransmitBuf::segment_size`].
+421    ToSegmentSize,
+422    /// Pad to [`MIN_INITIAL_SIZE`], the minimal QUIC MTU of 1200 bytes
+423    ToMinMtu,
+424}
+425
+426impl std::ops::BitOrAssign for PadDatagram {
+427    fn bitor_assign(&mut self, rhs: Self) {
+428        *self = *self | rhs;
+429    }
+430}
+431
+432impl std::ops::BitOr for PadDatagram {
+433    type Output = Self;
+434
+435    fn bitor(self, rhs: Self) -> Self::Output {
+436        match (self, rhs) {
+437            (Self::No, rhs) => rhs,
+438            (Self::ToSize(size), Self::No) => Self::ToSize(size),
+439            (Self::ToSize(a), Self::ToSize(b)) => Self::ToSize(a.max(b)),
+440            (Self::ToSize(_), Self::ToSegmentSize) => Self::ToSegmentSize,
+441            (Self::ToSize(_), Self::ToMinMtu) => Self::ToMinMtu,
+442            (Self::ToSegmentSize, Self::No) => Self::ToSegmentSize,
+443            (Self::ToSegmentSize, Self::ToSize(_)) => Self::ToSegmentSize,
+444            (Self::ToSegmentSize, Self::ToSegmentSize) => Self::ToSegmentSize,
+445            (Self::ToSegmentSize, Self::ToMinMtu) => Self::ToMinMtu,
+446            (Self::ToMinMtu, _) => Self::ToMinMtu,
+447        }
+448    }
+449}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/packet_crypto.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/packet_crypto.rs.html new file mode 100644 index 000000000..47fa797d1 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/packet_crypto.rs.html @@ -0,0 +1,198 @@ +packet_crypto.rs - source

iroh_quinn_proto/connection/
packet_crypto.rs

1use tracing::{debug, trace};
+2
+3use crate::Instant;
+4use crate::connection::spaces::PacketSpace;
+5use crate::crypto::{HeaderKey, KeyPair, PacketKey};
+6use crate::packet::{Packet, PartialDecode, SpaceId};
+7use crate::token::ResetToken;
+8use crate::{RESET_TOKEN_SIZE, TransportError};
+9
+10use super::PathId;
+11
+12/// Removes header protection of a packet, or returns `None` if the packet was dropped
+13pub(super) fn unprotect_header(
+14    partial_decode: PartialDecode,
+15    spaces: &[PacketSpace; 3],
+16    zero_rtt_crypto: Option<&ZeroRttCrypto>,
+17    stateless_reset_token: Option<ResetToken>,
+18) -> Option<UnprotectHeaderResult> {
+19    let header_crypto = if partial_decode.is_0rtt() {
+20        if let Some(crypto) = zero_rtt_crypto {
+21            Some(&*crypto.header)
+22        } else {
+23            debug!("dropping unexpected 0-RTT packet");
+24            return None;
+25        }
+26    } else if let Some(space) = partial_decode.space() {
+27        if let Some(ref crypto) = spaces[space].crypto {
+28            Some(&*crypto.header.remote)
+29        } else {
+30            debug!(
+31                "discarding unexpected {:?} packet ({} bytes)",
+32                space,
+33                partial_decode.len(),
+34            );
+35            return None;
+36        }
+37    } else {
+38        // Unprotected packet
+39        None
+40    };
+41
+42    let packet = partial_decode.data();
+43    let stateless_reset = packet.len() >= RESET_TOKEN_SIZE + 5
+44        && stateless_reset_token.as_deref() == Some(&packet[packet.len() - RESET_TOKEN_SIZE..]);
+45
+46    match partial_decode.finish(header_crypto) {
+47        Ok(packet) => Some(UnprotectHeaderResult {
+48            packet: Some(packet),
+49            stateless_reset,
+50        }),
+51        Err(_) if stateless_reset => Some(UnprotectHeaderResult {
+52            packet: None,
+53            stateless_reset: true,
+54        }),
+55        Err(e) => {
+56            trace!("unable to complete packet decoding: {}", e);
+57            None
+58        }
+59    }
+60}
+61
+62pub(super) struct UnprotectHeaderResult {
+63    /// The packet with the now unprotected header (`None` in the case of stateless reset packets
+64    /// that fail to be decoded)
+65    pub(super) packet: Option<Packet>,
+66    /// Whether the packet was a stateless reset packet
+67    pub(super) stateless_reset: bool,
+68}
+69
+70/// Decrypts a packet's body in-place
+71pub(super) fn decrypt_packet_body(
+72    packet: &mut Packet,
+73    path_id: PathId,
+74    spaces: &[PacketSpace; 3],
+75    zero_rtt_crypto: Option<&ZeroRttCrypto>,
+76    conn_key_phase: bool,
+77    prev_crypto: Option<&PrevCrypto>,
+78    next_crypto: Option<&KeyPair<Box<dyn PacketKey>>>,
+79) -> Result<Option<DecryptPacketResult>, Option<TransportError>> {
+80    if !packet.header.is_protected() {
+81        // Unprotected packets also don't have packet numbers
+82        return Ok(None);
+83    }
+84    let space = packet.header.space();
+85
+86    if path_id != PathId::ZERO && space != SpaceId::Data {
+87        // do not try to decrypt illegal multipath packets
+88        return Err(Some(TransportError::PROTOCOL_VIOLATION(
+89            "multipath packet on non Data packet number space",
+90        )));
+91    }
+92    // Packets that do not belong to known path ids are valid as long as they can be decrypted.
+93    // If we didn't have a path, that's for the purposes of this function equivalent to not
+94    // having received packets on that path yet. So both of these cases are represented by `None`.
+95    let rx_packet = spaces[space]
+96        .path_space(path_id)
+97        .and_then(|space| space.rx_packet);
+98    let number = packet
+99        .header
+100        .number()
+101        .ok_or(None)?
+102        .expand(rx_packet.map(|n| n + 1).unwrap_or_default());
+103    let packet_key_phase = packet.header.key_phase();
+104
+105    let mut crypto_update = false;
+106    let crypto = if packet.header.is_0rtt() {
+107        &zero_rtt_crypto.unwrap().packet
+108    } else if packet_key_phase == conn_key_phase || space != SpaceId::Data {
+109        &spaces[space].crypto.as_ref().unwrap().packet.remote
+110    } else if let Some(prev) = prev_crypto.and_then(|crypto| {
+111        // If this packet comes prior to acknowledgment of the key update by the peer,
+112        if crypto.end_packet.is_none_or(|(pn, _)| number < pn) {
+113            // use the previous keys.
+114            Some(crypto)
+115        } else {
+116            // Otherwise, this must be a remotely-initiated key update, so fall through to the
+117            // final case.
+118            None
+119        }
+120    }) {
+121        &prev.crypto.remote
+122    } else {
+123        // We're in the Data space with a key phase mismatch and either there is no locally
+124        // initiated key update or the locally initiated key update was acknowledged by a
+125        // lower-numbered packet. The key phase mismatch must therefore represent a new
+126        // remotely-initiated key update.
+127        crypto_update = true;
+128        &next_crypto.unwrap().remote
+129    };
+130
+131    crypto
+132        .decrypt(path_id, number, &packet.header_data, &mut packet.payload)
+133        .map_err(|_| {
+134            trace!("decryption failed with packet number {}", number);
+135            None
+136        })?;
+137
+138    if !packet.reserved_bits_valid() {
+139        return Err(Some(TransportError::PROTOCOL_VIOLATION(
+140            "reserved bits set",
+141        )));
+142    }
+143
+144    let mut outgoing_key_update_acked = false;
+145    if let Some(prev) = prev_crypto
+146        && prev.end_packet.is_none()
+147        && packet_key_phase == conn_key_phase
+148    {
+149        outgoing_key_update_acked = true;
+150    }
+151
+152    if crypto_update {
+153        // Validate incoming key update
+154        // If `rx_packet` is `None`, then either the path is entirely new, or we haven't received
+155        // any packets on this path yet. In that case, having the first packet be a crypto update
+156        // is fine.
+157        let invalid_packet_number = rx_packet.is_some_and(|rx_packet| number <= rx_packet);
+158        if invalid_packet_number || prev_crypto.is_some_and(|x| x.update_unacked) {
+159            trace!(?number, ?rx_packet, %path_id, "crypto update failed");
+160            return Err(Some(TransportError::KEY_UPDATE_ERROR("")));
+161        }
+162    }
+163
+164    Ok(Some(DecryptPacketResult {
+165        number,
+166        outgoing_key_update_acked,
+167        incoming_key_update: crypto_update,
+168    }))
+169}
+170
+171pub(super) struct DecryptPacketResult {
+172    /// The packet number
+173    pub(super) number: u64,
+174    /// Whether a locally initiated key update has been acknowledged by the peer
+175    pub(super) outgoing_key_update_acked: bool,
+176    /// Whether the peer has initiated a key update
+177    pub(super) incoming_key_update: bool,
+178}
+179
+180pub(super) struct PrevCrypto {
+181    /// The keys used for the previous key phase, temporarily retained to decrypt packets sent by
+182    /// the peer prior to its own key update.
+183    pub(super) crypto: KeyPair<Box<dyn PacketKey>>,
+184    /// The incoming packet that ends the interval for which these keys are applicable, and the time
+185    /// of its receipt.
+186    ///
+187    /// Incoming packets should be decrypted using these keys iff this is `None` or their packet
+188    /// number is lower. `None` indicates that we have not yet received a packet using newer keys,
+189    /// which implies that the update was locally initiated.
+190    pub(super) end_packet: Option<(u64, Instant)>,
+191    /// Whether the following key phase is from a remotely initiated update that we haven't acked
+192    pub(super) update_unacked: bool,
+193}
+194
+195pub(super) struct ZeroRttCrypto {
+196    pub(super) header: Box<dyn HeaderKey>,
+197    pub(super) packet: Box<dyn PacketKey>,
+198}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/paths.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/paths.rs.html new file mode 100644 index 000000000..ac01944b5 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/paths.rs.html @@ -0,0 +1,993 @@ +paths.rs - source

iroh_quinn_proto/connection/
paths.rs

1use std::{cmp, net::SocketAddr};
+2
+3use identity_hash::IntMap;
+4use thiserror::Error;
+5use tracing::{debug, trace};
+6
+7use super::{
+8    PathError, PathStats,
+9    mtud::MtuDiscovery,
+10    pacing::Pacer,
+11    spaces::{PacketNumberSpace, SentPacket},
+12};
+13use crate::{
+14    ConnectionId, Duration, FourTuple, Instant, TIMER_GRANULARITY, TransportConfig, VarInt,
+15    coding::{self, Decodable, Encodable},
+16    congestion,
+17    frame::ObservedAddr,
+18    packet::SpaceId,
+19};
+20
+21#[cfg(feature = "qlog")]
+22use qlog::events::quic::RecoveryMetricsUpdated;
+23
+24/// Id representing different paths when using multipath extension
+25#[cfg_attr(test, derive(test_strategy::Arbitrary))]
+26#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
+27pub struct PathId(pub(crate) u32);
+28
+29impl std::hash::Hash for PathId {
+30    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+31        state.write_u32(self.0);
+32    }
+33}
+34
+35impl identity_hash::IdentityHashable for PathId {}
+36
+37impl Decodable for PathId {
+38    fn decode<B: bytes::Buf>(r: &mut B) -> coding::Result<Self> {
+39        let v = VarInt::decode(r)?;
+40        let v = u32::try_from(v.0).map_err(|_| coding::UnexpectedEnd)?;
+41        Ok(Self(v))
+42    }
+43}
+44
+45impl Encodable for PathId {
+46    fn encode<B: bytes::BufMut>(&self, w: &mut B) {
+47        VarInt(self.0.into()).encode(w)
+48    }
+49}
+50
+51impl PathId {
+52    /// The maximum path ID allowed.
+53    pub const MAX: Self = Self(u32::MAX);
+54
+55    /// The 0 path id.
+56    pub const ZERO: Self = Self(0);
+57
+58    /// The number of bytes this [`PathId`] uses when encoded as a [`VarInt`]
+59    pub(crate) const fn size(&self) -> usize {
+60        VarInt(self.0 as u64).size()
+61    }
+62
+63    /// Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead
+64    /// of overflowing.
+65    pub fn saturating_add(self, rhs: impl Into<Self>) -> Self {
+66        let rhs = rhs.into();
+67        let inner = self.0.saturating_add(rhs.0);
+68        Self(inner)
+69    }
+70
+71    /// Saturating integer subtraction. Computes self - rhs, saturating at the numeric bounds
+72    /// instead of overflowing.
+73    pub fn saturating_sub(self, rhs: impl Into<Self>) -> Self {
+74        let rhs = rhs.into();
+75        let inner = self.0.saturating_sub(rhs.0);
+76        Self(inner)
+77    }
+78
+79    /// Get the next [`PathId`]
+80    pub(crate) fn next(&self) -> Self {
+81        self.saturating_add(Self(1))
+82    }
+83
+84    /// Get the underlying u32
+85    pub(crate) fn as_u32(&self) -> u32 {
+86        self.0
+87    }
+88}
+89
+90impl std::fmt::Display for PathId {
+91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+92        self.0.fmt(f)
+93    }
+94}
+95
+96impl<T: Into<u32>> From<T> for PathId {
+97    fn from(source: T) -> Self {
+98        Self(source.into())
+99    }
+100}
+101
+102/// State needed for a single path ID.
+103///
+104/// A single path ID can migrate according to the rules in RFC9000 §9, either voluntary or
+105/// involuntary. We need to keep the [`PathData`] of the previously used such path available
+106/// in order to defend against migration attacks (see RFC9000 §9.3.1, §9.3.2 and §9.3.3) as
+107/// well as to support path probing (RFC9000 §9.1).
+108#[derive(Debug)]
+109pub(super) struct PathState {
+110    pub(super) data: PathData,
+111    pub(super) prev: Option<(ConnectionId, PathData)>,
+112}
+113
+114impl PathState {
+115    /// Update counters to account for a packet becoming acknowledged, lost, or abandoned
+116    pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) {
+117        // Visit known paths from newest to oldest to find the one `pn` was sent on
+118        for path_data in [&mut self.data]
+119            .into_iter()
+120            .chain(self.prev.as_mut().map(|(_, data)| data))
+121        {
+122            if path_data.remove_in_flight(packet) {
+123                return;
+124            }
+125        }
+126    }
+127}
+128
+129#[derive(Debug)]
+130pub(super) struct SentChallengeInfo {
+131    /// When was the challenge sent on the wire.
+132    pub(super) sent_instant: Instant,
+133    /// The 4-tuple on which this path challenge was sent.
+134    pub(super) network_path: FourTuple,
+135}
+136
+137/// Description of a particular network path
+138#[derive(Debug)]
+139pub(super) struct PathData {
+140    pub(super) network_path: FourTuple,
+141    pub(super) rtt: RttEstimator,
+142    /// Whether we're enabling ECN on outgoing packets
+143    pub(super) sending_ecn: bool,
+144    /// Congestion controller state
+145    pub(super) congestion: Box<dyn congestion::Controller>,
+146    /// Pacing state
+147    pub(super) pacing: Pacer,
+148    /// Actually sent challenges (on the wire).
+149    pub(super) challenges_sent: IntMap<u64, SentChallengeInfo>,
+150    /// Whether to *immediately* trigger another PATH_CHALLENGE.
+151    ///
+152    /// This is picked up by [`super::Connection::space_can_send`].
+153    pub(super) send_new_challenge: bool,
+154    /// Pending responses to PATH_CHALLENGE frames
+155    pub(super) path_responses: PathResponses,
+156    /// Whether we're certain the peer can both send and receive on this address
+157    ///
+158    /// Initially equal to `use_stateless_retry` for servers, and becomes false again on every
+159    /// migration. Always true for clients.
+160    pub(super) validated: bool,
+161    /// Total size of all UDP datagrams sent on this path
+162    pub(super) total_sent: u64,
+163    /// Total size of all UDP datagrams received on this path
+164    pub(super) total_recvd: u64,
+165    /// The state of the MTU discovery process
+166    pub(super) mtud: MtuDiscovery,
+167    /// Packet number of the first packet sent after an RTT sample was collected on this path
+168    ///
+169    /// Used in persistent congestion determination.
+170    pub(super) first_packet_after_rtt_sample: Option<(SpaceId, u64)>,
+171    /// The in-flight packets and bytes
+172    ///
+173    /// Note that this is across all spaces on this path
+174    pub(super) in_flight: InFlight,
+175    /// Whether this path has had it's remote address reported back to the peer. This only happens
+176    /// if both peers agree to so based on their transport parameters.
+177    pub(super) observed_addr_sent: bool,
+178    /// Observed address frame with the largest sequence number received from the peer on this path.
+179    pub(super) last_observed_addr_report: Option<ObservedAddr>,
+180    /// The QUIC-MULTIPATH path status
+181    pub(super) status: PathStatusState,
+182    /// Number of the first packet sent on this path
+183    ///
+184    /// With RFC9000 §9 style migration (i.e. not multipath) the PathId does not change and
+185    /// hence packet numbers continue. This is used to determine whether a packet was sent
+186    /// on such an earlier path. Insufficient to determine if a packet was sent on a later
+187    /// path.
+188    first_packet: Option<u64>,
+189    /// The number of times a PTO has been sent without receiving an ack.
+190    pub(super) pto_count: u32,
+191
+192    //
+193    // Per-path idle & keep alive
+194    //
+195    /// Idle timeout for the path
+196    ///
+197    /// If expired, the path will be abandoned.  This is different from the connection-wide
+198    /// idle timeout which closes the connection if expired.
+199    pub(super) idle_timeout: Option<Duration>,
+200    /// Keep alives to send on this path
+201    ///
+202    /// There is also a connection-level keep alive configured in the
+203    /// [`TransportParameters`].  This triggers activity on any path which can keep the
+204    /// connection alive.
+205    ///
+206    /// [`TransportParameters`]: crate::transport_parameters::TransportParameters
+207    pub(super) keep_alive: Option<Duration>,
+208
+209    /// Whether the path has already been considered opened from an application perspective
+210    ///
+211    /// This means, for paths other than the original [`PathId::ZERO`], a first path challenge has
+212    /// been responded to, regardless of the initial validation status of the path. This state is
+213    /// irreversible, since it's not affected by the path being closed.
+214    pub(super) open: bool,
+215
+216    /// State relevant to sending and receiving PATH_ABANDON frames.
+217    pub(super) abandon_state: AbandonState,
+218
+219    /// Snapshot of the qlog recovery metrics
+220    #[cfg(feature = "qlog")]
+221    recovery_metrics: RecoveryMetrics,
+222
+223    /// Tag uniquely identifying a path in a connection
+224    generation: u64,
+225}
+226
+227/// The abandon-relevant state a path can be in.
+228#[derive(Debug)]
+229pub(super) enum AbandonState {
+230    /// This path wasn't abandoned yet.
+231    NotAbandoned,
+232    /// A PATH_ABANDON frame was sent for this path, and we're expecting a response
+233    /// by the given deadline.
+234    ///
+235    /// If we don't receive PATH_ABANDON by that time *and* we receive a packet on this
+236    /// path, then we assume our peer has ignored our PATH_ABANDON and error out.
+237    ///
+238    /// This is checked in [`crate::Connection::on_packet_authenticated`].
+239    ExpectingPathAbandon { deadline: Instant },
+240    /// We received a PATH_ABANDON and are just waiting for the path's packets to drain
+241    /// and eventually will discard the path.
+242    ReceivedPathAbandon,
+243}
+244
+245impl PathData {
+246    pub(super) fn new(
+247        network_path: FourTuple,
+248        allow_mtud: bool,
+249        peer_max_udp_payload_size: Option<u16>,
+250        generation: u64,
+251        now: Instant,
+252        config: &TransportConfig,
+253    ) -> Self {
+254        let congestion = config
+255            .congestion_controller_factory
+256            .clone()
+257            .build(now, config.get_initial_mtu());
+258        Self {
+259            network_path,
+260            rtt: RttEstimator::new(config.initial_rtt),
+261            sending_ecn: true,
+262            pacing: Pacer::new(
+263                config.initial_rtt,
+264                congestion.initial_window(),
+265                config.get_initial_mtu(),
+266                now,
+267            ),
+268            congestion,
+269            challenges_sent: Default::default(),
+270            send_new_challenge: false,
+271            path_responses: PathResponses::default(),
+272            validated: false,
+273            total_sent: 0,
+274            total_recvd: 0,
+275            mtud: config
+276                .mtu_discovery_config
+277                .as_ref()
+278                .filter(|_| allow_mtud)
+279                .map_or_else(
+280                    || MtuDiscovery::disabled(config.get_initial_mtu(), config.min_mtu),
+281                    |mtud_config| {
+282                        MtuDiscovery::new(
+283                            config.get_initial_mtu(),
+284                            config.min_mtu,
+285                            peer_max_udp_payload_size,
+286                            mtud_config.clone(),
+287                        )
+288                    },
+289                ),
+290            first_packet_after_rtt_sample: None,
+291            in_flight: InFlight::new(),
+292            observed_addr_sent: false,
+293            last_observed_addr_report: None,
+294            status: Default::default(),
+295            first_packet: None,
+296            pto_count: 0,
+297            idle_timeout: config.default_path_max_idle_timeout,
+298            keep_alive: config.default_path_keep_alive_interval,
+299            open: false,
+300            abandon_state: AbandonState::NotAbandoned,
+301            #[cfg(feature = "qlog")]
+302            recovery_metrics: RecoveryMetrics::default(),
+303            generation,
+304        }
+305    }
+306
+307    /// Create a new path from a previous one.
+308    ///
+309    /// This should only be called when migrating paths.
+310    pub(super) fn from_previous(
+311        network_path: FourTuple,
+312        prev: &Self,
+313        generation: u64,
+314        now: Instant,
+315    ) -> Self {
+316        let congestion = prev.congestion.clone_box();
+317        let smoothed_rtt = prev.rtt.get();
+318        Self {
+319            network_path,
+320            rtt: prev.rtt,
+321            pacing: Pacer::new(smoothed_rtt, congestion.window(), prev.current_mtu(), now),
+322            sending_ecn: true,
+323            congestion,
+324            challenges_sent: Default::default(),
+325            send_new_challenge: false,
+326            path_responses: PathResponses::default(),
+327            validated: false,
+328            total_sent: 0,
+329            total_recvd: 0,
+330            mtud: prev.mtud.clone(),
+331            first_packet_after_rtt_sample: prev.first_packet_after_rtt_sample,
+332            in_flight: InFlight::new(),
+333            observed_addr_sent: false,
+334            last_observed_addr_report: None,
+335            status: prev.status.clone(),
+336            first_packet: None,
+337            pto_count: 0,
+338            idle_timeout: prev.idle_timeout,
+339            keep_alive: prev.keep_alive,
+340            open: false,
+341            abandon_state: AbandonState::NotAbandoned,
+342            #[cfg(feature = "qlog")]
+343            recovery_metrics: prev.recovery_metrics.clone(),
+344            generation,
+345        }
+346    }
+347
+348    /// Whether we're in the process of validating this path with PATH_CHALLENGEs
+349    pub(super) fn is_validating_path(&self) -> bool {
+350        !self.challenges_sent.is_empty() || self.send_new_challenge
+351    }
+352
+353    /// Indicates whether we're a server that hasn't validated the peer's address and hasn't
+354    /// received enough data from the peer to permit sending `bytes_to_send` additional bytes
+355    pub(super) fn anti_amplification_blocked(&self, bytes_to_send: u64) -> bool {
+356        !self.validated && self.total_recvd * 3 < self.total_sent + bytes_to_send
+357    }
+358
+359    /// Returns the path's current MTU
+360    pub(super) fn current_mtu(&self) -> u16 {
+361        self.mtud.current_mtu()
+362    }
+363
+364    /// Account for transmission of `packet` with number `pn` in `space`
+365    pub(super) fn sent(&mut self, pn: u64, packet: SentPacket, space: &mut PacketNumberSpace) {
+366        self.in_flight.insert(&packet);
+367        if self.first_packet.is_none() {
+368            self.first_packet = Some(pn);
+369        }
+370        if let Some(forgotten) = space.sent(pn, packet) {
+371            self.remove_in_flight(&forgotten);
+372        }
+373    }
+374
+375    /// Remove `packet` with number `pn` from this path's congestion control counters, or return
+376    /// `false` if `pn` was sent before this path was established.
+377    pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) -> bool {
+378        if packet.path_generation != self.generation {
+379            return false;
+380        }
+381        self.in_flight.remove(packet);
+382        true
+383    }
+384
+385    /// Increment the total size of sent UDP datagrams
+386    pub(super) fn inc_total_sent(&mut self, inc: u64) {
+387        self.total_sent = self.total_sent.saturating_add(inc);
+388        if !self.validated {
+389            trace!(
+390                network_path = %self.network_path,
+391                anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent),
+392                "anti amplification budget decreased"
+393            );
+394        }
+395    }
+396
+397    /// Increment the total size of received UDP datagrams
+398    pub(super) fn inc_total_recvd(&mut self, inc: u64) {
+399        self.total_recvd = self.total_recvd.saturating_add(inc);
+400        if !self.validated {
+401            trace!(
+402                network_path = %self.network_path,
+403                anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent),
+404                "anti amplification budget increased"
+405            );
+406        }
+407    }
+408
+409    /// The earliest time at which a sent challenge is considered lost.
+410    pub(super) fn earliest_expiring_challenge(&self) -> Option<Instant> {
+411        if self.challenges_sent.is_empty() {
+412            return None;
+413        }
+414        let pto = self.rtt.pto_base();
+415        self.challenges_sent
+416            .values()
+417            .map(|info| info.sent_instant)
+418            .min()
+419            .map(|sent_instant| sent_instant + pto)
+420    }
+421
+422    /// Handle receiving a PATH_RESPONSE.
+423    pub(super) fn on_path_response_received(
+424        &mut self,
+425        now: Instant,
+426        token: u64,
+427        network_path: FourTuple,
+428    ) -> OnPathResponseReceived {
+429        match self.challenges_sent.get(&token) {
+430            // Response to an on-path PathChallenge
+431            Some(info)
+432                if info.network_path.is_probably_same_path(&network_path)
+433                    && self.network_path.is_probably_same_path(&network_path) =>
+434            {
+435                self.network_path.update_local_if_same_remote(&network_path);
+436                let sent_instant = info.sent_instant;
+437                if !std::mem::replace(&mut self.validated, true) {
+438                    trace!("new path validated");
+439                }
+440                // Clear any other on-path sent challenge.
+441                self.challenges_sent
+442                    .retain(|_token, info| !info.network_path.is_probably_same_path(&network_path));
+443
+444                self.send_new_challenge = false;
+445
+446                // This RTT can only be used for the initial RTT, not as a normal
+447                // sample: https://www.rfc-editor.org/rfc/rfc9002#section-6.2.2-2.
+448                let rtt = now.saturating_duration_since(sent_instant);
+449                self.rtt.reset_initial_rtt(rtt);
+450
+451                let was_open = std::mem::replace(&mut self.open, true);
+452                OnPathResponseReceived::OnPath { was_open }
+453            }
+454            // Response to an off-path PathChallenge
+455            Some(info) if info.network_path.is_probably_same_path(&network_path) => {
+456                self.challenges_sent
+457                    .retain(|_token, info| !info.network_path.is_probably_same_path(&network_path));
+458                OnPathResponseReceived::OffPath
+459            }
+460            // Response to a PathChallenge we recognize, but from an invalid remote
+461            Some(info) => OnPathResponseReceived::Invalid {
+462                expected: info.network_path,
+463            },
+464            // Response to an unknown PathChallenge
+465            None => OnPathResponseReceived::Unknown,
+466        }
+467    }
+468
+469    #[cfg(feature = "qlog")]
+470    pub(super) fn qlog_recovery_metrics(
+471        &mut self,
+472        path_id: PathId,
+473    ) -> Option<RecoveryMetricsUpdated> {
+474        let controller_metrics = self.congestion.metrics();
+475
+476        let metrics = RecoveryMetrics {
+477            min_rtt: Some(self.rtt.min),
+478            smoothed_rtt: Some(self.rtt.get()),
+479            latest_rtt: Some(self.rtt.latest),
+480            rtt_variance: Some(self.rtt.var),
+481            pto_count: Some(self.pto_count),
+482            bytes_in_flight: Some(self.in_flight.bytes),
+483            packets_in_flight: Some(self.in_flight.ack_eliciting),
+484
+485            congestion_window: Some(controller_metrics.congestion_window),
+486            ssthresh: controller_metrics.ssthresh,
+487            pacing_rate: controller_metrics.pacing_rate,
+488        };
+489
+490        let event = metrics.to_qlog_event(path_id, &self.recovery_metrics);
+491        self.recovery_metrics = metrics;
+492        event
+493    }
+494
+495    /// Return how long we need to wait before sending `bytes_to_send`
+496    ///
+497    /// See [`Pacer::delay`].
+498    pub(super) fn pacing_delay(&mut self, bytes_to_send: u64, now: Instant) -> Option<Instant> {
+499        let smoothed_rtt = self.rtt.get();
+500        self.pacing.delay(
+501            smoothed_rtt,
+502            bytes_to_send,
+503            self.current_mtu(),
+504            self.congestion.window(),
+505            now,
+506        )
+507    }
+508
+509    /// Updates the last observed address report received on this path.
+510    ///
+511    /// If the address was updated, it's returned to be informed to the application.
+512    #[must_use = "updated observed address must be reported to the application"]
+513    pub(super) fn update_observed_addr_report(
+514        &mut self,
+515        observed: ObservedAddr,
+516    ) -> Option<SocketAddr> {
+517        match self.last_observed_addr_report.as_mut() {
+518            Some(prev) => {
+519                if prev.seq_no >= observed.seq_no {
+520                    // frames that do not increase the sequence number on this path are ignored
+521                    None
+522                } else if prev.ip == observed.ip && prev.port == observed.port {
+523                    // keep track of the last seq_no but do not report the address as updated
+524                    prev.seq_no = observed.seq_no;
+525                    None
+526                } else {
+527                    let addr = observed.socket_addr();
+528                    self.last_observed_addr_report = Some(observed);
+529                    Some(addr)
+530                }
+531            }
+532            None => {
+533                let addr = observed.socket_addr();
+534                self.last_observed_addr_report = Some(observed);
+535                Some(addr)
+536            }
+537        }
+538    }
+539
+540    pub(crate) fn remote_status(&self) -> Option<PathStatus> {
+541        self.status.remote_status.map(|(_seq, status)| status)
+542    }
+543
+544    pub(crate) fn local_status(&self) -> PathStatus {
+545        self.status.local_status
+546    }
+547
+548    pub(super) fn generation(&self) -> u64 {
+549        self.generation
+550    }
+551}
+552
+553pub(super) enum OnPathResponseReceived {
+554    /// This response validates the path on its current remote address.
+555    OnPath { was_open: bool },
+556    /// This response is valid, but it's for a remote other than the path's current remote address.
+557    OffPath,
+558    /// The received token is unknown.
+559    Unknown,
+560    /// The response is invalid.
+561    Invalid {
+562        /// The 4-tuple that was expected for this token.
+563        expected: FourTuple,
+564    },
+565}
+566
+567/// Congestion metrics as described in [`recovery_metrics_updated`].
+568///
+569/// [`recovery_metrics_updated`]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-quic-events.html#name-recovery_metrics_updated
+570#[cfg(feature = "qlog")]
+571#[derive(Default, Clone, PartialEq, Debug)]
+572#[non_exhaustive]
+573struct RecoveryMetrics {
+574    pub min_rtt: Option<Duration>,
+575    pub smoothed_rtt: Option<Duration>,
+576    pub latest_rtt: Option<Duration>,
+577    pub rtt_variance: Option<Duration>,
+578    pub pto_count: Option<u32>,
+579    pub bytes_in_flight: Option<u64>,
+580    pub packets_in_flight: Option<u64>,
+581    pub congestion_window: Option<u64>,
+582    pub ssthresh: Option<u64>,
+583    pub pacing_rate: Option<u64>,
+584}
+585
+586#[cfg(feature = "qlog")]
+587impl RecoveryMetrics {
+588    /// Retain only values that have been updated since the last snapshot.
+589    fn retain_updated(&self, previous: &Self) -> Self {
+590        macro_rules! keep_if_changed {
+591            ($name:ident) => {
+592                if previous.$name == self.$name {
+593                    None
+594                } else {
+595                    self.$name
+596                }
+597            };
+598        }
+599
+600        Self {
+601            min_rtt: keep_if_changed!(min_rtt),
+602            smoothed_rtt: keep_if_changed!(smoothed_rtt),
+603            latest_rtt: keep_if_changed!(latest_rtt),
+604            rtt_variance: keep_if_changed!(rtt_variance),
+605            pto_count: keep_if_changed!(pto_count),
+606            bytes_in_flight: keep_if_changed!(bytes_in_flight),
+607            packets_in_flight: keep_if_changed!(packets_in_flight),
+608            congestion_window: keep_if_changed!(congestion_window),
+609            ssthresh: keep_if_changed!(ssthresh),
+610            pacing_rate: keep_if_changed!(pacing_rate),
+611        }
+612    }
+613
+614    /// Emit a `MetricsUpdated` event containing only updated values
+615    fn to_qlog_event(&self, path_id: PathId, previous: &Self) -> Option<RecoveryMetricsUpdated> {
+616        let updated = self.retain_updated(previous);
+617
+618        if updated == Self::default() {
+619            return None;
+620        }
+621
+622        Some(RecoveryMetricsUpdated {
+623            min_rtt: updated.min_rtt.map(|rtt| rtt.as_secs_f32()),
+624            smoothed_rtt: updated.smoothed_rtt.map(|rtt| rtt.as_secs_f32()),
+625            latest_rtt: updated.latest_rtt.map(|rtt| rtt.as_secs_f32()),
+626            rtt_variance: updated.rtt_variance.map(|rtt| rtt.as_secs_f32()),
+627            pto_count: updated
+628                .pto_count
+629                .map(|count| count.try_into().unwrap_or(u16::MAX)),
+630            bytes_in_flight: updated.bytes_in_flight,
+631            packets_in_flight: updated.packets_in_flight,
+632            congestion_window: updated.congestion_window,
+633            ssthresh: updated.ssthresh,
+634            pacing_rate: updated.pacing_rate,
+635            path_id: Some(path_id.as_u32() as u64),
+636        })
+637    }
+638}
+639
+640/// RTT estimation for a particular network path
+641#[derive(Copy, Clone, Debug)]
+642pub struct RttEstimator {
+643    /// The most recent RTT measurement made when receiving an ack for a previously unacked packet
+644    latest: Duration,
+645    /// The smoothed RTT of the connection, computed as described in RFC6298
+646    smoothed: Option<Duration>,
+647    /// The RTT variance, computed as described in RFC6298
+648    var: Duration,
+649    /// The minimum RTT seen in the connection, ignoring ack delay.
+650    min: Duration,
+651}
+652
+653impl RttEstimator {
+654    pub(super) fn new(initial_rtt: Duration) -> Self {
+655        Self {
+656            latest: initial_rtt,
+657            smoothed: None,
+658            var: initial_rtt / 2,
+659            min: initial_rtt,
+660        }
+661    }
+662
+663    /// Resets the estimator using a new initial_rtt value.
+664    ///
+665    /// This only resets the initial_rtt **if** no samples have been recorded yet. If there
+666    /// are any recorded samples the initial estimate can not be adjusted after the fact.
+667    ///
+668    /// This is useful when you receive a PATH_RESPONSE in the first packet received on a
+669    /// new path. In this case you can use the delay of the PATH_CHALLENGE-PATH_RESPONSE as
+670    /// the initial RTT to get a better expected estimation.
+671    ///
+672    /// A PATH_CHALLENGE-PATH_RESPONSE pair later in the connection should not be used
+673    /// explicitly as an estimation since PATH_CHALLENGE is an ACK-eliciting packet itself
+674    /// already.
+675    pub(crate) fn reset_initial_rtt(&mut self, initial_rtt: Duration) {
+676        if self.smoothed.is_none() {
+677            self.latest = initial_rtt;
+678            self.var = initial_rtt / 2;
+679            self.min = initial_rtt;
+680        }
+681    }
+682
+683    /// The current best RTT estimation.
+684    pub fn get(&self) -> Duration {
+685        self.smoothed.unwrap_or(self.latest)
+686    }
+687
+688    /// Conservative estimate of RTT
+689    ///
+690    /// Takes the maximum of smoothed and latest RTT, as recommended
+691    /// in 6.1.2 of the recovery spec (draft 29).
+692    pub fn conservative(&self) -> Duration {
+693        self.get().max(self.latest)
+694    }
+695
+696    /// Minimum RTT registered so far for this estimator.
+697    pub fn min(&self) -> Duration {
+698        self.min
+699    }
+700
+701    /// PTO computed as described in RFC9002#6.2.1.
+702    pub(crate) fn pto_base(&self) -> Duration {
+703        self.get() + cmp::max(4 * self.var, TIMER_GRANULARITY)
+704    }
+705
+706    /// Records an RTT sample.
+707    pub(crate) fn update(&mut self, ack_delay: Duration, rtt: Duration) {
+708        self.latest = rtt;
+709        // https://www.rfc-editor.org/rfc/rfc9002.html#section-5.2-3:
+710        // min_rtt does not adjust for ack_delay to avoid underestimating.
+711        self.min = cmp::min(self.min, self.latest);
+712        // Based on RFC6298.
+713        if let Some(smoothed) = self.smoothed {
+714            let adjusted_rtt = if self.min + ack_delay <= self.latest {
+715                self.latest - ack_delay
+716            } else {
+717                self.latest
+718            };
+719            let var_sample = smoothed.abs_diff(adjusted_rtt);
+720            self.var = (3 * self.var + var_sample) / 4;
+721            self.smoothed = Some((7 * smoothed + adjusted_rtt) / 8);
+722        } else {
+723            self.smoothed = Some(self.latest);
+724            self.var = self.latest / 2;
+725            self.min = self.latest;
+726        }
+727    }
+728}
+729
+730#[derive(Default, Debug)]
+731pub(crate) struct PathResponses {
+732    pending: Vec<PathResponse>,
+733}
+734
+735impl PathResponses {
+736    pub(crate) fn push(&mut self, packet: u64, token: u64, network_path: FourTuple) {
+737        /// Arbitrary permissive limit to prevent abuse
+738        const MAX_PATH_RESPONSES: usize = 16;
+739        let response = PathResponse {
+740            packet,
+741            token,
+742            network_path,
+743        };
+744        let existing = self
+745            .pending
+746            .iter_mut()
+747            .find(|x| x.network_path.remote == network_path.remote);
+748        if let Some(existing) = existing {
+749            // Update a queued response
+750            if existing.packet <= packet {
+751                *existing = response;
+752            }
+753            return;
+754        }
+755        if self.pending.len() < MAX_PATH_RESPONSES {
+756            self.pending.push(response);
+757        } else {
+758            // We don't expect to ever hit this with well-behaved peers, so we don't bother dropping
+759            // older challenges.
+760            trace!("ignoring excessive PATH_CHALLENGE");
+761        }
+762    }
+763
+764    pub(crate) fn pop_off_path(&mut self, network_path: FourTuple) -> Option<(u64, FourTuple)> {
+765        let response = *self.pending.last()?;
+766        // We use an exact comparison here, because once we've received for the first time,
+767        // we really should either already have a local_ip, or we will never get one
+768        // (because our OS doesn't support it).
+769        if response.network_path == network_path {
+770            // We don't bother searching further because we expect that the on-path response will
+771            // get drained in the immediate future by a call to `pop_on_path`
+772            return None;
+773        }
+774        self.pending.pop();
+775        Some((response.token, response.network_path))
+776    }
+777
+778    pub(crate) fn pop_on_path(&mut self, network_path: FourTuple) -> Option<u64> {
+779        let response = *self.pending.last()?;
+780        // Using an exact comparison. See explanation in `pop_off_path`.
+781        if response.network_path != network_path {
+782            // We don't bother searching further because we expect that the off-path response will
+783            // get drained in the immediate future by a call to `pop_off_path`
+784            return None;
+785        }
+786        self.pending.pop();
+787        Some(response.token)
+788    }
+789
+790    pub(crate) fn is_empty(&self) -> bool {
+791        self.pending.is_empty()
+792    }
+793}
+794
+795#[derive(Copy, Clone, Debug)]
+796struct PathResponse {
+797    /// The packet number the corresponding PATH_CHALLENGE was received in
+798    packet: u64,
+799    /// The token of the PATH_CHALLENGE
+800    token: u64,
+801    /// The path the corresponding PATH_CHALLENGE was received from
+802    network_path: FourTuple,
+803}
+804
+805/// Summary statistics of packets that have been sent on a particular path, but which have not yet
+806/// been acked or deemed lost
+807#[derive(Debug)]
+808pub(super) struct InFlight {
+809    /// Sum of the sizes of all sent packets considered "in flight" by congestion control
+810    ///
+811    /// The size does not include IP or UDP overhead. Packets only containing ACK frames do not
+812    /// count towards this to ensure congestion control does not impede congestion feedback.
+813    pub(super) bytes: u64,
+814    /// Number of packets in flight containing frames other than ACK and PADDING
+815    ///
+816    /// This can be 0 even when bytes is not 0 because PADDING frames cause a packet to be
+817    /// considered "in flight" by congestion control. However, if this is nonzero, bytes will always
+818    /// also be nonzero.
+819    pub(super) ack_eliciting: u64,
+820}
+821
+822impl InFlight {
+823    fn new() -> Self {
+824        Self {
+825            bytes: 0,
+826            ack_eliciting: 0,
+827        }
+828    }
+829
+830    fn insert(&mut self, packet: &SentPacket) {
+831        self.bytes += u64::from(packet.size);
+832        self.ack_eliciting += u64::from(packet.ack_eliciting);
+833    }
+834
+835    /// Update counters to account for a packet becoming acknowledged, lost, or abandoned
+836    fn remove(&mut self, packet: &SentPacket) {
+837        self.bytes -= u64::from(packet.size);
+838        self.ack_eliciting -= u64::from(packet.ack_eliciting);
+839    }
+840}
+841
+842/// State for QUIC-MULTIPATH PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP frames
+843#[derive(Debug, Clone, Default)]
+844pub(super) struct PathStatusState {
+845    /// The local status
+846    local_status: PathStatus,
+847    /// Local sequence number, for both PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP
+848    ///
+849    /// This is the number of the *next* path status frame to be sent.
+850    local_seq: VarInt,
+851    /// The status set by the remote
+852    remote_status: Option<(VarInt, PathStatus)>,
+853}
+854
+855impl PathStatusState {
+856    /// To be called on received PATH_STATUS_AVAILABLE/PATH_STATUS_BACKUP frames
+857    pub(super) fn remote_update(&mut self, status: PathStatus, seq: VarInt) {
+858        if self.remote_status.is_some_and(|(curr, _)| curr >= seq) {
+859            return trace!(%seq, "ignoring path status update");
+860        }
+861
+862        let prev = self.remote_status.replace((seq, status)).map(|(_, s)| s);
+863        if prev != Some(status) {
+864            debug!(?status, ?seq, "remote changed path status");
+865        }
+866    }
+867
+868    /// Updates the local status
+869    ///
+870    /// If the local status changed, the previous value is returned
+871    pub(super) fn local_update(&mut self, status: PathStatus) -> Option<PathStatus> {
+872        if self.local_status == status {
+873            return None;
+874        }
+875
+876        self.local_seq = self.local_seq.saturating_add(1u8);
+877        Some(std::mem::replace(&mut self.local_status, status))
+878    }
+879
+880    pub(crate) fn seq(&self) -> VarInt {
+881        self.local_seq
+882    }
+883}
+884
+885/// The QUIC-MULTIPATH path status
+886///
+887/// See section "3.3 Path Status Management":
+888/// <https://quicwg.org/multipath/draft-ietf-quic-multipath.html#name-path-status-management>
+889#[cfg_attr(test, derive(test_strategy::Arbitrary))]
+890#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
+891pub enum PathStatus {
+892    /// Paths marked with as available will be used when scheduling packets
+893    ///
+894    /// If multiple paths are available, packets will be scheduled on whichever has
+895    /// capacity.
+896    #[default]
+897    Available,
+898    /// Paths marked as backup will only be used if there are no available paths
+899    ///
+900    /// If the max_idle_timeout is specified the path will be kept alive so that it does not
+901    /// expire.
+902    Backup,
+903}
+904
+905/// Application events about paths
+906#[derive(Debug, Clone, PartialEq, Eq)]
+907pub enum PathEvent {
+908    /// A new path has been opened
+909    Opened {
+910        /// Which path is now open
+911        id: PathId,
+912    },
+913    /// A path has been closed
+914    Closed {
+915        /// Which path has been closed
+916        id: PathId,
+917        /// Error code supplied by the peer
+918        /// See <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-error-codes>
+919        /// for a list of known errors.
+920        error_code: VarInt,
+921    },
+922    /// All remaining state for a path has been removed
+923    ///
+924    /// The [`PathEvent::Closed`] would have been emitted for this path earlier.
+925    Abandoned {
+926        /// Which path had its state dropped
+927        id: PathId,
+928        /// The final path stats, they are no longer available via [`Connection::stats`]
+929        ///
+930        /// [`Connection::stats`]: super::Connection::stats
+931        path_stats: PathStats,
+932    },
+933    /// Path was closed locally
+934    LocallyClosed {
+935        /// Path for which the error occurred
+936        id: PathId,
+937        /// The error that occurred
+938        error: PathError,
+939    },
+940    /// The remote changed the status of the path
+941    ///
+942    /// The local status is not changed because of this event. It is up to the application
+943    /// to update the local status, which is used for packet scheduling, when the remote
+944    /// changes the status.
+945    RemoteStatus {
+946        /// Path which has changed status
+947        id: PathId,
+948        /// The new status set by the remote
+949        status: PathStatus,
+950    },
+951    /// Received an observation of our external address from the peer.
+952    ObservedAddr {
+953        /// Path over which the observed address was reported, [`PathId::ZERO`] when multipath is
+954        /// not negotiated
+955        id: PathId,
+956        /// The address observed by the remote over this path
+957        addr: SocketAddr,
+958    },
+959}
+960
+961/// Error from setting path status
+962#[derive(Debug, Error, Clone, PartialEq, Eq)]
+963pub enum SetPathStatusError {
+964    /// Error indicating that a path has not been opened or has already been abandoned
+965    #[error("closed path")]
+966    ClosedPath,
+967    /// Error indicating that this operation requires multipath to be negotiated whereas it hasn't been
+968    #[error("multipath not negotiated")]
+969    MultipathNotNegotiated,
+970}
+971
+972/// Error indicating that a path has not been opened or has already been abandoned
+973#[derive(Debug, Default, Error, Clone, PartialEq, Eq)]
+974#[error("closed path")]
+975pub struct ClosedPath {
+976    pub(super) _private: (),
+977}
+978
+979#[cfg(test)]
+980mod tests {
+981    use super::*;
+982
+983    #[test]
+984    fn test_path_id_saturating_add() {
+985        // add within range behaves normally
+986        let large: PathId = u16::MAX.into();
+987        let next = u32::from(u16::MAX) + 1;
+988        assert_eq!(large.saturating_add(1u8), PathId::from(next));
+989
+990        // outside range saturates
+991        assert_eq!(PathId::MAX.saturating_add(1u8), PathId::MAX)
+992    }
+993}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/qlog.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/qlog.rs.html new file mode 100644 index 000000000..1ddd09672 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/qlog.rs.html @@ -0,0 +1,1269 @@ +qlog.rs - source

iroh_quinn_proto/connection/
qlog.rs

1//! Implements support for emitting qlog events.
+2//!
+3//! This uses the [`qlog`] crate to emit qlog events. The n0-qlog crate, and thus this
+4//! implementation, is currently based on [draft-ietf-quic-qlog-main-schema-13] an
+5//! [draft-ietf-quic-qlog-quic-events-12].
+6//!
+7//! [draft-ietf-quic-qlog-main-schema-13]: https://www.ietf.org/archive/id/draft-ietf-quic-qlog-main-schema-13.html
+8//! [draft-ietf-quic-qlog-quic-events-12]: https://www.ietf.org/archive/id/draft-ietf-quic-qlog-quic-events-12.html
+9
+10// Function bodies in this module are regularly cfg'd out
+11#![allow(unused_variables)]
+12
+13#[cfg(not(feature = "qlog"))]
+14use std::marker::PhantomData;
+15#[cfg(feature = "qlog")]
+16use std::sync::{Arc, Mutex};
+17use std::{
+18    net::{IpAddr, SocketAddr},
+19    time::Duration,
+20};
+21
+22#[cfg(feature = "qlog")]
+23use qlog::{
+24    CommonFields, HexSlice, TokenType, VantagePoint,
+25    events::{
+26        ApplicationError, ConnectionClosedFrameError, Event, EventData, RawInfo, TupleEndpointInfo,
+27        quic::{
+28            self, AckedRanges, AddressDiscoveryRole, ConnectionStarted, ErrorSpace, PacketHeader,
+29            PacketLost, PacketLostTrigger, PacketReceived, PacketSent, PacketType,
+30            ParametersRestored, ParametersSet, PreferredAddress, QlogTimerType, QuicFrame,
+31            StreamType, TimerEventType, TimerType, TimerUpdated, TransportInitiator, TupleAssigned,
+32        },
+33    },
+34    streamer::QlogStreamer,
+35};
+36#[cfg(feature = "qlog")]
+37use tracing::warn;
+38
+39use crate::{
+40    Connection, ConnectionId, FourTuple, Frame, Instant, PathId,
+41    connection::{PathData, SentPacket, timer::Timer},
+42    frame::EncodableFrame,
+43    packet::{Header, SpaceId},
+44    transport_parameters::TransportParameters,
+45};
+46#[cfg(feature = "qlog")]
+47use crate::{
+48    QlogConfig, Side, TransportErrorCode,
+49    connection::timer::{ConnTimer, PathTimer},
+50    frame::{self, DataBlocked, StreamDataBlocked, StreamsBlocked},
+51};
+52
+53/// Shareable handle to a single qlog output stream
+54#[cfg(feature = "qlog")]
+55#[derive(Clone)]
+56pub(crate) struct QlogStream(Arc<Mutex<QlogStreamer>>);
+57
+58#[cfg(feature = "qlog")]
+59impl QlogStream {
+60    pub(crate) fn new(
+61        config: QlogConfig,
+62        initial_dst_cid: ConnectionId,
+63        side: Side,
+64        now: Instant,
+65    ) -> Result<Self, qlog::Error> {
+66        let vantage_point = VantagePoint {
+67            name: None,
+68            ty: match side {
+69                Side::Client => qlog::VantagePointType::Client,
+70                Side::Server => qlog::VantagePointType::Server,
+71            },
+72            flow: None,
+73        };
+74
+75        let common_fields = CommonFields {
+76            group_id: Some(initial_dst_cid.to_string()),
+77            ..Default::default()
+78        };
+79
+80        let trace = qlog::TraceSeq::new(
+81            config.title.clone(),
+82            config.description.clone(),
+83            Some(common_fields),
+84            Some(vantage_point),
+85            vec![],
+86        );
+87
+88        let start_time = config.start_time.unwrap_or(now);
+89
+90        let mut streamer = QlogStreamer::new(
+91            config.title,
+92            config.description,
+93            start_time,
+94            trace,
+95            qlog::events::EventImportance::Extra,
+96            config.writer,
+97        );
+98
+99        streamer.start_log()?;
+100        Ok(Self(Arc::new(Mutex::new(streamer))))
+101    }
+102
+103    fn emit_event(&self, event: EventData, now: Instant) {
+104        self.emit_event_with_tuple_id(event, now, None);
+105    }
+106
+107    fn emit_event_with_tuple_id(
+108        &self,
+109        event: EventData,
+110        now: Instant,
+111        network_path: Option<String>,
+112    ) {
+113        // Time will be overwritten by `add_event_with_instant`
+114        let mut event = Event::with_time(0.0, event);
+115        event.tuple = network_path;
+116        let mut qlog_streamer = self.0.lock().unwrap();
+117        if let Err(e) = qlog_streamer.add_event_with_instant(event, now) {
+118            warn!("could not emit qlog event: {e}");
+119        }
+120    }
+121}
+122
+123/// A [`QlogStream`] that may be either dynamically disabled or compiled out entirely
+124#[derive(Clone, Default)]
+125pub(crate) struct QlogSink {
+126    #[cfg(feature = "qlog")]
+127    stream: Option<QlogStream>,
+128}
+129
+130impl QlogSink {
+131    #[cfg(feature = "qlog")]
+132    pub(crate) fn new(
+133        config: QlogConfig,
+134        initial_dst_cid: ConnectionId,
+135        side: Side,
+136        now: Instant,
+137    ) -> Self {
+138        let stream = QlogStream::new(config, initial_dst_cid, side, now)
+139            .inspect_err(|err| warn!("failed to initialize qlog streamer: {err}"))
+140            .ok();
+141        Self { stream }
+142    }
+143
+144    pub(crate) fn emit_connection_started(
+145        &self,
+146        now: Instant,
+147        local_cid: ConnectionId,
+148        remote_cid: ConnectionId,
+149        remote: SocketAddr,
+150        local_ip: Option<IpAddr>,
+151        transport_params: &TransportParameters,
+152    ) {
+153        #[cfg(feature = "qlog")]
+154        {
+155            let Some(stream) = self.stream.as_ref() else {
+156                return;
+157            };
+158            stream.emit_event(
+159                EventData::ConnectionStarted(ConnectionStarted {
+160                    local: tuple_endpoint_info(local_ip, None, Some(local_cid)),
+161                    remote: tuple_endpoint_info(
+162                        Some(remote.ip()),
+163                        Some(remote.port()),
+164                        Some(remote_cid),
+165                    ),
+166                }),
+167                now,
+168            );
+169
+170            let params = transport_params.to_qlog(TransportInitiator::Local);
+171            let event = EventData::ParametersSet(params);
+172            stream.emit_event(event, now);
+173        }
+174    }
+175
+176    pub(super) fn emit_recovery_metrics(&self, path_id: PathId, path: &mut PathData, now: Instant) {
+177        #[cfg(feature = "qlog")]
+178        {
+179            let Some(stream) = self.stream.as_ref() else {
+180                return;
+181            };
+182
+183            let Some(metrics) = path.qlog_recovery_metrics(path_id) else {
+184                return;
+185            };
+186
+187            stream.emit_event(EventData::MetricsUpdated(metrics), now);
+188        }
+189    }
+190
+191    pub(super) fn emit_packet_lost(
+192        &self,
+193        pn: u64,
+194        info: &SentPacket,
+195        loss_delay: Duration,
+196        space: SpaceId,
+197        now: Instant,
+198    ) {
+199        #[cfg(feature = "qlog")]
+200        {
+201            let Some(stream) = self.stream.as_ref() else {
+202                return;
+203            };
+204
+205            let event = PacketLost {
+206                header: Some(PacketHeader {
+207                    packet_number: Some(pn),
+208                    packet_type: packet_type(space, false),
+209                    length: Some(info.size),
+210                    ..Default::default()
+211                }),
+212                frames: None,
+213                trigger: Some(
+214                    match info.time_sent.saturating_duration_since(now) >= loss_delay {
+215                        true => PacketLostTrigger::TimeThreshold,
+216                        false => PacketLostTrigger::ReorderingThreshold,
+217                    },
+218                ),
+219                is_mtu_probe_packet: None,
+220            };
+221
+222            stream.emit_event(EventData::PacketLost(event), now);
+223        }
+224    }
+225
+226    pub(super) fn emit_peer_transport_params_restored(&self, conn: &Connection, now: Instant) {
+227        #[cfg(feature = "qlog")]
+228        {
+229            let Some(stream) = self.stream.as_ref() else {
+230                return;
+231            };
+232            let params = conn.peer_params.to_qlog_restored();
+233            let event = EventData::ParametersRestored(params);
+234            stream.emit_event(event, now);
+235        }
+236    }
+237
+238    pub(super) fn emit_peer_transport_params_received(&self, conn: &Connection, now: Instant) {
+239        #[cfg(feature = "qlog")]
+240        {
+241            let Some(stream) = self.stream.as_ref() else {
+242                return;
+243            };
+244            let params = conn.peer_params.to_qlog(TransportInitiator::Remote);
+245            let event = EventData::ParametersSet(params);
+246            stream.emit_event(event, now);
+247        }
+248    }
+249
+250    pub(super) fn emit_tuple_assigned(&self, path_id: PathId, tuple: FourTuple, now: Instant) {
+251        #[cfg(feature = "qlog")]
+252        {
+253            let Some(stream) = self.stream.as_ref() else {
+254                return;
+255            };
+256            let tuple_id = fmt_tuple_id(path_id.as_u32() as u64);
+257            let event = TupleAssigned {
+258                tuple_id,
+259                tuple_local: tuple
+260                    .local_ip
+261                    .map(|local_ip| tuple_endpoint_info(Some(local_ip), None, None)),
+262                tuple_remote: Some(tuple_endpoint_info(
+263                    Some(tuple.remote.ip()),
+264                    Some(tuple.remote.port()),
+265                    None,
+266                )),
+267            };
+268
+269            stream.emit_event(EventData::TupleAssigned(event), now);
+270        }
+271    }
+272
+273    pub(super) fn emit_packet_sent(&self, packet: QlogSentPacket, now: Instant) {
+274        #[cfg(feature = "qlog")]
+275        {
+276            let Some(stream) = self.stream.as_ref() else {
+277                return;
+278            };
+279            let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id);
+280            stream.emit_event_with_tuple_id(EventData::PacketSent(packet.inner), now, tuple_id);
+281        }
+282    }
+283
+284    pub(super) fn emit_packet_received(&self, packet: QlogRecvPacket, now: Instant) {
+285        #[cfg(feature = "qlog")]
+286        {
+287            let Some(stream) = self.stream.as_ref() else {
+288                return;
+289            };
+290            let mut packet = packet;
+291            packet.emit_padding();
+292            let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id);
+293            let event = packet.inner;
+294            stream.emit_event_with_tuple_id(EventData::PacketReceived(event), now, tuple_id);
+295        }
+296    }
+297
+298    /// Emits a timer event.
+299    ///
+300    /// This function is not public: Instead, create a [`QlogSinkWithTime`] via [`Self::with_time`] and use
+301    /// its `emit_timer_` methods.
+302    #[cfg(feature = "qlog")]
+303    fn emit_timer(&self, timer: Timer, op: TimerOp, now: Instant) {
+304        let Some(stream) = self.stream.as_ref() else {
+305            return;
+306        };
+307
+308        let timer_type: Option<TimerType> = match timer {
+309            Timer::Conn(conn_timer) => match conn_timer {
+310                ConnTimer::Idle => Some(QlogTimerType::IdleTimeout.into()),
+311                ConnTimer::Close => Some(TimerType::custom("close")),
+312                ConnTimer::KeyDiscard => Some(TimerType::custom("key_discard")),
+313                ConnTimer::KeepAlive => Some(TimerType::custom("keep_alive")),
+314                ConnTimer::PushNewCid => Some(TimerType::custom("push_new_cid")),
+315            },
+316            Timer::PerPath(_, path_timer) => match path_timer {
+317                PathTimer::LossDetection => Some(QlogTimerType::LossTimeout.into()),
+318                PathTimer::PathIdle => Some(TimerType::custom("path_idle")),
+319                PathTimer::PathValidation => Some(QlogTimerType::PathValidation.into()),
+320                PathTimer::PathChallengeLost => Some(TimerType::custom("path_challenge_lost")),
+321                PathTimer::PathOpen => Some(TimerType::custom("path_open")),
+322                PathTimer::PathKeepAlive => Some(TimerType::custom("path_keep_alive")),
+323                PathTimer::Pacing => Some(TimerType::custom("pacing")),
+324                PathTimer::MaxAckDelay => Some(QlogTimerType::Ack.into()),
+325                PathTimer::DiscardPath => Some(TimerType::custom("discard_path")),
+326            },
+327        };
+328
+329        let Some(timer_type) = timer_type else {
+330            return;
+331        };
+332
+333        let delta = match op {
+334            TimerOp::Set(instant) => instant
+335                .checked_duration_since(now)
+336                .map(|dur| dur.as_secs_f32() * 1000.),
+337            _ => None,
+338        };
+339        let path_id = match timer {
+340            Timer::Conn(_) => None,
+341            Timer::PerPath(path_id, _) => Some(path_id.as_u32() as u64),
+342        };
+343
+344        let event_type = match op {
+345            TimerOp::Set(_) => TimerEventType::Set,
+346            TimerOp::Expire => TimerEventType::Expired,
+347            TimerOp::Cancelled => TimerEventType::Cancelled,
+348        };
+349
+350        let event = TimerUpdated {
+351            path_id,
+352            timer_type: Some(timer_type),
+353            timer_id: None,
+354            packet_number_space: None,
+355            event_type,
+356            delta,
+357        };
+358        stream.emit_event(EventData::TimerUpdated(event), now);
+359    }
+360
+361    /// Returns a [`QlogSinkWithTime`] that passes along a `now` timestamp.
+362    ///
+363    /// This may be used if you want to pass a [`QlogSink`] downwards together with the current
+364    /// `now` timestamp, to not have to pass the latter separately as an additional argument just
+365    /// for qlog support.
+366    pub(super) fn with_time(&self, now: Instant) -> QlogSinkWithTime<'_> {
+367        #[cfg(feature = "qlog")]
+368        let s = QlogSinkWithTime { sink: self, now };
+369        #[cfg(not(feature = "qlog"))]
+370        let s = QlogSinkWithTime {
+371            _phantom: PhantomData,
+372        };
+373        s
+374    }
+375}
+376
+377/// A [`QlogSink`] with a `now` timestamp.
+378#[derive(Clone)]
+379pub(super) struct QlogSinkWithTime<'a> {
+380    #[cfg(feature = "qlog")]
+381    sink: &'a QlogSink,
+382    #[cfg(feature = "qlog")]
+383    now: Instant,
+384    #[cfg(not(feature = "qlog"))]
+385    _phantom: PhantomData<&'a ()>,
+386}
+387
+388impl<'a> QlogSinkWithTime<'a> {
+389    pub(super) fn emit_timer_stop(&self, timer: Timer) {
+390        #[cfg(feature = "qlog")]
+391        self.sink.emit_timer(timer, TimerOp::Cancelled, self.now)
+392    }
+393
+394    pub(super) fn emit_timer_set(&self, timer: Timer, expire_at: Instant) {
+395        #[cfg(feature = "qlog")]
+396        self.sink
+397            .emit_timer(timer, TimerOp::Set(expire_at), self.now)
+398    }
+399
+400    pub(super) fn emit_timer_expire(&self, timer: Timer) {
+401        #[cfg(feature = "qlog")]
+402        self.sink.emit_timer(timer, TimerOp::Expire, self.now)
+403    }
+404}
+405
+406#[cfg(feature = "qlog")]
+407enum TimerOp {
+408    Set(Instant),
+409    Expire,
+410    Cancelled,
+411}
+412
+413/// Info about a sent packet. Zero-sized struct if `qlog` feature is not enabled.
+414#[derive(Default)]
+415pub(crate) struct QlogSentPacket {
+416    #[cfg(feature = "qlog")]
+417    inner: PacketSent,
+418}
+419
+420impl QlogSentPacket {
+421    /// Sets data from the packet header.
+422    pub(crate) fn header(
+423        &mut self,
+424        header: &Header,
+425        pn: Option<u64>,
+426        space: SpaceId,
+427        is_0rtt: bool,
+428        path_id: PathId,
+429    ) {
+430        #[cfg(feature = "qlog")]
+431        {
+432            self.inner.header.scid = header.src_cid().map(stringify_cid);
+433            self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
+434            self.inner.header.packet_number = pn;
+435            self.inner.header.packet_type = packet_type(space, is_0rtt);
+436            self.inner.header.path_id = Some(path_id.as_u32() as u64);
+437        }
+438    }
+439
+440    /// Adds a PADDING frame.
+441    ///
+442    /// This is a no-op if the `qlog` feature is not enabled.
+443    pub(crate) fn frame_padding(&mut self, count: usize) {
+444        #[cfg(feature = "qlog")]
+445        self.frame_raw(QuicFrame::Padding {
+446            raw: Some(RawInfo {
+447                length: Some(count as u64),
+448                payload_length: Some(count as u64),
+449                data: None,
+450            }),
+451        });
+452    }
+453
+454    /// Adds a frame by pushing a [`QuicFrame`].
+455    ///
+456    /// This function is only available if the `qlog` feature is enabled, because constructing a [`QuicFrame`] may involve
+457    /// calculations which shouldn't be performed if the `qlog` feature is disabled.
+458    #[cfg(feature = "qlog")]
+459    fn frame_raw(&mut self, frame: QuicFrame) {
+460        self.inner.frames.get_or_insert_default().push(frame);
+461    }
+462
+463    /// Finalizes the packet by setting the final packet length (after encryption).
+464    pub(super) fn finalize(&mut self, len: usize) {
+465        #[cfg(feature = "qlog")]
+466        {
+467            self.inner.header.length = Some(len as u16);
+468        }
+469    }
+470
+471    pub(crate) fn record<'a>(&mut self, frame: &EncodableFrame<'a>) {
+472        #[cfg(feature = "qlog")]
+473        self.frame_raw(frame.to_qlog());
+474    }
+475}
+476
+477/// Info about a received packet. Zero-sized struct if `qlog` feature is not enabled.
+478pub(crate) struct QlogRecvPacket {
+479    #[cfg(feature = "qlog")]
+480    inner: PacketReceived,
+481    #[cfg(feature = "qlog")]
+482    padding: usize,
+483}
+484
+485impl QlogRecvPacket {
+486    /// Creates a new [`QlogRecvPacket`]. Noop if `qlog` feature is not enabled.
+487    ///
+488    /// `len` is the packet's full length (before decryption).
+489    pub(crate) fn new(len: usize) -> Self {
+490        #[cfg(not(feature = "qlog"))]
+491        let this = Self {};
+492
+493        #[cfg(feature = "qlog")]
+494        let this = {
+495            let mut this = Self {
+496                inner: Default::default(),
+497                padding: 0,
+498            };
+499            this.inner.header.length = Some(len as u16);
+500            this
+501        };
+502
+503        this
+504    }
+505
+506    /// Adds info from the packet header.
+507    pub(crate) fn header(&mut self, header: &Header, pn: Option<u64>, path_id: PathId) {
+508        #[cfg(feature = "qlog")]
+509        {
+510            let is_0rtt = !header.is_1rtt();
+511            self.inner.header.scid = header.src_cid().map(stringify_cid);
+512            self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
+513            self.inner.header.packet_number = pn;
+514            self.inner.header.packet_type = packet_type(header.space(), is_0rtt);
+515            self.inner.header.path_id = Some(path_id.as_u32() as u64);
+516        }
+517    }
+518
+519    /// Adds a frame.
+520    pub(crate) fn frame(&mut self, frame: &Frame) {
+521        #[cfg(feature = "qlog")]
+522        {
+523            if matches!(frame, crate::Frame::Padding) {
+524                self.padding += 1;
+525            } else {
+526                self.emit_padding();
+527                self.inner
+528                    .frames
+529                    .get_or_insert_default()
+530                    .push(frame.to_qlog())
+531            }
+532        }
+533    }
+534
+535    #[cfg(feature = "qlog")]
+536    fn emit_padding(&mut self) {
+537        if self.padding > 0 {
+538            self.inner
+539                .frames
+540                .get_or_insert_default()
+541                .push(QuicFrame::Padding {
+542                    raw: Some(RawInfo {
+543                        length: Some(self.padding as u64),
+544                        payload_length: Some(self.padding as u64),
+545                        data: None,
+546                    }),
+547                });
+548            self.padding = 0;
+549        }
+550    }
+551}
+552
+553/* Frame conversions to qlog */
+554#[cfg(feature = "qlog")]
+555pub(crate) trait ToQlog {
+556    fn to_qlog(&self) -> QuicFrame;
+557}
+558
+559#[cfg(feature = "qlog")]
+560impl<'a> ToQlog for frame::AckEncoder<'a> {
+561    fn to_qlog(&self) -> QuicFrame {
+562        QuicFrame::Ack {
+563            ack_delay: Some(self.delay as f32),
+564            acked_ranges: Some(AckedRanges::Double(
+565                self.ranges
+566                    .iter()
+567                    .map(|range| (range.start, range.end))
+568                    .collect(),
+569            )),
+570            ect1: self.ecn.map(|e| e.ect1),
+571            ect0: self.ecn.map(|e| e.ect0),
+572            ce: self.ecn.map(|e| e.ce),
+573            raw: None,
+574        }
+575    }
+576}
+577
+578#[cfg(feature = "qlog")]
+579impl ToQlog for frame::AckFrequency {
+580    fn to_qlog(&self) -> QuicFrame {
+581        QuicFrame::AckFrequency {
+582            sequence_number: self.sequence.into_inner(),
+583            ack_eliciting_threshold: self.ack_eliciting_threshold.into_inner(),
+584            requested_max_ack_delay: self.request_max_ack_delay.into_inner(),
+585            reordering_threshold: self.reordering_threshold.into_inner(),
+586            raw: None,
+587        }
+588    }
+589}
+590
+591#[cfg(feature = "qlog")]
+592impl ToQlog for frame::AddAddress {
+593    fn to_qlog(&self) -> QuicFrame {
+594        QuicFrame::AddAddress {
+595            sequence_number: self.seq_no.into_inner(),
+596            ip_v4: match self.ip {
+597                IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+598                IpAddr::V6(ipv6_addr) => None,
+599            },
+600            ip_v6: match self.ip {
+601                IpAddr::V4(ipv4_addr) => None,
+602                IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+603            },
+604            port: self.port,
+605        }
+606    }
+607}
+608
+609#[cfg(feature = "qlog")]
+610impl ToQlog for frame::CloseEncoder<'_> {
+611    fn to_qlog(&self) -> QuicFrame {
+612        self.close.to_qlog()
+613    }
+614}
+615
+616#[cfg(feature = "qlog")]
+617impl ToQlog for frame::Close {
+618    fn to_qlog(&self) -> QuicFrame {
+619        match self {
+620            Self::Connection(f) => {
+621                let (error, error_code) = transport_error(f.error_code);
+622                let error = error.map(|transport_error| {
+623                    ConnectionClosedFrameError::TransportError(transport_error)
+624                });
+625                QuicFrame::ConnectionClose {
+626                    error_space: Some(ErrorSpace::TransportError),
+627                    error,
+628                    error_code,
+629                    reason: String::from_utf8(f.reason.to_vec()).ok(),
+630                    reason_bytes: None,
+631                    trigger_frame_type: None,
+632                }
+633            }
+634            Self::Application(f) => QuicFrame::ConnectionClose {
+635                error_space: Some(ErrorSpace::ApplicationError),
+636                error: None,
+637                error_code: Some(f.error_code.into_inner()),
+638                reason: String::from_utf8(f.reason.to_vec()).ok(),
+639                reason_bytes: None,
+640                trigger_frame_type: None,
+641            },
+642        }
+643    }
+644}
+645
+646#[cfg(feature = "qlog")]
+647impl ToQlog for frame::Crypto {
+648    fn to_qlog(&self) -> QuicFrame {
+649        QuicFrame::Crypto {
+650            offset: self.offset,
+651            raw: Some(RawInfo {
+652                length: Some(self.data.len() as u64),
+653                ..Default::default()
+654            }),
+655        }
+656    }
+657}
+658
+659#[cfg(feature = "qlog")]
+660impl ToQlog for frame::Datagram {
+661    fn to_qlog(&self) -> QuicFrame {
+662        QuicFrame::Datagram {
+663            raw: Some(RawInfo {
+664                length: Some(self.data.len() as u64),
+665                ..Default::default()
+666            }),
+667        }
+668    }
+669}
+670
+671#[cfg(feature = "qlog")]
+672impl ToQlog for frame::HandshakeDone {
+673    fn to_qlog(&self) -> QuicFrame {
+674        QuicFrame::HandshakeDone { raw: None }
+675    }
+676}
+677
+678#[cfg(feature = "qlog")]
+679impl ToQlog for frame::ImmediateAck {
+680    fn to_qlog(&self) -> QuicFrame {
+681        QuicFrame::ImmediateAck { raw: None }
+682    }
+683}
+684
+685#[cfg(feature = "qlog")]
+686impl ToQlog for frame::MaxData {
+687    fn to_qlog(&self) -> QuicFrame {
+688        QuicFrame::MaxData {
+689            maximum: self.0.into(),
+690            raw: None,
+691        }
+692    }
+693}
+694
+695#[cfg(feature = "qlog")]
+696impl ToQlog for frame::MaxPathId {
+697    fn to_qlog(&self) -> QuicFrame {
+698        QuicFrame::MaxPathId {
+699            maximum_path_id: self.0.as_u32().into(),
+700            raw: None,
+701        }
+702    }
+703}
+704
+705#[cfg(feature = "qlog")]
+706impl ToQlog for frame::MaxStreamData {
+707    fn to_qlog(&self) -> QuicFrame {
+708        QuicFrame::MaxStreamData {
+709            stream_id: self.id.into(),
+710            maximum: self.offset,
+711            raw: None,
+712        }
+713    }
+714}
+715
+716#[cfg(feature = "qlog")]
+717impl ToQlog for frame::MaxStreams {
+718    fn to_qlog(&self) -> QuicFrame {
+719        QuicFrame::MaxStreams {
+720            maximum: self.count,
+721            stream_type: self.dir.into(),
+722            raw: None,
+723        }
+724    }
+725}
+726
+727#[cfg(feature = "qlog")]
+728impl ToQlog for frame::NewConnectionId {
+729    fn to_qlog(&self) -> QuicFrame {
+730        match self.path_id {
+731            None => QuicFrame::NewConnectionId {
+732                sequence_number: self.sequence,
+733                retire_prior_to: self.retire_prior_to,
+734                connection_id_length: Some(self.id.len() as u8),
+735                connection_id: self.id.to_string(),
+736                stateless_reset_token: Some(self.reset_token.to_string()),
+737                raw: None,
+738            },
+739            Some(path_id) => QuicFrame::PathNewConnectionId {
+740                path_id: path_id.0 as u64,
+741                sequence_number: self.sequence,
+742                retire_prior_to: self.retire_prior_to,
+743                connection_id_length: Some(self.id.len() as u8),
+744                connection_id: self.id.to_string(),
+745                stateless_reset_token: Some(self.reset_token.to_string()),
+746                raw: None,
+747            },
+748        }
+749    }
+750}
+751
+752#[cfg(feature = "qlog")]
+753impl ToQlog for frame::NewToken {
+754    fn to_qlog(&self) -> QuicFrame {
+755        QuicFrame::NewToken {
+756            token: qlog::Token {
+757                ty: Some(TokenType::Retry),
+758                raw: Some(RawInfo {
+759                    data: HexSlice::maybe_string(Some(&self.token)),
+760                    length: Some(self.token.len() as u64),
+761                    payload_length: None,
+762                }),
+763                details: None,
+764            },
+765            raw: None,
+766        }
+767    }
+768}
+769
+770#[cfg(feature = "qlog")]
+771impl ToQlog for frame::ObservedAddr {
+772    fn to_qlog(&self) -> QuicFrame {
+773        QuicFrame::ObservedAddress {
+774            sequence_number: self.seq_no.into_inner(),
+775            ip_v4: match self.ip {
+776                IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+777                IpAddr::V6(ipv6_addr) => None,
+778            },
+779            ip_v6: match self.ip {
+780                IpAddr::V4(ipv4_addr) => None,
+781                IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+782            },
+783            port: self.port,
+784            raw: None,
+785        }
+786    }
+787}
+788
+789#[cfg(feature = "qlog")]
+790impl ToQlog for frame::PathAbandon {
+791    fn to_qlog(&self) -> QuicFrame {
+792        QuicFrame::PathAbandon {
+793            path_id: self.path_id.as_u32().into(),
+794            error_code: self.error_code.into(),
+795            raw: None,
+796        }
+797    }
+798}
+799
+800#[cfg(feature = "qlog")]
+801impl ToQlog for frame::PathAckEncoder<'_> {
+802    fn to_qlog(&self) -> QuicFrame {
+803        QuicFrame::PathAck {
+804            path_id: self.path_id.as_u32() as u64,
+805            ack_delay: Some(self.delay as f32),
+806            acked_ranges: Some(AckedRanges::Double(
+807                self.ranges
+808                    .iter()
+809                    .map(|range| (range.start, range.end))
+810                    .collect(),
+811            )),
+812            ect1: self.ecn.map(|e| e.ect1),
+813            ect0: self.ecn.map(|e| e.ect0),
+814            ce: self.ecn.map(|e| e.ce),
+815            raw: None,
+816        }
+817    }
+818}
+819
+820#[cfg(feature = "qlog")]
+821impl ToQlog for frame::PathChallenge {
+822    #[cfg(feature = "qlog")]
+823    fn to_qlog(&self) -> QuicFrame {
+824        QuicFrame::PathChallenge {
+825            data: Some(self.to_string()),
+826            raw: None,
+827        }
+828    }
+829}
+830
+831#[cfg(feature = "qlog")]
+832impl ToQlog for frame::PathCidsBlocked {
+833    fn to_qlog(&self) -> QuicFrame {
+834        QuicFrame::PathCidsBlocked {
+835            path_id: self.path_id.as_u32().into(),
+836            next_sequence_number: self.next_seq.into(),
+837            raw: None,
+838        }
+839    }
+840}
+841
+842#[cfg(feature = "qlog")]
+843impl ToQlog for frame::PathResponse {
+844    fn to_qlog(&self) -> QuicFrame {
+845        QuicFrame::PathResponse {
+846            data: Some(self.to_string()),
+847            raw: None,
+848        }
+849    }
+850}
+851
+852#[cfg(feature = "qlog")]
+853impl ToQlog for frame::ReachOut {
+854    fn to_qlog(&self) -> QuicFrame {
+855        QuicFrame::ReachOut {
+856            round: self.round.into_inner(),
+857            ip_v4: match self.ip {
+858                IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+859                IpAddr::V6(ipv6_addr) => None,
+860            },
+861            ip_v6: match self.ip {
+862                IpAddr::V4(ipv4_addr) => None,
+863                IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+864            },
+865            port: self.port,
+866        }
+867    }
+868}
+869
+870#[cfg(feature = "qlog")]
+871impl ToQlog for frame::Ping {
+872    fn to_qlog(&self) -> QuicFrame {
+873        QuicFrame::Ping { raw: None }
+874    }
+875}
+876
+877#[cfg(feature = "qlog")]
+878impl ToQlog for frame::PathStatusAvailable {
+879    fn to_qlog(&self) -> QuicFrame {
+880        QuicFrame::PathStatusAvailable {
+881            path_id: self.path_id.as_u32().into(),
+882            path_status_sequence_number: self.status_seq_no.into(),
+883            raw: None,
+884        }
+885    }
+886}
+887
+888#[cfg(feature = "qlog")]
+889impl ToQlog for frame::PathStatusBackup {
+890    fn to_qlog(&self) -> QuicFrame {
+891        QuicFrame::PathStatusBackup {
+892            path_id: self.path_id.as_u32().into(),
+893            path_status_sequence_number: self.status_seq_no.into(),
+894            raw: None,
+895        }
+896    }
+897}
+898
+899#[cfg(feature = "qlog")]
+900impl ToQlog for frame::PathsBlocked {
+901    fn to_qlog(&self) -> QuicFrame {
+902        QuicFrame::PathsBlocked {
+903            maximum_path_id: self.0.as_u32().into(),
+904            raw: None,
+905        }
+906    }
+907}
+908
+909#[cfg(feature = "qlog")]
+910impl ToQlog for frame::ResetStream {
+911    fn to_qlog(&self) -> QuicFrame {
+912        QuicFrame::ResetStream {
+913            stream_id: self.id.into(),
+914            error_code: Some(self.error_code.into_inner()),
+915            final_size: self.final_offset.into(),
+916            error: ApplicationError::Unknown,
+917            raw: None,
+918        }
+919    }
+920}
+921
+922#[cfg(feature = "qlog")]
+923impl ToQlog for frame::StopSending {
+924    fn to_qlog(&self) -> QuicFrame {
+925        QuicFrame::StopSending {
+926            stream_id: self.id.into(),
+927            error_code: Some(self.error_code.into_inner()),
+928            error: ApplicationError::Unknown,
+929            raw: None,
+930        }
+931    }
+932}
+933
+934#[cfg(feature = "qlog")]
+935impl ToQlog for frame::RetireConnectionId {
+936    fn to_qlog(&self) -> QuicFrame {
+937        match self.path_id {
+938            None => QuicFrame::RetireConnectionId {
+939                sequence_number: self.sequence,
+940                raw: None,
+941            },
+942            Some(path_id) => QuicFrame::PathRetireConnectionId {
+943                path_id: path_id.0 as u64,
+944                sequence_number: self.sequence,
+945                raw: None,
+946            },
+947        }
+948    }
+949}
+950
+951#[cfg(feature = "qlog")]
+952impl ToQlog for frame::RemoveAddress {
+953    fn to_qlog(&self) -> QuicFrame {
+954        QuicFrame::RemoveAddress {
+955            sequence_number: self.seq_no.into_inner(),
+956        }
+957    }
+958}
+959
+960#[cfg(feature = "qlog")]
+961impl ToQlog for frame::StreamMetaEncoder {
+962    fn to_qlog(&self) -> QuicFrame {
+963        let meta = &self.meta;
+964        QuicFrame::Stream {
+965            stream_id: meta.id.into(),
+966            offset: Some(meta.offsets.start),
+967            fin: Some(meta.fin),
+968            raw: Some(RawInfo {
+969                length: Some(meta.offsets.end - meta.offsets.start),
+970                ..Default::default()
+971            }),
+972        }
+973    }
+974}
+975
+976#[cfg(feature = "qlog")]
+977impl Frame {
+978    /// Converts a [`crate::Frame`] into a [`QuicFrame`].
+979    pub(crate) fn to_qlog(&self) -> QuicFrame {
+980        match self {
+981            Self::Padding => QuicFrame::Padding {
+982                raw: Some(RawInfo {
+983                    length: None,
+984                    payload_length: Some(1),
+985                    data: None,
+986                }),
+987            },
+988            Self::Ping => frame::Ping.to_qlog(),
+989            Self::Ack(f) => QuicFrame::Ack {
+990                ack_delay: Some(f.delay as f32),
+991                acked_ranges: Some(AckedRanges::Double(
+992                    f.iter().map(|range| (range.start, range.end)).collect(),
+993                )),
+994                ect1: f.ecn.as_ref().map(|e| e.ect1),
+995                ect0: f.ecn.as_ref().map(|e| e.ect0),
+996                ce: f.ecn.as_ref().map(|e| e.ce),
+997                raw: None,
+998            },
+999            Self::ResetStream(f) => f.to_qlog(),
+1000            Self::StopSending(f) => f.to_qlog(),
+1001            Self::Crypto(f) => f.to_qlog(),
+1002            Self::NewToken(f) => f.to_qlog(),
+1003            Self::Stream(s) => QuicFrame::Stream {
+1004                stream_id: s.id.into(),
+1005                offset: Some(s.offset),
+1006                fin: Some(s.fin),
+1007                raw: Some(RawInfo {
+1008                    length: Some(s.data.len() as u64),
+1009                    ..Default::default()
+1010                }),
+1011            },
+1012            Self::MaxData(v) => v.to_qlog(),
+1013            Self::MaxStreamData(f) => f.to_qlog(),
+1014            Self::MaxStreams(f) => f.to_qlog(),
+1015            Self::DataBlocked(DataBlocked(offset)) => QuicFrame::DataBlocked {
+1016                limit: *offset,
+1017                raw: None,
+1018            },
+1019            Self::StreamDataBlocked(StreamDataBlocked { id, offset }) => {
+1020                QuicFrame::StreamDataBlocked {
+1021                    stream_id: (*id).into(),
+1022                    limit: *offset,
+1023                    raw: None,
+1024                }
+1025            }
+1026            Self::StreamsBlocked(StreamsBlocked { dir, limit }) => QuicFrame::StreamsBlocked {
+1027                stream_type: (*dir).into(),
+1028                limit: *limit,
+1029                raw: None,
+1030            },
+1031            Self::NewConnectionId(f) => f.to_qlog(),
+1032            Self::RetireConnectionId(f) => f.to_qlog(),
+1033            Self::PathChallenge(f) => f.to_qlog(),
+1034            Self::PathResponse(f) => f.to_qlog(),
+1035            Self::Close(close) => close.to_qlog(),
+1036            Self::Datagram(d) => d.to_qlog(),
+1037            Self::HandshakeDone => frame::HandshakeDone.to_qlog(),
+1038            Self::PathAck(ack) => QuicFrame::PathAck {
+1039                path_id: ack.path_id.as_u32().into(),
+1040                ack_delay: Some(ack.delay as f32),
+1041                ect1: ack.ecn.as_ref().map(|e| e.ect1),
+1042                ect0: ack.ecn.as_ref().map(|e| e.ect0),
+1043                ce: ack.ecn.as_ref().map(|e| e.ce),
+1044                raw: None,
+1045                acked_ranges: Some(AckedRanges::Double(
+1046                    ack.ranges
+1047                        .iter()
+1048                        .map(|range| (range.start, range.end))
+1049                        .collect(),
+1050                )),
+1051            },
+1052            Self::PathAbandon(frame) => frame.to_qlog(),
+1053            Self::PathStatusAvailable(frame) => frame.to_qlog(),
+1054            Self::PathStatusBackup(frame) => frame.to_qlog(),
+1055            Self::PathsBlocked(frame) => frame.to_qlog(),
+1056            Self::PathCidsBlocked(frame) => frame.to_qlog(),
+1057            Self::MaxPathId(f) => f.to_qlog(),
+1058            Self::AckFrequency(f) => f.to_qlog(),
+1059            Self::ImmediateAck => frame::ImmediateAck.to_qlog(),
+1060            Self::ObservedAddr(f) => f.to_qlog(),
+1061            Self::AddAddress(f) => f.to_qlog(),
+1062            Self::ReachOut(f) => f.to_qlog(),
+1063            Self::RemoveAddress(f) => f.to_qlog(),
+1064        }
+1065    }
+1066}
+1067
+1068#[cfg(feature = "qlog")]
+1069impl From<crate::Dir> for StreamType {
+1070    fn from(value: crate::Dir) -> Self {
+1071        match value {
+1072            crate::Dir::Bi => Self::Bidirectional,
+1073            crate::Dir::Uni => Self::Unidirectional,
+1074        }
+1075    }
+1076}
+1077
+1078#[cfg(feature = "qlog")]
+1079fn packet_type(space: SpaceId, is_0rtt: bool) -> PacketType {
+1080    match space {
+1081        SpaceId::Initial => PacketType::Initial,
+1082        SpaceId::Handshake => PacketType::Handshake,
+1083        SpaceId::Data if is_0rtt => PacketType::ZeroRtt,
+1084        SpaceId::Data => PacketType::OneRtt,
+1085    }
+1086}
+1087
+1088#[cfg(feature = "qlog")]
+1089fn stringify_cid(cid: ConnectionId) -> String {
+1090    format!("{cid}")
+1091}
+1092
+1093#[cfg(feature = "qlog")]
+1094fn tuple_endpoint_info(
+1095    ip: Option<IpAddr>,
+1096    port: Option<u16>,
+1097    cid: Option<ConnectionId>,
+1098) -> TupleEndpointInfo {
+1099    let (ip_v4, port_v4, ip_v6, port_v6) = match ip {
+1100        Some(addr) => match addr {
+1101            IpAddr::V4(ipv4_addr) => (Some(ipv4_addr.to_string()), port, None, None),
+1102            IpAddr::V6(ipv6_addr) => (None, None, Some(ipv6_addr.to_string()), port),
+1103        },
+1104        None => (None, None, None, None),
+1105    };
+1106    TupleEndpointInfo {
+1107        ip_v4,
+1108        port_v4,
+1109        ip_v6,
+1110        port_v6,
+1111        connection_ids: cid.map(|cid| vec![cid.to_string()]),
+1112    }
+1113}
+1114
+1115#[cfg(feature = "qlog")]
+1116fn transport_error(code: TransportErrorCode) -> (Option<quic::TransportError>, Option<u64>) {
+1117    let transport_error = match code {
+1118        TransportErrorCode::NO_ERROR => Some(quic::TransportError::NoError),
+1119        TransportErrorCode::INTERNAL_ERROR => Some(quic::TransportError::InternalError),
+1120        TransportErrorCode::CONNECTION_REFUSED => Some(quic::TransportError::ConnectionRefused),
+1121        TransportErrorCode::FLOW_CONTROL_ERROR => Some(quic::TransportError::FlowControlError),
+1122        TransportErrorCode::STREAM_LIMIT_ERROR => Some(quic::TransportError::StreamLimitError),
+1123        TransportErrorCode::STREAM_STATE_ERROR => Some(quic::TransportError::StreamStateError),
+1124        TransportErrorCode::FINAL_SIZE_ERROR => Some(quic::TransportError::FinalSizeError),
+1125        TransportErrorCode::FRAME_ENCODING_ERROR => Some(quic::TransportError::FrameEncodingError),
+1126        TransportErrorCode::TRANSPORT_PARAMETER_ERROR => {
+1127            Some(quic::TransportError::TransportParameterError)
+1128        }
+1129        TransportErrorCode::CONNECTION_ID_LIMIT_ERROR => {
+1130            Some(quic::TransportError::ConnectionIdLimitError)
+1131        }
+1132        TransportErrorCode::PROTOCOL_VIOLATION => Some(quic::TransportError::ProtocolViolation),
+1133        TransportErrorCode::INVALID_TOKEN => Some(quic::TransportError::InvalidToken),
+1134        TransportErrorCode::APPLICATION_ERROR => Some(quic::TransportError::ApplicationError),
+1135        TransportErrorCode::CRYPTO_BUFFER_EXCEEDED => {
+1136            Some(quic::TransportError::CryptoBufferExceeded)
+1137        }
+1138        TransportErrorCode::KEY_UPDATE_ERROR => Some(quic::TransportError::KeyUpdateError),
+1139        TransportErrorCode::AEAD_LIMIT_REACHED => Some(quic::TransportError::AeadLimitReached),
+1140        TransportErrorCode::NO_VIABLE_PATH => Some(quic::TransportError::NoViablePath),
+1141        // multipath
+1142        TransportErrorCode::APPLICATION_ABANDON_PATH => {
+1143            Some(quic::TransportError::ApplicationAbandonPath)
+1144        }
+1145        TransportErrorCode::PATH_RESOURCE_LIMIT_REACHED => {
+1146            Some(quic::TransportError::PathResourceLimitReached)
+1147        }
+1148        TransportErrorCode::PATH_UNSTABLE_OR_POOR => Some(quic::TransportError::PathUnstableOrPoor),
+1149        TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH => {
+1150            Some(quic::TransportError::NoCidsAvailableForPath)
+1151        }
+1152        _ => None,
+1153    };
+1154    let code = match transport_error {
+1155        None => Some(code.into()),
+1156        Some(_) => None,
+1157    };
+1158    (transport_error, code)
+1159}
+1160
+1161#[cfg(feature = "qlog")]
+1162fn fmt_tuple_id(path_id: u64) -> String {
+1163    format!("p{path_id}")
+1164}
+1165
+1166#[cfg(feature = "qlog")]
+1167impl TransportParameters {
+1168    fn to_qlog(self, initiator: TransportInitiator) -> ParametersSet {
+1169        ParametersSet {
+1170            initiator: Some(initiator),
+1171            resumption_allowed: None,
+1172            early_data_enabled: None,
+1173            tls_cipher: None,
+1174            original_destination_connection_id: self
+1175                .original_dst_cid
+1176                .as_ref()
+1177                .map(ToString::to_string),
+1178            initial_source_connection_id: self.initial_src_cid.as_ref().map(ToString::to_string),
+1179            retry_source_connection_id: self.retry_src_cid.as_ref().map(ToString::to_string),
+1180            stateless_reset_token: self.stateless_reset_token.as_ref().map(ToString::to_string),
+1181            disable_active_migration: Some(self.disable_active_migration),
+1182            max_idle_timeout: Some(self.max_idle_timeout.into()),
+1183            max_udp_payload_size: Some(self.max_udp_payload_size.into()),
+1184            ack_delay_exponent: Some(self.ack_delay_exponent.into()),
+1185            max_ack_delay: Some(self.max_ack_delay.into()),
+1186            active_connection_id_limit: Some(self.active_connection_id_limit.into()),
+1187            initial_max_data: Some(self.initial_max_data.into()),
+1188            initial_max_stream_data_bidi_local: Some(
+1189                self.initial_max_stream_data_bidi_local.into(),
+1190            ),
+1191            initial_max_stream_data_bidi_remote: Some(
+1192                self.initial_max_stream_data_bidi_remote.into(),
+1193            ),
+1194            initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
+1195            initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
+1196            initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
+1197            preferred_address: self.preferred_address.as_ref().map(Into::into),
+1198            min_ack_delay: self.min_ack_delay.map(Into::into),
+1199            address_discovery: self.address_discovery_role.to_qlog(),
+1200            initial_max_path_id: self.initial_max_path_id.map(|p| p.as_u32() as u64),
+1201            max_remote_nat_traversal_addresses: self
+1202                .max_remote_nat_traversal_addresses
+1203                .map(|v| u64::from(v.get())),
+1204            max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
+1205            grease_quic_bit: Some(self.grease_quic_bit),
+1206            unknown_parameters: Default::default(),
+1207        }
+1208    }
+1209
+1210    fn to_qlog_restored(self) -> ParametersRestored {
+1211        ParametersRestored {
+1212            disable_active_migration: Some(self.disable_active_migration),
+1213            max_idle_timeout: Some(self.max_idle_timeout.into()),
+1214            max_udp_payload_size: Some(self.max_udp_payload_size.into()),
+1215            active_connection_id_limit: Some(self.active_connection_id_limit.into()),
+1216            initial_max_data: Some(self.initial_max_data.into()),
+1217            initial_max_stream_data_bidi_local: Some(
+1218                self.initial_max_stream_data_bidi_local.into(),
+1219            ),
+1220            initial_max_stream_data_bidi_remote: Some(
+1221                self.initial_max_stream_data_bidi_remote.into(),
+1222            ),
+1223            initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
+1224            initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
+1225            initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
+1226            max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
+1227            grease_quic_bit: Some(self.grease_quic_bit),
+1228        }
+1229    }
+1230}
+1231
+1232#[cfg(feature = "qlog")]
+1233impl From<&crate::transport_parameters::PreferredAddress> for PreferredAddress {
+1234    fn from(value: &crate::transport_parameters::PreferredAddress) -> Self {
+1235        let port_v4 = value.address_v4.map(|addr| addr.port()).unwrap_or_default();
+1236        let port_v6 = value.address_v6.map(|addr| addr.port()).unwrap_or_default();
+1237        let ip_v4 = value
+1238            .address_v4
+1239            .map(|addr| addr.ip().to_string())
+1240            .unwrap_or_default();
+1241        let ip_v6 = value
+1242            .address_v6
+1243            .map(|addr| addr.ip().to_string())
+1244            .unwrap_or_default();
+1245        let connection_id = value.connection_id.to_string();
+1246        let stateless_reset_token = value.stateless_reset_token.to_string();
+1247
+1248        Self {
+1249            ip_v4,
+1250            ip_v6,
+1251            port_v4,
+1252            port_v6,
+1253            connection_id,
+1254            stateless_reset_token,
+1255        }
+1256    }
+1257}
+1258
+1259#[cfg(feature = "qlog")]
+1260impl crate::address_discovery::Role {
+1261    fn to_qlog(self) -> Option<AddressDiscoveryRole> {
+1262        match self {
+1263            Self::SendOnly => Some(AddressDiscoveryRole::SendOnly),
+1264            Self::ReceiveOnly => Some(AddressDiscoveryRole::ReceiveOnly),
+1265            Self::Both => Some(AddressDiscoveryRole::Both),
+1266            Self::Disabled => None,
+1267        }
+1268    }
+1269}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/send_buffer.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/send_buffer.rs.html new file mode 100644 index 000000000..d5f3a5762 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/send_buffer.rs.html @@ -0,0 +1,762 @@ +send_buffer.rs - source

iroh_quinn_proto/connection/
send_buffer.rs

1use std::{collections::VecDeque, ops::Range};
+2
+3use bytes::{Buf, BufMut, Bytes, BytesMut};
+4
+5use crate::{VarInt, connection::streams::BytesOrSlice, range_set::ArrayRangeSet};
+6
+7/// Buffer of outgoing retransmittable stream data
+8#[derive(Default, Debug)]
+9pub(super) struct SendBuffer {
+10    /// Data queued by the application that has to be retained for resends.
+11    ///
+12    /// Only data up to the highest contiguous acknowledged offset can be discarded.
+13    /// We could discard acknowledged in this buffer, but it would require a more
+14    /// complex data structure. Instead, we track acknowledged ranges in `acks`.
+15    ///
+16    /// Data keeps track of the base offset of the buffered data.
+17    data: SendBufferData,
+18    /// The first offset that hasn't been sent even once
+19    ///
+20    /// Always lies in `data.range()`
+21    unsent: u64,
+22    /// Acknowledged ranges which couldn't be discarded yet as they don't include the earliest
+23    /// offset in `unacked`
+24    ///
+25    /// All ranges must be within `data.range().start..(data.range().end - unsent)`, since data
+26    /// that has never been sent can't be acknowledged.
+27    // TODO: Recover storage from these by compacting (#700)
+28    acks: ArrayRangeSet,
+29    /// Previously transmitted ranges deemed lost and marked for retransmission
+30    ///
+31    /// All ranges must be within `data.range().start..(data.range().end - unsent)`, since data
+32    /// that has never been sent can't be retransmitted.
+33    ///
+34    /// This should usually not overlap with `acks`, but this is not strictly enforced.
+35    retransmits: ArrayRangeSet,
+36}
+37
+38/// Maximum number of bytes to combine into a single segment
+39///
+40/// Any segment larger than this will be stored as-is, possibly triggering a flush of the buffer.
+41const MAX_COMBINE: usize = 1452;
+42
+43/// This is where the data of the send buffer lives. It supports appending at the end,
+44/// removing from the front, and retrieving data by range.
+45#[derive(Default, Debug)]
+46struct SendBufferData {
+47    /// Start offset of the buffered data
+48    offset: u64,
+49    /// Total size of [`Self::segments`] and [`Self::last_segment`]
+50    len: usize,
+51    /// Buffered data segments
+52    segments: VecDeque<Bytes>,
+53    /// Last segment, possibly empty
+54    last_segment: BytesMut,
+55}
+56
+57impl SendBufferData {
+58    /// Total size of buffered data
+59    fn len(&self) -> usize {
+60        self.len
+61    }
+62
+63    /// Range of buffered data
+64    #[inline(always)]
+65    fn range(&self) -> Range<u64> {
+66        self.offset..self.offset + self.len as u64
+67    }
+68
+69    /// Append data to the end of the buffer
+70    fn append<'a>(&'a mut self, data: impl BytesOrSlice<'a>) {
+71        self.len += data.len();
+72        if data.len() > MAX_COMBINE {
+73            // use in place
+74            if !self.last_segment.is_empty() {
+75                self.segments.push_back(self.last_segment.split().freeze());
+76            }
+77            self.segments.push_back(data.into_bytes());
+78        } else {
+79            // copy
+80            let rest = if self.last_segment.len() + data.len() > MAX_COMBINE
+81                && !self.last_segment.is_empty()
+82            {
+83                // fill up last_segment up to MAX_COMBINE and flush
+84                let capacity = MAX_COMBINE.saturating_sub(self.last_segment.len());
+85                let (curr, rest) = data.as_ref().split_at(capacity);
+86                self.last_segment.put_slice(curr);
+87                self.segments.push_back(self.last_segment.split().freeze());
+88                rest
+89            } else {
+90                data.as_ref()
+91            };
+92            // copy the rest into the now empty last_segment
+93            self.last_segment.extend_from_slice(rest);
+94        }
+95    }
+96
+97    /// Discard data from the front of the buffer
+98    ///
+99    /// Calling this with n > len() is allowed and will simply clear the buffer.
+100    fn pop_front(&mut self, n: usize) {
+101        let mut n = n.min(self.len);
+102        self.len -= n;
+103        self.offset += n as u64;
+104        while n > 0 {
+105            // segments is empty, which leaves only last_segment
+106            let Some(front) = self.segments.front_mut() else {
+107                break;
+108            };
+109            if front.len() <= n {
+110                // Remove the whole front segment
+111                n -= front.len();
+112                self.segments.pop_front();
+113            } else {
+114                // Advance within the front segment
+115                front.advance(n);
+116                n = 0;
+117            }
+118        }
+119        // the rest has to be in the last segment
+120        self.last_segment.advance(n);
+121        // shrink segments if we have a lot of unused capacity
+122        if self.segments.len() * 4 < self.segments.capacity() {
+123            self.segments.shrink_to_fit();
+124        }
+125    }
+126
+127    /// Iterator over all segments in order
+128    ///
+129    /// Concatenates `segments` and `last_segment` so they can be handled uniformly
+130    fn segments_iter(&self) -> impl Iterator<Item = &[u8]> {
+131        self.segments
+132            .iter()
+133            .map(|x| x.as_ref())
+134            .chain(std::iter::once(self.last_segment.as_ref()))
+135    }
+136
+137    /// Returns data which is associated with a range
+138    ///
+139    /// Requesting a range outside of the buffered data will panic.
+140    #[cfg(any(test, feature = "bench"))]
+141    fn get(&self, offsets: Range<u64>) -> &[u8] {
+142        assert!(
+143            offsets.start >= self.range().start && offsets.end <= self.range().end,
+144            "Requested range is outside of buffered data"
+145        );
+146        // translate to segment-relative offsets and usize
+147        let offsets = Range {
+148            start: (offsets.start - self.offset) as usize,
+149            end: (offsets.end - self.offset) as usize,
+150        };
+151        let mut segment_offset = 0;
+152        for segment in self.segments_iter() {
+153            if offsets.start >= segment_offset && offsets.start < segment_offset + segment.len() {
+154                let start = offsets.start - segment_offset;
+155                let end = offsets.end - segment_offset;
+156
+157                return &segment[start..end.min(segment.len())];
+158            }
+159            segment_offset += segment.len();
+160        }
+161
+162        unreachable!("impossible if segments and range are consistent");
+163    }
+164
+165    fn get_into(&self, offsets: Range<u64>, buf: &mut impl BufMut) {
+166        assert!(
+167            offsets.start >= self.range().start && offsets.end <= self.range().end,
+168            "Requested range is outside of buffered data"
+169        );
+170        // translate to segment-relative offsets and usize
+171        let offsets = Range {
+172            start: (offsets.start - self.offset) as usize,
+173            end: (offsets.end - self.offset) as usize,
+174        };
+175        let mut segment_offset = 0;
+176        for segment in self.segments_iter() {
+177            // intersect segment range with requested range
+178            let start = segment_offset.max(offsets.start);
+179            let end = (segment_offset + segment.len()).min(offsets.end);
+180            if start < end {
+181                // slice range intersects with requested range
+182                buf.put_slice(&segment[start - segment_offset..end - segment_offset]);
+183            }
+184            segment_offset += segment.len();
+185            if segment_offset >= offsets.end {
+186                // we are beyond the requested range
+187                break;
+188            }
+189        }
+190    }
+191
+192    #[cfg(test)]
+193    fn to_vec(&self) -> Vec<u8> {
+194        let mut result = Vec::with_capacity(self.len);
+195        for segment in self.segments_iter() {
+196            result.extend_from_slice(segment);
+197        }
+198        result
+199    }
+200}
+201
+202impl SendBuffer {
+203    /// Construct an empty buffer at the initial offset
+204    pub(super) fn new() -> Self {
+205        Self::default()
+206    }
+207
+208    /// Append application data to the end of the stream
+209    pub(super) fn write<'a>(&'a mut self, data: impl BytesOrSlice<'a>) {
+210        self.data.append(data);
+211    }
+212
+213    /// Discard a range of acknowledged stream data
+214    pub(super) fn ack(&mut self, mut range: Range<u64>) {
+215        // Clamp the range to data which is still tracked
+216        let base_offset = self.fully_acked_offset();
+217        range.start = base_offset.max(range.start);
+218        range.end = base_offset.max(range.end);
+219
+220        self.acks.insert(range);
+221
+222        while self.acks.min() == Some(self.fully_acked_offset()) {
+223            let prefix = self.acks.pop_min().unwrap();
+224            let to_advance = (prefix.end - prefix.start) as usize;
+225            self.data.pop_front(to_advance);
+226        }
+227
+228        // Remove retransmit ranges which have been acknowledged
+229        //
+230        // We have to do this since we have just dropped the data, and asking
+231        // for non-present data would be an error.
+232        self.retransmits.remove(0..self.fully_acked_offset());
+233    }
+234
+235    /// Compute the next range to transmit on this stream and update state to account for that
+236    /// transmission.
+237    ///
+238    /// `max_len` here includes the space which is available to transmit the
+239    /// offset and length of the data to send. The caller has to guarantee that
+240    /// there is at least enough space available to write maximum-sized metadata
+241    /// (8 byte offset + 8 byte length).
+242    ///
+243    /// The method returns a tuple:
+244    /// - The first return value indicates the range of data to send
+245    /// - The second return value indicates whether the length needs to be encoded
+246    ///   in the STREAM frames metadata (`true`), or whether it can be omitted
+247    ///   since the selected range will fill the whole packet.
+248    pub(super) fn poll_transmit(&mut self, mut max_len: usize) -> (Range<u64>, bool) {
+249        debug_assert!(max_len >= 8 + 8);
+250        let mut encode_length = false;
+251
+252        if let Some(range) = self.retransmits.pop_min() {
+253            // Retransmit sent data
+254
+255            // When the offset is known, we know how many bytes are required to encode it.
+256            // Offset 0 requires no space
+257            if range.start != 0 {
+258                max_len -= VarInt::size(unsafe { VarInt::from_u64_unchecked(range.start) });
+259            }
+260            if range.end - range.start < max_len as u64 {
+261                encode_length = true;
+262                max_len -= 8;
+263            }
+264
+265            let end = range.end.min((max_len as u64).saturating_add(range.start));
+266            if end != range.end {
+267                self.retransmits.insert(end..range.end);
+268            }
+269            return (range.start..end, encode_length);
+270        }
+271
+272        // Transmit new data
+273
+274        // When the offset is known, we know how many bytes are required to encode it.
+275        // Offset 0 requires no space
+276        if self.unsent != 0 {
+277            max_len -= VarInt::size(unsafe { VarInt::from_u64_unchecked(self.unsent) });
+278        }
+279        if self.offset() - self.unsent < max_len as u64 {
+280            encode_length = true;
+281            max_len -= 8;
+282        }
+283
+284        let end = self
+285            .offset()
+286            .min((max_len as u64).saturating_add(self.unsent));
+287        let result = self.unsent..end;
+288        self.unsent = end;
+289        (result, encode_length)
+290    }
+291
+292    /// Returns data which is associated with a range
+293    ///
+294    /// This function can return a subset of the range, if the data is stored
+295    /// in noncontiguous fashion in the send buffer. In this case callers
+296    /// should call the function again with an incremented start offset to
+297    /// retrieve more data.
+298    #[cfg(any(test, feature = "bench"))]
+299    pub(super) fn get(&self, offsets: Range<u64>) -> &[u8] {
+300        self.data.get(offsets)
+301    }
+302
+303    pub(super) fn get_into(&self, offsets: Range<u64>, buf: &mut impl BufMut) {
+304        self.data.get_into(offsets, buf)
+305    }
+306
+307    /// Queue a range of sent but unacknowledged data to be retransmitted
+308    pub(super) fn retransmit(&mut self, mut range: Range<u64>) {
+309        debug_assert!(range.end <= self.unsent, "unsent data can't be lost");
+310        // don't allow retransmitting data that has already been fully acknowledged,
+311        // since we don't have it anymore.
+312        //
+313        // Note that we do allow retransmitting data that has been acknowledged
+314        // for simplicity. Not doing so would require clipping the range against
+315        // all acknowledged ranges.
+316        range.start = range.start.max(self.fully_acked_offset());
+317        self.retransmits.insert(range);
+318    }
+319
+320    pub(super) fn retransmit_all_for_0rtt(&mut self) {
+321        // check that we still got all data - we didn't get any acks.
+322        debug_assert_eq!(self.fully_acked_offset(), 0);
+323        self.unsent = 0;
+324    }
+325
+326    /// Offset up to which all data has been acknowledged
+327    fn fully_acked_offset(&self) -> u64 {
+328        self.data.range().start
+329    }
+330
+331    /// First stream offset unwritten by the application, i.e. the offset that the next write will
+332    /// begin at
+333    pub(super) fn offset(&self) -> u64 {
+334        self.data.range().end
+335    }
+336
+337    /// Whether all sent data has been acknowledged
+338    pub(super) fn is_fully_acked(&self) -> bool {
+339        self.data.len() == 0
+340    }
+341
+342    /// Whether there's data to send
+343    ///
+344    /// There may be sent unacknowledged data even when this is false.
+345    pub(super) fn has_unsent_data(&self) -> bool {
+346        self.unsent != self.offset() || !self.retransmits.is_empty()
+347    }
+348
+349    /// Compute the amount of data that hasn't been acknowledged
+350    pub(super) fn unacked(&self) -> u64 {
+351        self.data.len() as u64 - self.acks.iter().map(|x| x.end - x.start).sum::<u64>()
+352    }
+353}
+354
+355#[cfg(test)]
+356mod tests {
+357    use super::*;
+358
+359    #[test]
+360    fn fragment_with_length() {
+361        let mut buf = SendBuffer::new();
+362        const MSG: &[u8] = b"Hello, world!";
+363        buf.write(MSG);
+364        // 0 byte offset => 19 bytes left => 13 byte data isn't enough
+365        // with 8 bytes reserved for length 11 payload bytes will fit
+366        assert_eq!(buf.poll_transmit(19), (0..11, true));
+367        assert_eq!(
+368            buf.poll_transmit(MSG.len() + 16 - 11),
+369            (11..MSG.len() as u64, true)
+370        );
+371        assert_eq!(
+372            buf.poll_transmit(58),
+373            (MSG.len() as u64..MSG.len() as u64, true)
+374        );
+375    }
+376
+377    #[test]
+378    fn fragment_without_length() {
+379        let mut buf = SendBuffer::new();
+380        const MSG: &[u8] = b"Hello, world with some extra data!";
+381        buf.write(MSG);
+382        // 0 byte offset => 19 bytes left => can be filled by 34 bytes payload
+383        assert_eq!(buf.poll_transmit(19), (0..19, false));
+384        assert_eq!(
+385            buf.poll_transmit(MSG.len() - 19 + 1),
+386            (19..MSG.len() as u64, false)
+387        );
+388        assert_eq!(
+389            buf.poll_transmit(58),
+390            (MSG.len() as u64..MSG.len() as u64, true)
+391        );
+392    }
+393
+394    #[test]
+395    fn reserves_encoded_offset() {
+396        let mut buf = SendBuffer::new();
+397
+398        // Pretend we have more than 1 GB of data in the buffer
+399        let chunk: Bytes = Bytes::from_static(&[0; 1024 * 1024]);
+400        for _ in 0..1025 {
+401            buf.write(chunk.clone());
+402        }
+403
+404        const SIZE1: u64 = 64;
+405        const SIZE2: u64 = 16 * 1024;
+406        const SIZE3: u64 = 1024 * 1024 * 1024;
+407
+408        // Offset 0 requires no space
+409        assert_eq!(buf.poll_transmit(16), (0..16, false));
+410        buf.retransmit(0..16);
+411        assert_eq!(buf.poll_transmit(16), (0..16, false));
+412        let mut transmitted = 16u64;
+413
+414        // Offset 16 requires 1 byte
+415        assert_eq!(
+416            buf.poll_transmit((SIZE1 - transmitted + 1) as usize),
+417            (transmitted..SIZE1, false)
+418        );
+419        buf.retransmit(transmitted..SIZE1);
+420        assert_eq!(
+421            buf.poll_transmit((SIZE1 - transmitted + 1) as usize),
+422            (transmitted..SIZE1, false)
+423        );
+424        transmitted = SIZE1;
+425
+426        // Offset 64 requires 2 bytes
+427        assert_eq!(
+428            buf.poll_transmit((SIZE2 - transmitted + 2) as usize),
+429            (transmitted..SIZE2, false)
+430        );
+431        buf.retransmit(transmitted..SIZE2);
+432        assert_eq!(
+433            buf.poll_transmit((SIZE2 - transmitted + 2) as usize),
+434            (transmitted..SIZE2, false)
+435        );
+436        transmitted = SIZE2;
+437
+438        // Offset 16384 requires requires 4 bytes
+439        assert_eq!(
+440            buf.poll_transmit((SIZE3 - transmitted + 4) as usize),
+441            (transmitted..SIZE3, false)
+442        );
+443        buf.retransmit(transmitted..SIZE3);
+444        assert_eq!(
+445            buf.poll_transmit((SIZE3 - transmitted + 4) as usize),
+446            (transmitted..SIZE3, false)
+447        );
+448        transmitted = SIZE3;
+449
+450        // Offset 1GB requires 8 bytes
+451        assert_eq!(
+452            buf.poll_transmit(chunk.len() + 8),
+453            (transmitted..transmitted + chunk.len() as u64, false)
+454        );
+455        buf.retransmit(transmitted..transmitted + chunk.len() as u64);
+456        assert_eq!(
+457            buf.poll_transmit(chunk.len() + 8),
+458            (transmitted..transmitted + chunk.len() as u64, false)
+459        );
+460    }
+461
+462    /// tests that large segments are copied as-is in the SendBuffer
+463    #[test]
+464    fn multiple_large_segments() {
+465        // this must be bigger than MAX_COMBINE so we don't get writes coalesced.
+466        const N: usize = 2000;
+467        const K: u64 = N as u64;
+468        fn dup(data: &[u8]) -> Bytes {
+469            let mut buf = BytesMut::with_capacity(data.len() * N);
+470            for c in data {
+471                for _ in 0..N {
+472                    buf.put_u8(*c);
+473                }
+474            }
+475            buf.freeze()
+476        }
+477
+478        fn same(a: &[u8], b: &[u8]) -> bool {
+479            // surprisingly, eq also checks the fat pointer metadata aka length
+480            std::ptr::eq(a.as_ptr(), b.as_ptr())
+481        }
+482
+483        let mut buf = SendBuffer::new();
+484        let msg: Bytes = dup(b"Hello, world!");
+485        let msg_len: u64 = msg.len() as u64;
+486
+487        let seg1: Bytes = dup(b"He");
+488        buf.write(seg1.clone());
+489        let seg2: Bytes = dup(b"llo,");
+490        buf.write(seg2.clone());
+491        let seg3: Bytes = dup(b" w");
+492        buf.write(seg3.clone());
+493        let seg4: Bytes = dup(b"o");
+494        buf.write(seg4.clone());
+495        let seg5: Bytes = dup(b"rld!");
+496        buf.write(seg5.clone());
+497        assert_eq!(aggregate_unacked(&buf), msg);
+498        // Check that the segments were stored as-is
+499        assert!(same(buf.get(0..5 * K), &seg1));
+500        assert!(same(buf.get(2 * K..8 * K), &seg2));
+501        assert!(same(buf.get(6 * K..8 * K), &seg3));
+502        assert!(same(buf.get(8 * 2000..msg_len), &seg4));
+503        assert!(same(buf.get(9 * 2000..msg_len), &seg5));
+504        // Now drain the segments
+505        buf.ack(0..K);
+506        assert_eq!(aggregate_unacked(&buf), &msg[N..]);
+507        buf.ack(0..3 * K);
+508        assert_eq!(aggregate_unacked(&buf), &msg[3 * N..]);
+509        buf.ack(3 * K..5 * K);
+510        assert_eq!(aggregate_unacked(&buf), &msg[5 * N..]);
+511        // ack with gap, doesn't free anything
+512        buf.ack(7 * K..9 * K);
+513        assert_eq!(aggregate_unacked(&buf), &msg[5 * N..]);
+514        // fill the gap, free up to 9 K
+515        buf.ack(4 * K..7 * K);
+516        assert_eq!(aggregate_unacked(&buf), &msg[9 * N..]);
+517        // ack all
+518        buf.ack(0..msg_len);
+519        assert_eq!(aggregate_unacked(&buf), &[] as &[u8]);
+520    }
+521
+522    #[test]
+523    fn retransmit() {
+524        let mut buf = SendBuffer::new();
+525        const MSG: &[u8] = b"Hello, world with extra data!";
+526        buf.write(MSG);
+527        // Transmit two frames
+528        assert_eq!(buf.poll_transmit(16), (0..16, false));
+529        assert_eq!(buf.poll_transmit(16), (16..23, true));
+530        // Lose the first, but not the second
+531        buf.retransmit(0..16);
+532        // Ensure we only retransmit the lost frame, then continue sending fresh data
+533        assert_eq!(buf.poll_transmit(16), (0..16, false));
+534        assert_eq!(buf.poll_transmit(16), (23..MSG.len() as u64, true));
+535        // Lose the second frame
+536        buf.retransmit(16..23);
+537        assert_eq!(buf.poll_transmit(16), (16..23, true));
+538    }
+539
+540    #[test]
+541    fn ack() {
+542        let mut buf = SendBuffer::new();
+543        const MSG: &[u8] = b"Hello, world!";
+544        buf.write(MSG);
+545        assert_eq!(buf.poll_transmit(16), (0..8, true));
+546        buf.ack(0..8);
+547        assert_eq!(aggregate_unacked(&buf), &MSG[8..]);
+548    }
+549
+550    #[test]
+551    fn reordered_ack() {
+552        let mut buf = SendBuffer::new();
+553        const MSG: &[u8] = b"Hello, world with extra data!";
+554        buf.write(MSG);
+555        assert_eq!(buf.poll_transmit(16), (0..16, false));
+556        assert_eq!(buf.poll_transmit(16), (16..23, true));
+557        buf.ack(16..23);
+558        assert_eq!(aggregate_unacked(&buf), MSG);
+559        buf.ack(0..16);
+560        assert_eq!(aggregate_unacked(&buf), &MSG[23..]);
+561        assert!(buf.acks.is_empty());
+562    }
+563
+564    fn aggregate_unacked(buf: &SendBuffer) -> Vec<u8> {
+565        buf.data.to_vec()
+566    }
+567
+568    #[test]
+569    #[should_panic(expected = "Requested range is outside of buffered data")]
+570    fn send_buffer_get_out_of_range() {
+571        let data = SendBufferData::default();
+572        data.get(0..1);
+573    }
+574
+575    #[test]
+576    #[should_panic(expected = "Requested range is outside of buffered data")]
+577    fn send_buffer_get_into_out_of_range() {
+578        let data = SendBufferData::default();
+579        let mut buf = Vec::new();
+580        data.get_into(0..1, &mut buf);
+581    }
+582}
+583
+584#[cfg(all(test, not(target_family = "wasm")))]
+585mod proptests {
+586    use super::*;
+587
+588    use proptest::prelude::*;
+589    use test_strategy::{Arbitrary, proptest};
+590    use crate::tests::subscribe;
+591    use tracing::trace;
+592
+593    #[derive(Debug, Clone, Arbitrary)]
+594    enum Op {
+595        // write the given bytes
+596        Write(#[strategy(proptest::collection::vec(any::<u8>(), 0..1024))] Vec<u8>),
+597        // ack a random range
+598        Ack(Range<u64>),
+599        // retransmit a random range
+600        Retransmit(Range<u64>),
+601        // poll_transmit with the given max len
+602        PollTransmit(#[strategy(16usize..1024)] usize),
+603    }
+604
+605    /// Map a range into a target range
+606    ///
+607    /// If the target range is empty, it will be returned as is.
+608    /// For a non-empty target range, 0 in the input range will be mapped to
+609    /// the start of the target range, and the input range will wrap around
+610    /// the target range as needed.
+611    fn map_range(input: Range<u64>, target: Range<u64>) -> Range<u64> {
+612        if target.is_empty() {
+613            return target;
+614        }
+615        let size = target.end - target.start;
+616        let a = target.start + (input.start % size);
+617        let b = target.start + (input.end % size);
+618        a.min(b)..a.max(b)
+619    }
+620
+621    #[proptest]
+622    fn send_buffer_matches_reference(
+623        #[strategy(proptest::collection::vec(any::<Op>(), 1..100))] ops: Vec<Op>,
+624    ) {
+625        let _guard = subscribe();
+626        let mut sb = SendBuffer::new();
+627        // all data written to the send buffer
+628        let mut buf = Vec::new();
+629        // max offset that has been returned by poll_transmit
+630        let mut max_send_offset = 0u64;
+631        // max offset up to which data has been fully acked
+632        let mut max_full_send_offset = 0u64;
+633        trace!("");
+634        for op in ops {
+635            match op {
+636                Op::Write(data) => {
+637                    trace!("Op::Write({})", data.len());
+638                    buf.extend_from_slice(&data);
+639                    sb.write(Bytes::from(data));
+640                }
+641                Op::Ack(range) => {
+642                    // we can only get acks for data that has been sent
+643                    let range = map_range(range, 0..max_send_offset);
+644                    // update fully acked range
+645                    if range.contains(&max_full_send_offset) {
+646                        max_full_send_offset = range.end;
+647                    }
+648                    trace!("Op::Ack({:?})", range);
+649                    sb.ack(range);
+650                }
+651                Op::Retransmit(range) => {
+652                    // we can only get retransmits for data that has been sent
+653                    let range = map_range(range, 0..max_send_offset);
+654                    trace!("Op::Retransmit({:?})", range);
+655                    sb.retransmit(range);
+656                }
+657                Op::PollTransmit(max_len) => {
+658                    trace!("Op::PollTransmit({})", max_len);
+659                    let (range, _partial) = sb.poll_transmit(max_len);
+660                    max_send_offset = max_send_offset.max(range.end);
+661                    assert!(
+662                        range.start >= max_full_send_offset,
+663                        "poll_transmit returned already fully acked data: range={:?}, max_full_send_offset={}",
+664                        range,
+665                        max_full_send_offset
+666                    );
+667
+668                    let mut t1 = Vec::new();
+669                    sb.get_into(range.clone(), &mut t1);
+670
+671                    let mut t2 = Vec::new();
+672                    t2.extend_from_slice(&buf[range.start as usize..range.end as usize]);
+673
+674                    assert_eq!(t1, t2, "Data mismatch for range {:?}", range);
+675                }
+676            }
+677        }
+678        // Drain all remaining data
+679        trace!("Op::Retransmit({:?})", 0..max_send_offset);
+680        sb.retransmit(0..max_send_offset);
+681        loop {
+682            trace!("Op::PollTransmit({})", 1024);
+683            let (range, _partial) = sb.poll_transmit(1024);
+684            if range.is_empty() {
+685                break;
+686            }
+687            trace!("Op::Ack({:?})", range);
+688            sb.ack(range);
+689        }
+690        assert!(
+691            sb.is_fully_acked(),
+692            "SendBuffer not fully acked at end of ops"
+693        );
+694    }
+695}
+696
+697#[cfg(feature = "bench")]
+698pub mod send_buffer_benches {
+699    //! Bench fns for SendBuffer
+700    //!
+701    //! These are defined here and re-exported via `bench_exports` in lib.rs,
+702    //! so we can access the private `SendBuffer` struct.
+703    use bytes::Bytes;
+704    use criterion::Criterion;
+705
+706    use super::SendBuffer;
+707
+708    /// Pathological case: many segments, get from end
+709    pub fn get_into_many_segments(criterion: &mut Criterion) {
+710        let mut group = criterion.benchmark_group("get_into_many_segments");
+711        let mut buf = SendBuffer::new();
+712
+713        const SEGMENTS: u64 = 10000;
+714        const SEGMENT_SIZE: u64 = 10;
+715        const PACKET_SIZE: u64 = 1200;
+716        const BYTES: u64 = SEGMENTS * SEGMENT_SIZE;
+717
+718        // 10000 segments of 10 bytes each = 100KB total (same data size)
+719        for i in 0..SEGMENTS {
+720            buf.write(Bytes::from(vec![i as u8; SEGMENT_SIZE as usize]));
+721        }
+722
+723        let mut tgt = Vec::with_capacity(PACKET_SIZE as usize);
+724        group.bench_function("get_into", |b| {
+725            b.iter(|| {
+726                // Get from end (very slow - scans through all 1000 segments)
+727                tgt.clear();
+728                buf.get_into(BYTES - PACKET_SIZE..BYTES, std::hint::black_box(&mut tgt));
+729            });
+730        });
+731    }
+732
+733    /// Get segments in the old way, using a loop of get calls
+734    pub fn get_loop_many_segments(criterion: &mut Criterion) {
+735        let mut group = criterion.benchmark_group("get_loop_many_segments");
+736        let mut buf = SendBuffer::new();
+737
+738        const SEGMENTS: u64 = 10000;
+739        const SEGMENT_SIZE: u64 = 10;
+740        const PACKET_SIZE: u64 = 1200;
+741        const BYTES: u64 = SEGMENTS * SEGMENT_SIZE;
+742
+743        // 10000 segments of 10 bytes each = 100KB total (same data size)
+744        for i in 0..SEGMENTS {
+745            buf.write(Bytes::from(vec![i as u8; SEGMENT_SIZE as usize]));
+746        }
+747
+748        let mut tgt = Vec::with_capacity(PACKET_SIZE as usize);
+749        group.bench_function("get_loop", |b| {
+750            b.iter(|| {
+751                // Get from end (very slow - scans through all 1000 segments)
+752                tgt.clear();
+753                let mut range = BYTES - PACKET_SIZE..BYTES;
+754                while range.start < range.end {
+755                    let slice = std::hint::black_box(buf.get(range.clone()));
+756                    range.start += slice.len() as u64;
+757                    tgt.extend_from_slice(slice);
+758                }
+759            });
+760        });
+761    }
+762}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/spaces.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/spaces.rs.html new file mode 100644 index 000000000..f1cb09d14 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/spaces.rs.html @@ -0,0 +1,1330 @@ +spaces.rs - source

iroh_quinn_proto/connection/
spaces.rs

1use std::{
+2    cmp,
+3    collections::{BTreeMap, BTreeSet, VecDeque},
+4    mem,
+5    net::IpAddr,
+6    ops::{Bound, Index, IndexMut},
+7};
+8
+9use rand::Rng;
+10use rustc_hash::{FxHashMap, FxHashSet};
+11use sorted_index_buffer::SortedIndexBuffer;
+12use tracing::trace;
+13
+14use super::{PathId, assembler::Assembler};
+15use crate::{
+16    Dir, Duration, FourTuple, Instant, StreamId, TransportError, TransportErrorCode, VarInt,
+17    connection::StreamsState,
+18    crypto::Keys,
+19    frame::{self, AddAddress, RemoveAddress},
+20    packet::SpaceId,
+21    range_set::ArrayRangeSet,
+22    shared::IssuedCid,
+23};
+24
+25pub(super) struct PacketSpace {
+26    pub(super) crypto: Option<Keys>,
+27
+28    /// Data to send
+29    pub(super) pending: Retransmits,
+30
+31    /// Incoming cryptographic handshake stream
+32    pub(super) crypto_stream: Assembler,
+33    /// Current offset of outgoing cryptographic handshake stream
+34    pub(super) crypto_offset: u64,
+35
+36    /// Multipath packet number spaces
+37    ///
+38    /// Each [`PathId`] has it's own [`PacketNumberSpace`].  Only the [`SpaceId::Data`] can
+39    /// have multiple packet number spaces, the other spaces only have a number space for
+40    /// `PathId::ZERO`, which is populated at creation.
+41    pub(super) number_spaces: BTreeMap<PathId, PacketNumberSpace>,
+42}
+43
+44impl PacketSpace {
+45    pub(super) fn new(now: Instant, space: SpaceId, rng: &mut (impl Rng + ?Sized)) -> Self {
+46        let number_space_0 = PacketNumberSpace::new(now, space, rng);
+47        Self {
+48            crypto: None,
+49            pending: Retransmits::default(),
+50            crypto_stream: Assembler::new(),
+51            crypto_offset: 0,
+52            number_spaces: BTreeMap::from([(PathId::ZERO, number_space_0)]),
+53        }
+54    }
+55
+56    #[cfg(test)]
+57    pub(super) fn new_deterministic(now: Instant, space: SpaceId) -> Self {
+58        let number_space_0 = PacketNumberSpace::new_deterministic(now, space);
+59        Self {
+60            crypto: None,
+61            pending: Retransmits::default(),
+62            crypto_stream: Assembler::new(),
+63            crypto_offset: 0,
+64            number_spaces: BTreeMap::from([(PathId::ZERO, number_space_0)]),
+65        }
+66    }
+67
+68    /// Returns the [`PacketNumberSpace`] for a path
+69    ///
+70    /// When multipath is disabled use [`PathId::ZERO`].
+71    pub(super) fn path_space(&self, path_id: PathId) -> Option<&PacketNumberSpace> {
+72        self.number_spaces.get(&path_id)
+73    }
+74
+75    /// Returns a mutable reference to the [`PacketNumberSpace`] for a path
+76    ///
+77    /// When multipath is disabled use [`PathId::ZERO`].
+78    pub(super) fn path_space_mut(&mut self, path_id: PathId) -> Option<&mut PacketNumberSpace> {
+79        self.number_spaces.get_mut(&path_id)
+80    }
+81
+82    /// Returns the [`PacketNumberSpace`] for a path
+83    ///
+84    /// When multipath is disabled use `PathId::ZERO`.
+85    // TODO(flub): Note that this only exists as `&mut self` because it creates a new
+86    //    [`PacketNumberSpace`] if one is not yet available for a path.  This forces a few
+87    //    more `&mut` references to users than strictly needed.  An alternative would be to
+88    //    return an Option but that would need to be handled for all callers.  This could be
+89    //    worth exploring once we have all the main multipath bits fitted.
+90    pub(super) fn for_path(&mut self, path: PathId) -> &mut PacketNumberSpace {
+91        self.number_spaces
+92            .get_mut(&path)
+93            .unwrap_or_else(|| panic!("PacketNumberSpace missing for {path}"))
+94    }
+95
+96    pub(super) fn iter_paths_mut(&mut self) -> impl Iterator<Item = &mut PacketNumberSpace> {
+97        self.number_spaces.values_mut()
+98    }
+99
+100    /// Queue data for a tail loss probe (or anti-amplification deadlock prevention) packet
+101    ///
+102    /// Does nothing if no tail loss probe needs to be sent.
+103    ///
+104    /// Probes are sent similarly to normal packets when an expected ACK has not arrived. We never
+105    /// deem a packet lost until we receive an ACK that should have included it, but if a trailing
+106    /// run of packets (or their ACKs) are lost, this might not happen in a timely fashion. We send
+107    /// probe packets to force an ACK, and exempt them from congestion control to prevent a deadlock
+108    /// when the congestion window is filled with lost tail packets.
+109    ///
+110    /// We prefer to send new data, to make the most efficient use of bandwidth. If there's no data
+111    /// waiting to be sent, then we retransmit in-flight data to reduce odds of loss. If there's no
+112    /// in-flight data either, we're probably a client guarding against a handshake
+113    /// anti-amplification deadlock and we just make something up.
+114    pub(super) fn maybe_queue_probe(
+115        &mut self,
+116        path_id: PathId,
+117        request_immediate_ack: bool,
+118        streams: &StreamsState,
+119    ) {
+120        if self.for_path(path_id).loss_probes == 0 {
+121            return;
+122        }
+123
+124        if request_immediate_ack {
+125            // The probe should be ACKed without delay (should only be used in the Data space and
+126            // when the peer supports the acknowledgement frequency extension)
+127            self.for_path(path_id).immediate_ack_pending = true;
+128        }
+129
+130        // Retransmit the data of the oldest in-flight packet
+131        if !self.pending.is_empty(streams) {
+132            // There's real data to send here, no need to make something up
+133            return;
+134        }
+135
+136        // We use retransmits from any path.
+137        for packet in self
+138            .number_spaces
+139            .values_mut()
+140            .flat_map(|s| s.sent_packets.values_mut())
+141        {
+142            if !packet.retransmits.is_empty(streams) {
+143                // Remove retransmitted data from the old packet so we don't end up retransmitting
+144                // it *again* even if the copy we're sending now gets acknowledged.
+145                self.pending |= mem::take(&mut packet.retransmits);
+146                return;
+147            }
+148        }
+149
+150        // Nothing new to send and nothing to retransmit, so fall back on a ping. This should only
+151        // happen in rare cases during the handshake when the server becomes blocked by
+152        // anti-amplification.
+153        if !self.for_path(path_id).immediate_ack_pending {
+154            self.for_path(path_id).ping_pending = true;
+155        }
+156    }
+157
+158    /// Whether there is anything to send in this space
+159    ///
+160    /// For the data space [`Connection::can_send_1rtt`] also needs to be consulted.
+161    ///
+162    /// [`Connection::can_send_1rtt`]: super::Connection::can_send_1rtt
+163    pub(super) fn can_send(&self, path_id: PathId, streams: &StreamsState) -> SendableFrames {
+164        let acks = self
+165            .number_spaces
+166            .values()
+167            .any(|pns| pns.pending_acks.can_send());
+168        let path_exclusive = self
+169            .number_spaces
+170            .get(&path_id)
+171            .is_some_and(|s| s.ping_pending || s.immediate_ack_pending);
+172        let other = !self.pending.is_empty(streams) || path_exclusive;
+173        SendableFrames {
+174            acks,
+175            other,
+176            close: false,
+177            path_exclusive,
+178        }
+179    }
+180
+181    /// The number of packets sent with the current crypto keys
+182    ///
+183    /// Used to know if a key update is needed.
+184    pub(super) fn sent_with_keys(&self) -> u64 {
+185        self.number_spaces.values().map(|s| s.sent_with_keys).sum()
+186    }
+187}
+188
+189impl Index<SpaceId> for [PacketSpace; 3] {
+190    type Output = PacketSpace;
+191    fn index(&self, space: SpaceId) -> &PacketSpace {
+192        &self.as_ref()[space as usize]
+193    }
+194}
+195
+196impl IndexMut<SpaceId> for [PacketSpace; 3] {
+197    fn index_mut(&mut self, space: SpaceId) -> &mut PacketSpace {
+198        &mut self.as_mut()[space as usize]
+199    }
+200}
+201
+202/// The per-path packet number space to support multipath.
+203///
+204/// This contains the data specific to a per-path packet number space.  You should access
+205/// this via [`PacketSpace::for_path`].
+206pub(super) struct PacketNumberSpace {
+207    /// Highest received packet number, if any
+208    pub(super) rx_packet: Option<u64>,
+209    /// The packet number of the next packet that will be sent, if any. In the Data space, the
+210    /// packet number stored here is sometimes skipped by [`PacketNumberFilter`] logic.
+211    pub(super) next_packet_number: u64,
+212    /// The largest packet number the remote peer acknowledged in an ACK frame.
+213    pub(super) largest_acked_packet: Option<u64>,
+214    pub(super) largest_acked_packet_sent: Instant,
+215    /// The highest-numbered ACK-eliciting packet we've sent
+216    pub(super) largest_ack_eliciting_sent: u64,
+217    /// Number of packets in `sent_packets` with numbers above `largest_ack_eliciting_sent`
+218    pub(super) unacked_non_ack_eliciting_tail: u64,
+219    /// Transmitted but not acked
+220    // We use a BTreeMap here so we can efficiently query by range on ACK and for loss detection
+221    pub(super) sent_packets: SortedIndexBuffer<SentPacket>,
+222    /// Packets that were deemed lost
+223    // Older packets are regularly removed in `Connection::drain_lost_packets`.
+224    pub(super) lost_packets: SortedIndexBuffer<LostPacket>,
+225    /// Number of explicit congestion notification codepoints seen on incoming packets
+226    pub(super) ecn_counters: frame::EcnCounts,
+227    /// Recent ECN counters sent by the peer in ACK frames
+228    ///
+229    /// Updated (and inspected) whenever we receive an ACK with a new highest acked packet
+230    /// number. Stored per-space to simplify verification, which would otherwise have difficulty
+231    /// distinguishing between ECN bleaching and counts having been updated by a near-simultaneous
+232    /// ACK already processed in another space.
+233    pub(super) ecn_feedback: frame::EcnCounts,
+234    /// Number of packets sent in the current key phase
+235    pub(super) sent_with_keys: u64,
+236    /// A PING frame needs to be sent on this path
+237    pub(super) ping_pending: bool,
+238    /// An IMMEDIATE_ACK (draft-ietf-quic-ack-frequency) frame needs to be sent on this path
+239    pub(super) immediate_ack_pending: bool,
+240    /// Packet deduplicator
+241    pub(super) dedup: Dedup,
+242    /// Packet numbers to acknowledge
+243    pub(super) pending_acks: PendingAcks,
+244
+245    //
+246    // Loss Detection
+247    //
+248    /// The time the most recently sent retransmittable packet was sent.
+249    pub(super) time_of_last_ack_eliciting_packet: Option<Instant>,
+250    /// Earliest time when we might declare a packet lost.
+251    ///
+252    /// The time at which the earliest sent packet in this space will be considered lost
+253    /// based on exceeding the reordering window in time. Only set for packets numbered
+254    /// prior to a packet that has been acknowledged.
+255    pub(super) loss_time: Option<Instant>,
+256    /// Number of tail loss probes to send
+257    pub(super) loss_probes: u32,
+258
+259    /// Packet numbers to skip, only used in the data package space.
+260    pn_filter: Option<PacketNumberFilter>,
+261}
+262
+263impl PacketNumberSpace {
+264    pub(super) fn new(now: Instant, space: SpaceId, rng: &mut (impl Rng + ?Sized)) -> Self {
+265        let pn_filter = match space {
+266            SpaceId::Initial | SpaceId::Handshake => None,
+267            SpaceId::Data => Some(PacketNumberFilter::new(rng)),
+268        };
+269        Self {
+270            rx_packet: None,
+271            next_packet_number: 0,
+272            largest_acked_packet: None,
+273            largest_acked_packet_sent: now,
+274            largest_ack_eliciting_sent: 0,
+275            unacked_non_ack_eliciting_tail: 0,
+276            sent_packets: SortedIndexBuffer::new(),
+277            lost_packets: SortedIndexBuffer::new(),
+278            ecn_counters: frame::EcnCounts::ZERO,
+279            ecn_feedback: frame::EcnCounts::ZERO,
+280            sent_with_keys: 0,
+281            ping_pending: false,
+282            immediate_ack_pending: false,
+283            dedup: Default::default(),
+284            pending_acks: PendingAcks::new(),
+285            time_of_last_ack_eliciting_packet: None,
+286            loss_time: None,
+287            loss_probes: 0,
+288            pn_filter,
+289        }
+290    }
+291
+292    #[cfg(test)]
+293    fn new_deterministic(now: Instant, space: SpaceId) -> Self {
+294        let pn_filter = match space {
+295            SpaceId::Initial | SpaceId::Handshake => None,
+296            SpaceId::Data => Some(PacketNumberFilter::disabled()),
+297        };
+298        Self {
+299            rx_packet: None,
+300            next_packet_number: 0,
+301            largest_acked_packet: None,
+302            largest_acked_packet_sent: now,
+303            largest_ack_eliciting_sent: 0,
+304            unacked_non_ack_eliciting_tail: 0,
+305            sent_packets: SortedIndexBuffer::new(),
+306            lost_packets: SortedIndexBuffer::new(),
+307            ecn_counters: frame::EcnCounts::ZERO,
+308            ecn_feedback: frame::EcnCounts::ZERO,
+309            sent_with_keys: 0,
+310            ping_pending: false,
+311            immediate_ack_pending: false,
+312            dedup: Default::default(),
+313            pending_acks: PendingAcks::new(),
+314            time_of_last_ack_eliciting_packet: None,
+315            loss_time: None,
+316            loss_probes: 0,
+317            pn_filter,
+318        }
+319    }
+320
+321    /// Get the next outgoing packet number in this space
+322    ///
+323    /// In the Data space, the connection's [`PacketNumberFilter`] must be used rather than calling
+324    /// this directly.
+325    pub(super) fn get_tx_number(&mut self, rng: &mut (impl Rng + ?Sized)) -> u64 {
+326        // TODO: Handle packet number overflow gracefully
+327        assert!(self.next_packet_number < 2u64.pow(62));
+328        let mut pn = self.next_packet_number;
+329        self.next_packet_number += 1;
+330        self.sent_with_keys += 1;
+331
+332        // Skip this number if the filter says so, only enabled in the data space
+333        if let Some(ref mut filter) = self.pn_filter
+334            && filter.skip_pn(pn, rng)
+335        {
+336            pn = self.next_packet_number;
+337            self.next_packet_number += 1;
+338            self.sent_with_keys += 1;
+339        }
+340        pn
+341    }
+342
+343    pub(super) fn peek_tx_number(&mut self) -> u64 {
+344        let pn = self.next_packet_number;
+345        if let Some(ref filter) = self.pn_filter
+346            && pn == filter.next_skipped_packet_number
+347        {
+348            return pn + 1;
+349        }
+350        pn
+351    }
+352
+353    /// Checks whether a skipped packet number was ACKed.
+354    pub(super) fn check_ack(&self, range: std::ops::Range<u64>) -> Result<(), TransportError> {
+355        if let Some(ref filter) = self.pn_filter
+356            && filter
+357                .prev_skipped_packet_number
+358                .is_some_and(|pn| range.contains(&pn))
+359        {
+360            return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked"));
+361        }
+362        Ok(())
+363    }
+364
+365    /// Verifies sanity of an ECN block and returns whether congestion was encountered.
+366    pub(super) fn detect_ecn(
+367        &mut self,
+368        newly_acked: u64,
+369        ecn: frame::EcnCounts,
+370    ) -> Result<bool, &'static str> {
+371        let ect0_increase = ecn
+372            .ect0
+373            .checked_sub(self.ecn_feedback.ect0)
+374            .ok_or("peer ECT(0) count regression")?;
+375        let ect1_increase = ecn
+376            .ect1
+377            .checked_sub(self.ecn_feedback.ect1)
+378            .ok_or("peer ECT(1) count regression")?;
+379        let ce_increase = ecn
+380            .ce
+381            .checked_sub(self.ecn_feedback.ce)
+382            .ok_or("peer CE count regression")?;
+383        let total_increase = ect0_increase + ect1_increase + ce_increase;
+384        if total_increase < newly_acked {
+385            return Err("ECN bleaching");
+386        }
+387        if (ect0_increase + ce_increase) < newly_acked || ect1_increase != 0 {
+388            return Err("ECN corruption");
+389        }
+390        // If total_increase > newly_acked (which happens when ACKs are lost), this is required by
+391        // the draft so that long-term drift does not occur. If =, then the only question is whether
+392        // to count CE packets as CE or ECT0. Recording them as CE is more consistent and keeps the
+393        // congestion check obvious.
+394        self.ecn_feedback = ecn;
+395        Ok(ce_increase != 0)
+396    }
+397
+398    /// Stop tracking sent packet `number`, and return what we knew about it
+399    pub(super) fn take(&mut self, number: u64) -> Option<SentPacket> {
+400        let packet = self.sent_packets.remove(number)?;
+401        if !packet.ack_eliciting && number > self.largest_ack_eliciting_sent {
+402            self.unacked_non_ack_eliciting_tail =
+403                self.unacked_non_ack_eliciting_tail.checked_sub(1).unwrap();
+404        }
+405        Some(packet)
+406    }
+407
+408    /// May return a packet that should be forgotten
+409    pub(super) fn sent(&mut self, number: u64, packet: SentPacket) -> Option<SentPacket> {
+410        // Retain state for at most this many non-ACK-eliciting packets sent after the most recently
+411        // sent ACK-eliciting packet. We're never guaranteed to receive an ACK for those, and we
+412        // can't judge them as lost without an ACK, so to limit memory in applications which receive
+413        // packets but don't send ACK-eliciting data for long periods use we must eventually start
+414        // forgetting about them, although it might also be reasonable to just kill the connection
+415        // due to weird peer behavior.
+416        const MAX_UNACKED_NON_ACK_ELICTING_TAIL: u64 = 1_000;
+417
+418        let mut forgotten = None;
+419        if packet.ack_eliciting {
+420            self.unacked_non_ack_eliciting_tail = 0;
+421            self.largest_ack_eliciting_sent = number;
+422        } else if self.unacked_non_ack_eliciting_tail > MAX_UNACKED_NON_ACK_ELICTING_TAIL {
+423            let oldest_after_ack_eliciting = self
+424                .sent_packets
+425                .keys_range((
+426                    Bound::Excluded(self.largest_ack_eliciting_sent),
+427                    Bound::Unbounded,
+428                ))
+429                .next()
+430                .unwrap();
+431            // Per https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types,
+432            // non-ACK-eliciting packets must only contain PADDING, ACK, and CONNECTION_CLOSE
+433            // frames, which require no special handling on ACK or loss beyond removal from
+434            // in-flight counters if padded.
+435            let packet = self
+436                .sent_packets
+437                .remove(oldest_after_ack_eliciting)
+438                .unwrap();
+439            debug_assert!(!packet.ack_eliciting);
+440            forgotten = Some(packet);
+441        } else {
+442            self.unacked_non_ack_eliciting_tail += 1;
+443        }
+444
+445        self.sent_packets.insert(number, packet);
+446        forgotten
+447    }
+448
+449    /// Whether any congestion-controlled packets in this space are not yet acknowledged or lost
+450    pub(super) fn has_in_flight(&self) -> bool {
+451        // The number of non-congestion-controlled (i.e. size == 0) packets in flight at a time
+452        // should be small, since otherwise congestion control wouldn't be effective. Therefore,
+453        // this shouldn't need to visit many packets before finishing one way or another.
+454        self.sent_packets.values().any(|x| x.size != 0)
+455    }
+456}
+457
+458/// Represents one or more packets subject to retransmission
+459#[derive(Debug, Clone)]
+460pub(super) struct SentPacket {
+461    /// [`PathData::generation`](super::PathData::generation) of the path on which this packet was sent
+462    pub(super) path_generation: u64,
+463    /// The time the packet was sent.
+464    pub(super) time_sent: Instant,
+465    /// The number of bytes sent in the packet, not including UDP or IP overhead, but including QUIC
+466    /// framing overhead. Zero if this packet is not counted towards congestion control, i.e. not an
+467    /// "in flight" packet.
+468    pub(super) size: u16,
+469    /// Whether an acknowledgement is expected directly in response to this packet.
+470    pub(super) ack_eliciting: bool,
+471    /// The largest packet number acknowledged by this packet
+472    pub(super) largest_acked: FxHashMap<PathId, u64>,
+473    /// Data which needs to be retransmitted in case the packet is lost.
+474    /// The data is boxed to minimize `SentPacket` size for the typical case of
+475    /// packets only containing ACKs and STREAM frames.
+476    pub(super) retransmits: ThinRetransmits,
+477    /// Metadata for stream frames in a packet
+478    ///
+479    /// The actual application data is stored with the stream state.
+480    pub(super) stream_frames: frame::StreamMetaVec,
+481}
+482
+483/// Represents one or more packets that are deemed lost.
+484#[derive(Debug)]
+485pub(super) struct LostPacket {
+486    /// The time the packet was sent.
+487    pub(super) time_sent: Instant,
+488}
+489
+490/// Retransmittable data queue
+491#[allow(unreachable_pub)] // fuzzing only
+492#[derive(Debug, Default, Clone)]
+493pub struct Retransmits {
+494    pub(super) max_data: bool,
+495    pub(super) max_stream_id: [bool; 2],
+496    pub(super) reset_stream: Vec<(StreamId, VarInt)>,
+497    pub(super) stop_sending: Vec<frame::StopSending>,
+498    pub(super) max_stream_data: FxHashSet<StreamId>,
+499    pub(super) crypto: VecDeque<frame::Crypto>,
+500    pub(super) new_cids: Vec<IssuedCid>,
+501    pub(super) retire_cids: Vec<(PathId, u64)>,
+502    pub(super) ack_frequency: bool,
+503    pub(super) handshake_done: bool,
+504    pub(super) observed_addr: bool,
+505    /// Whether we should inform the peer we will allow higher [`PathId`]s.
+506    pub(super) max_path_id: bool,
+507    /// Whether we should inform the peer that their max [`PathId`] is blocking our attempt to open
+508    /// new paths.
+509    // TODO(@divma): we need logic to prevent sending this more than once after being ack-d once
+510    pub(super) paths_blocked: bool,
+511    /// For each enqueued NEW_TOKEN frame, a copy of the path's remote address
+512    ///
+513    /// There are 2 reasons this is unusual:
+514    ///
+515    /// - If the path changes, NEW_TOKEN frames bound for the old path are not retransmitted on the
+516    ///   new path. That is why this field stores the remote address: so that ones for old paths
+517    ///   can be filtered out.
+518    /// - If a token is lost, a new randomly generated token is re-transmitted, rather than the
+519    ///   original. This is so that if both transmissions are received, the client won't risk
+520    ///   sending the same token twice. That is why this field does _not_ store any actual token.
+521    ///
+522    /// It is true that a QUIC endpoint will only want to effectively have NEW_TOKEN frames
+523    /// enqueued for its current path at a given point in time. Based on that, we could conceivably
+524    /// change this from a vector to an `Option<(FourTuple, usize)>` or just a `usize` or
+525    /// something. However, due to the architecture of Quinn, it is considerably simpler to not do
+526    /// that; consider what such a change would mean for implementing `BitOrAssign` on Self.
+527    pub(super) new_tokens: Vec<FourTuple>,
+528    /// Paths which need to be abandoned
+529    pub(super) path_abandon: BTreeMap<PathId, TransportErrorCode>,
+530    /// If a [`frame::PathStatusAvailable`] and [`frame::PathStatusBackup`] need to be sent for a path
+531    pub(super) path_status: BTreeSet<PathId>,
+532    /// If a PATH_CIDS_BLOCKED frame needs to be sent for a path
+533    pub(super) path_cids_blocked: BTreeSet<PathId>,
+534
+535    // Nat traversal data
+536    /// Addresses to report in `ADD_ADDRESS` frames
+537    pub(super) add_address: BTreeSet<AddAddress>,
+538    /// Address IDs to remove in `REMOVE_ADDRESS` frames
+539    pub(super) remove_address: BTreeSet<RemoveAddress>,
+540    /// Round and local addresses to advertise in `REACH_OUT` frames
+541    pub(super) reach_out: Option<(VarInt, Vec<(IpAddr, u16)>)>,
+542}
+543
+544impl Retransmits {
+545    pub(super) fn is_empty(&self, streams: &StreamsState) -> bool {
+546        !self.max_data
+547            && !self.max_stream_id.into_iter().any(|x| x)
+548            && self.reset_stream.is_empty()
+549            && self.stop_sending.is_empty()
+550            && self
+551                .max_stream_data
+552                .iter()
+553                .all(|&id| !streams.can_send_flow_control(id))
+554            && self.crypto.is_empty()
+555            && self.new_cids.is_empty()
+556            && self.retire_cids.is_empty()
+557            && !self.ack_frequency
+558            && !self.handshake_done
+559            && !self.observed_addr
+560            && self.new_tokens.is_empty()
+561            && self.path_abandon.is_empty()
+562            && self.path_status.is_empty()
+563            && !self.max_path_id
+564            && !self.paths_blocked
+565            && self.add_address.is_empty()
+566            && self.remove_address.is_empty()
+567            && self.reach_out.is_none()
+568    }
+569}
+570
+571impl ::std::ops::BitOrAssign for Retransmits {
+572    fn bitor_assign(&mut self, mut rhs: Self) {
+573        // We reduce in-stream head-of-line blocking by queueing retransmits before other data for
+574        // STREAM and CRYPTO frames.
+575        self.max_data |= rhs.max_data;
+576        for dir in Dir::iter() {
+577            self.max_stream_id[dir as usize] |= rhs.max_stream_id[dir as usize];
+578        }
+579        self.reset_stream.extend_from_slice(&rhs.reset_stream);
+580        self.stop_sending.extend_from_slice(&rhs.stop_sending);
+581        self.max_stream_data.extend(&rhs.max_stream_data);
+582        for crypto in rhs.crypto.into_iter().rev() {
+583            self.crypto.push_front(crypto);
+584        }
+585        self.new_cids.extend(&rhs.new_cids);
+586        self.retire_cids.extend(rhs.retire_cids);
+587        self.ack_frequency |= rhs.ack_frequency;
+588        self.handshake_done |= rhs.handshake_done;
+589        self.observed_addr |= rhs.observed_addr;
+590        self.new_tokens.extend_from_slice(&rhs.new_tokens);
+591        self.path_abandon.append(&mut rhs.path_abandon);
+592        self.max_path_id |= rhs.max_path_id;
+593        self.paths_blocked |= rhs.paths_blocked;
+594        self.add_address.extend(rhs.add_address.iter().copied());
+595        self.remove_address
+596            .extend(rhs.remove_address.iter().copied());
+597        // if there are two rounds, prefer the most recent reach out set
+598        let lhs_round = self.reach_out.as_ref().map(|(round, _)| *round);
+599        let rhs_round = rhs.reach_out.as_ref().map(|(round, _)| *round);
+600        match (lhs_round, rhs_round) {
+601            (None, Some(_)) => self.reach_out = rhs.reach_out.clone(),
+602            (Some(lhs_round), Some(rhs_round)) if rhs_round > lhs_round => {
+603                self.reach_out = rhs.reach_out.clone()
+604            }
+605            _ => {}
+606        }
+607    }
+608}
+609
+610impl ::std::ops::BitOrAssign<ThinRetransmits> for Retransmits {
+611    fn bitor_assign(&mut self, rhs: ThinRetransmits) {
+612        if let Some(retransmits) = rhs.retransmits {
+613            self.bitor_assign(*retransmits)
+614        }
+615    }
+616}
+617
+618impl ::std::iter::FromIterator<Self> for Retransmits {
+619    fn from_iter<T>(iter: T) -> Self
+620    where
+621        T: IntoIterator<Item = Self>,
+622    {
+623        let mut result = Self::default();
+624        for packet in iter {
+625            result |= packet;
+626        }
+627        result
+628    }
+629}
+630
+631/// A variant of `Retransmits` which only allocates storage when required
+632#[derive(Debug, Default, Clone)]
+633pub(super) struct ThinRetransmits {
+634    retransmits: Option<Box<Retransmits>>,
+635}
+636
+637impl ThinRetransmits {
+638    /// Returns `true` if no retransmits are necessary
+639    pub(super) fn is_empty(&self, streams: &StreamsState) -> bool {
+640        match &self.retransmits {
+641            Some(retransmits) => retransmits.is_empty(streams),
+642            None => true,
+643        }
+644    }
+645
+646    /// Returns a reference to the retransmits stored in this box
+647    pub(super) fn get(&self) -> Option<&Retransmits> {
+648        self.retransmits.as_deref()
+649    }
+650
+651    /// Returns a mutable reference to the retransmits stored in this box
+652    pub(super) fn get_mut(&mut self) -> Option<&mut Retransmits> {
+653        self.retransmits.as_deref_mut()
+654    }
+655
+656    /// Returns a mutable reference to the stored retransmits
+657    ///
+658    /// This function will allocate a backing storage if required.
+659    pub(super) fn get_or_create(&mut self) -> &mut Retransmits {
+660        if self.retransmits.is_none() {
+661            self.retransmits = Some(Box::default());
+662        }
+663        self.retransmits.as_deref_mut().unwrap()
+664    }
+665}
+666
+667/// RFC4303-style sliding window packet number deduplicator.
+668///
+669/// A contiguous bitfield, where each bit corresponds to a packet number and the rightmost bit is
+670/// always set. A set bit represents a packet that has been successfully authenticated. Bits left of
+671/// the window are assumed to be set.
+672///
+673/// ```text
+674/// ...xxxxxxxxx 1 0
+675///     ^        ^ ^
+676/// window highest next
+677/// ```
+678#[derive(Debug, Default)]
+679pub(super) struct Dedup {
+680    window: Window,
+681    /// Lowest packet number higher than all yet authenticated.
+682    next: u64,
+683}
+684
+685/// Inner bitfield type.
+686///
+687/// Because QUIC never reuses packet numbers, this only needs to be large enough to deal with
+688/// packets that are reordered but still delivered in a timely manner.
+689type Window = u128;
+690
+691/// Number of packets tracked by `Dedup`.
+692const WINDOW_SIZE: u64 = 1 + mem::size_of::<Window>() as u64 * 8;
+693
+694impl Dedup {
+695    /// Construct an empty window positioned at the start.
+696    #[cfg(test)]
+697    pub(super) fn new() -> Self {
+698        Self { window: 0, next: 0 }
+699    }
+700
+701    /// Highest packet number authenticated.
+702    fn highest(&self) -> u64 {
+703        self.next - 1
+704    }
+705
+706    /// Record a newly authenticated packet number.
+707    ///
+708    /// Returns whether the packet might be a duplicate.
+709    pub(super) fn insert(&mut self, packet: u64) -> bool {
+710        if let Some(diff) = packet.checked_sub(self.next) {
+711            // Right of window
+712            self.window = ((self.window << 1) | 1)
+713                .checked_shl(cmp::min(diff, u64::from(u32::MAX)) as u32)
+714                .unwrap_or(0);
+715            self.next = packet + 1;
+716            false
+717        } else if self.highest() - packet < WINDOW_SIZE {
+718            // Within window
+719            if let Some(bit) = (self.highest() - packet).checked_sub(1) {
+720                // < highest
+721                let mask = 1 << bit;
+722                let duplicate = self.window & mask != 0;
+723                self.window |= mask;
+724                duplicate
+725            } else {
+726                // == highest
+727                true
+728            }
+729        } else {
+730            // Left of window
+731            true
+732        }
+733    }
+734
+735    /// Returns the packet number of the smallest packet missing between the provided interval
+736    ///
+737    /// If there are no missing packets, returns `None`
+738    fn smallest_missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> Option<u64> {
+739        debug_assert!(lower_bound <= upper_bound);
+740        debug_assert!(upper_bound <= self.highest());
+741        const BITFIELD_SIZE: u64 = (mem::size_of::<Window>() * 8) as u64;
+742
+743        // Since we already know the packets at the boundaries have been received, we only need to
+744        // check those in between them (this removes the necessity of extra logic to deal with the
+745        // highest packet, which is stored outside the bitfield)
+746        let lower_bound = lower_bound + 1;
+747        let upper_bound = upper_bound.saturating_sub(1);
+748
+749        // Note: the offsets are counted from the right
+750        // The highest packet is not included in the bitfield, so we subtract 1 to account for that
+751        let start_offset = (self.highest() - upper_bound).max(1) - 1;
+752        if start_offset >= BITFIELD_SIZE {
+753            // The start offset is outside of the window. All packets outside of the window are
+754            // considered to be received.
+755            return None;
+756        }
+757
+758        let end_offset_exclusive = self.highest().saturating_sub(lower_bound);
+759
+760        // The range is clamped at the edge of the window, because any earlier packets are
+761        // considered to be received
+762        let range_len = end_offset_exclusive
+763            .saturating_sub(start_offset)
+764            .min(BITFIELD_SIZE);
+765        if range_len == 0 {
+766            return None;
+767        }
+768
+769        // Ensure the shift is within bounds (we already know start_offset < BITFIELD_SIZE,
+770        // because of the early return)
+771        let mask = if range_len == BITFIELD_SIZE {
+772            u128::MAX
+773        } else {
+774            ((1u128 << range_len) - 1) << start_offset
+775        };
+776        let gaps = !self.window & mask;
+777
+778        let smallest_missing_offset = 128 - gaps.leading_zeros() as u64;
+779        let smallest_missing_packet = self.highest() - smallest_missing_offset;
+780
+781        if smallest_missing_packet <= upper_bound {
+782            Some(smallest_missing_packet)
+783        } else {
+784            None
+785        }
+786    }
+787
+788    /// Returns true if there are any missing packets between the provided interval
+789    ///
+790    /// The provided packet numbers must have been received before calling this function
+791    fn missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> bool {
+792        self.smallest_missing_in_interval(lower_bound, upper_bound)
+793            .is_some()
+794    }
+795}
+796
+797/// Indicates which data is available for sending
+798#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+799pub(super) struct SendableFrames {
+800    /// Whether there ACK frames to send, these are not ack-eliciting
+801    pub(super) acks: bool,
+802    /// Whether there are any other frames to send, these are ack-eliciting
+803    pub(super) other: bool,
+804    /// Whether there is a CONNECTION_CLOSE to send, this is not ack-eliciting
+805    pub(super) close: bool,
+806    /// Whether there are frames to send, which can only be sent on the path queried
+807    ///
+808    /// These are ack-eliciting, and a subset of [`SendableFrames::other`].  This is useful
+809    /// for QUIC-MULTIPATH, which may desire not to send any frames on a backup path, which
+810    /// can also be sent on an active path.
+811    pub(super) path_exclusive: bool,
+812}
+813
+814impl SendableFrames {
+815    /// Returns that no data is available for sending
+816    pub(super) fn empty() -> Self {
+817        Self {
+818            acks: false,
+819            other: false,
+820            close: false,
+821            path_exclusive: false,
+822        }
+823    }
+824
+825    /// Whether no data is sendable
+826    pub(super) fn is_empty(&self) -> bool {
+827        !self.acks && !self.other && !self.close && !self.path_exclusive
+828    }
+829}
+830
+831impl ::std::ops::BitOrAssign for SendableFrames {
+832    fn bitor_assign(&mut self, rhs: Self) {
+833        self.acks |= rhs.acks;
+834        self.other |= rhs.other;
+835        self.close |= rhs.close;
+836        self.path_exclusive |= rhs.path_exclusive;
+837    }
+838}
+839
+840#[derive(Debug)]
+841pub(super) struct PendingAcks {
+842    /// Whether we should send an ACK immediately, even if that means sending an ACK-only packet
+843    ///
+844    /// When `immediate_ack_required` is false, the normal behavior is to send ACK frames only when
+845    /// there is other data to send, or when the `MaxAckDelay` timer expires.
+846    immediate_ack_required: bool,
+847    /// The number of ack-eliciting packets received since the last ACK frame was sent
+848    ///
+849    /// Once the count _exceeds_ `ack_eliciting_threshold`, an immediate ACK is required
+850    ack_eliciting_since_last_ack_sent: u64,
+851    non_ack_eliciting_since_last_ack_sent: u64,
+852    ack_eliciting_threshold: u64,
+853    /// The reordering threshold, controlling how we respond to out-of-order ack-eliciting packets
+854    ///
+855    /// Different values enable different behavior:
+856    ///
+857    /// * `0`: no special action is taken
+858    /// * `1`: an ACK is immediately sent if it is out-of-order according to RFC 9000
+859    /// * `>1`: an ACK is immediately sent if it is out-of-order according to the ACK frequency draft
+860    reordering_threshold: u64,
+861    /// The earliest ack-eliciting packet since the last ACK was sent, used to calculate the moment
+862    /// upon which `max_ack_delay` elapses
+863    earliest_ack_eliciting_since_last_ack_sent: Option<Instant>,
+864    /// Packet number ranges for which to still send acknowledgements.
+865    ///
+866    /// These are packet number ranges of ack-eliciting packets the peer has sent and which
+867    /// need to be acknowledged.  Packet numbers are only removed from here once the peer has
+868    /// acknowledged the ACKs for them.
+869    ranges: ArrayRangeSet,
+870    /// The largest packet number received and the time it was received
+871    ///
+872    /// Used to calculate ACK delay in [`PendingAcks::ack_delay`].
+873    largest_packet: Option<(u64, Instant)>,
+874    /// The ack-eliciting packet we have received with the largest packet number
+875    largest_ack_eliciting_packet: Option<u64>,
+876    /// The largest acknowledged packet number sent in an ACK frame
+877    largest_acked: Option<u64>,
+878}
+879
+880impl PendingAcks {
+881    fn new() -> Self {
+882        Self {
+883            immediate_ack_required: false,
+884            ack_eliciting_since_last_ack_sent: 0,
+885            non_ack_eliciting_since_last_ack_sent: 0,
+886            ack_eliciting_threshold: 1,
+887            reordering_threshold: 1,
+888            earliest_ack_eliciting_since_last_ack_sent: None,
+889            ranges: Default::default(),
+890            largest_packet: Default::default(),
+891            largest_ack_eliciting_packet: Default::default(),
+892            largest_acked: Default::default(),
+893        }
+894    }
+895
+896    pub(super) fn set_ack_frequency_params(&mut self, frame: &frame::AckFrequency) {
+897        self.ack_eliciting_threshold = frame.ack_eliciting_threshold.into_inner();
+898        self.reordering_threshold = frame.reordering_threshold.into_inner();
+899    }
+900
+901    pub(super) fn set_immediate_ack_required(&mut self) {
+902        self.immediate_ack_required = true;
+903    }
+904
+905    pub(super) fn on_max_ack_delay_timeout(&mut self) {
+906        self.immediate_ack_required = self.ack_eliciting_since_last_ack_sent > 0;
+907    }
+908
+909    pub(super) fn max_ack_delay_timeout(&self, max_ack_delay: Duration) -> Option<Instant> {
+910        self.earliest_ack_eliciting_since_last_ack_sent
+911            .map(|earliest_unacked| earliest_unacked + max_ack_delay)
+912    }
+913
+914    /// Whether any ACK frames SHOULD be sent
+915    ///
+916    /// This is used in the top-level [`Connection::space_can_send`], so determines if a
+917    /// packet will be built. It is often possible to construct new ACK ranges to send
+918    /// before this returns `true`. This results in more ACK frames being sent, and
+919    /// processing those at the receiver costs CPU for very little improvements.
+920    ///
+921    /// [`Connection::space_can_send`]: super::Connection::space_can_send
+922    pub(super) fn can_send(&self) -> bool {
+923        self.immediate_ack_required && !self.ranges.is_empty()
+924    }
+925
+926    /// Returns the delay since the packet with the largest packet number was received
+927    pub(super) fn ack_delay(&self, now: Instant) -> Duration {
+928        self.largest_packet
+929            .map_or_else(Duration::default, |(_, received)| now - received)
+930    }
+931
+932    /// Handle receipt of a new packet
+933    ///
+934    /// Returns true if the max ack delay timer should be armed
+935    pub(super) fn packet_received(
+936        &mut self,
+937        now: Instant,
+938        packet_number: u64,
+939        ack_eliciting: bool,
+940        dedup: &Dedup,
+941    ) -> bool {
+942        if !ack_eliciting {
+943            self.non_ack_eliciting_since_last_ack_sent += 1;
+944            return false;
+945        }
+946
+947        let prev_largest_ack_eliciting = self.largest_ack_eliciting_packet.unwrap_or(0);
+948
+949        // Track largest ack-eliciting packet
+950        self.largest_ack_eliciting_packet = self
+951            .largest_ack_eliciting_packet
+952            .map(|pn| pn.max(packet_number))
+953            .or(Some(packet_number));
+954
+955        // Handle ack_eliciting_threshold
+956        self.ack_eliciting_since_last_ack_sent += 1;
+957        self.immediate_ack_required |=
+958            self.ack_eliciting_since_last_ack_sent > self.ack_eliciting_threshold;
+959
+960        // Handle out-of-order packets
+961        self.immediate_ack_required |=
+962            self.is_out_of_order(packet_number, prev_largest_ack_eliciting, dedup);
+963
+964        // Arm max_ack_delay timer if necessary
+965        if self.earliest_ack_eliciting_since_last_ack_sent.is_none() && !self.can_send() {
+966            self.earliest_ack_eliciting_since_last_ack_sent = Some(now);
+967            return true;
+968        }
+969
+970        false
+971    }
+972
+973    fn is_out_of_order(
+974        &self,
+975        packet_number: u64,
+976        prev_largest_ack_eliciting: u64,
+977        dedup: &Dedup,
+978    ) -> bool {
+979        match self.reordering_threshold {
+980            0 => false,
+981            1 => {
+982                // From https://www.rfc-editor.org/rfc/rfc9000#section-13.2.1-7
+983                packet_number < prev_largest_ack_eliciting
+984                    || dedup.missing_in_interval(prev_largest_ack_eliciting, packet_number)
+985            }
+986            _ => {
+987                // From acknowledgement frequency draft, section 6.1: send an ACK immediately if
+988                // doing so would cause the sender to detect a new packet loss
+989                let Some((largest_acked, largest_unacked)) =
+990                    self.largest_acked.zip(self.largest_ack_eliciting_packet)
+991                else {
+992                    return false;
+993                };
+994                if self.reordering_threshold > largest_acked {
+995                    return false;
+996                }
+997                // The largest packet number that could be declared lost without a new ACK being
+998                // sent
+999                let largest_reported = largest_acked - self.reordering_threshold + 1;
+1000                let Some(smallest_missing_unreported) =
+1001                    dedup.smallest_missing_in_interval(largest_reported, largest_unacked)
+1002                else {
+1003                    return false;
+1004                };
+1005                largest_unacked - smallest_missing_unreported >= self.reordering_threshold
+1006            }
+1007        }
+1008    }
+1009
+1010    /// Should be called whenever ACKs have been sent
+1011    ///
+1012    /// This will suppress sending further ACKs until additional ACK eliciting frames arrive
+1013    pub(super) fn acks_sent(&mut self) {
+1014        // It is possible (though unlikely) that the ACKs we just sent do not cover all the
+1015        // ACK-eliciting packets we have received (e.g. if there is not enough room in the packet to
+1016        // fit all the ranges). To keep things simple, however, we assume they do. If there are
+1017        // indeed some ACKs that weren't covered, the packets might be ACKed later anyway, because
+1018        // they are still contained in `self.ranges`. If we somehow fail to send the ACKs at a later
+1019        // moment, the peer will assume the packets got lost and will retransmit their frames in a
+1020        // new packet, which is suboptimal, because we already received them. Our assumption here is
+1021        // that simplicity results in code that is more performant, even in the presence of
+1022        // occasional redundant retransmits.
+1023        self.immediate_ack_required = false;
+1024        self.ack_eliciting_since_last_ack_sent = 0;
+1025        self.non_ack_eliciting_since_last_ack_sent = 0;
+1026        self.earliest_ack_eliciting_since_last_ack_sent = None;
+1027        self.largest_acked = self.largest_ack_eliciting_packet;
+1028    }
+1029
+1030    /// Insert one packet that needs to be acknowledged
+1031    pub(super) fn insert_one(&mut self, packet: u64, now: Instant) {
+1032        self.ranges.insert_one(packet);
+1033
+1034        if self.largest_packet.is_none_or(|(pn, _)| packet > pn) {
+1035            self.largest_packet = Some((packet, now));
+1036        }
+1037
+1038        if self.ranges.len() > MAX_ACK_BLOCKS {
+1039            self.ranges.pop_min();
+1040        }
+1041    }
+1042
+1043    /// Remove ACKs of packets numbered at or below `max` from the set of pending ACKs
+1044    pub(super) fn subtract_below(&mut self, max: u64) {
+1045        self.ranges.remove(0..(max + 1));
+1046    }
+1047
+1048    /// Returns the set of currently pending ACK ranges
+1049    pub(super) fn ranges(&self) -> &ArrayRangeSet {
+1050        &self.ranges
+1051    }
+1052
+1053    /// Queue an ACK if a significant number of non-ACK-eliciting packets have not yet been
+1054    /// acknowledged
+1055    ///
+1056    /// Should be called immediately before a non-probing packet is composed, when we've already
+1057    /// committed to sending a packet regardless.
+1058    pub(super) fn maybe_ack_non_eliciting(&mut self) {
+1059        // If we're going to send a packet anyway, and we've received a significant number of
+1060        // non-ACK-eliciting packets, then include an ACK to help the peer perform timely loss
+1061        // detection even if they're not sending any ACK-eliciting packets themselves. Exact
+1062        // threshold chosen somewhat arbitrarily.
+1063        const LAZY_ACK_THRESHOLD: u64 = 10;
+1064        if self.non_ack_eliciting_since_last_ack_sent > LAZY_ACK_THRESHOLD {
+1065            self.immediate_ack_required = true;
+1066        }
+1067    }
+1068}
+1069
+1070/// Helper for mitigating [optimistic ACK attacks]
+1071///
+1072/// A malicious peer could prompt the local application to begin a large data transfer, and then
+1073/// send ACKs without first waiting for data to be received. This could defeat congestion control,
+1074/// allowing the connection to consume disproportionate resources. We therefore occasionally skip
+1075/// packet numbers, and classify any ACK referencing a skipped packet number as a transport error.
+1076///
+1077/// Skipped packet numbers occur only in the application data space (where costly transfers might
+1078/// take place) and are distributed exponentially to reflect the reduced likelihood and impact of
+1079/// bad behavior from a peer that has been well-behaved for an extended period.
+1080///
+1081/// ACKs for packet numbers that have not yet been allocated are also a transport error, but an
+1082/// attacker with knowledge of the congestion control algorithm in use could time falsified ACKs to
+1083/// arrive after the packets they reference are sent.
+1084///
+1085/// [optimistic ACK attacks]: https://www.rfc-editor.org/rfc/rfc9000.html#name-optimistic-ack-attack
+1086pub(super) struct PacketNumberFilter {
+1087    /// Next outgoing packet number to skip
+1088    next_skipped_packet_number: u64,
+1089    /// Most recently skipped packet number
+1090    prev_skipped_packet_number: Option<u64>,
+1091    /// Next packet number to skip is randomly selected from 2^n..2^n+1
+1092    exponent: u32,
+1093}
+1094
+1095impl PacketNumberFilter {
+1096    pub(super) fn new(rng: &mut (impl Rng + ?Sized)) -> Self {
+1097        // First skipped PN is in 0..64
+1098        let exponent = 6;
+1099        Self {
+1100            next_skipped_packet_number: rng.random_range(0..2u64.saturating_pow(exponent)),
+1101            prev_skipped_packet_number: None,
+1102            exponent,
+1103        }
+1104    }
+1105
+1106    #[cfg(test)]
+1107    pub(super) fn disabled() -> Self {
+1108        Self {
+1109            next_skipped_packet_number: u64::MAX,
+1110            prev_skipped_packet_number: None,
+1111            exponent: u32::MAX,
+1112        }
+1113    }
+1114
+1115    /// Whether to use the provided packet number (false) or to skip it (true)
+1116    pub(super) fn skip_pn(&mut self, n: u64, rng: &mut (impl Rng + ?Sized)) -> bool {
+1117        if n != self.next_skipped_packet_number {
+1118            return false;
+1119        }
+1120
+1121        trace!("skipping pn {n}");
+1122        // Skip this packet number, and choose the next one to skip
+1123        self.prev_skipped_packet_number = Some(self.next_skipped_packet_number);
+1124        let next_exponent = self.exponent.saturating_add(1);
+1125        self.next_skipped_packet_number = rng
+1126            .random_range(2u64.saturating_pow(self.exponent)..2u64.saturating_pow(next_exponent));
+1127        self.exponent = next_exponent;
+1128        true
+1129    }
+1130}
+1131
+1132/// Ensures we can always fit all our ACKs in a single minimum-MTU packet with room to spare
+1133const MAX_ACK_BLOCKS: usize = 64;
+1134
+1135#[cfg(test)]
+1136mod test {
+1137    use super::*;
+1138
+1139    #[test]
+1140    fn sanity() {
+1141        let mut dedup = Dedup::new();
+1142        assert!(!dedup.insert(0));
+1143        assert_eq!(dedup.next, 1);
+1144        assert_eq!(dedup.window, 0b1);
+1145        assert!(dedup.insert(0));
+1146        assert_eq!(dedup.next, 1);
+1147        assert_eq!(dedup.window, 0b1);
+1148        assert!(!dedup.insert(1));
+1149        assert_eq!(dedup.next, 2);
+1150        assert_eq!(dedup.window, 0b11);
+1151        assert!(!dedup.insert(2));
+1152        assert_eq!(dedup.next, 3);
+1153        assert_eq!(dedup.window, 0b111);
+1154        assert!(!dedup.insert(4));
+1155        assert_eq!(dedup.next, 5);
+1156        assert_eq!(dedup.window, 0b11110);
+1157        assert!(!dedup.insert(7));
+1158        assert_eq!(dedup.next, 8);
+1159        assert_eq!(dedup.window, 0b1111_0100);
+1160        assert!(dedup.insert(4));
+1161        assert!(!dedup.insert(3));
+1162        assert_eq!(dedup.next, 8);
+1163        assert_eq!(dedup.window, 0b1111_1100);
+1164        assert!(!dedup.insert(6));
+1165        assert_eq!(dedup.next, 8);
+1166        assert_eq!(dedup.window, 0b1111_1101);
+1167        assert!(!dedup.insert(5));
+1168        assert_eq!(dedup.next, 8);
+1169        assert_eq!(dedup.window, 0b1111_1111);
+1170    }
+1171
+1172    #[test]
+1173    fn happypath() {
+1174        let mut dedup = Dedup::new();
+1175        for i in 0..(2 * WINDOW_SIZE) {
+1176            assert!(!dedup.insert(i));
+1177            for j in 0..=i {
+1178                assert!(dedup.insert(j));
+1179            }
+1180        }
+1181    }
+1182
+1183    #[test]
+1184    fn jump() {
+1185        let mut dedup = Dedup::new();
+1186        dedup.insert(2 * WINDOW_SIZE);
+1187        assert!(dedup.insert(WINDOW_SIZE));
+1188        assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1);
+1189        assert_eq!(dedup.window, 0);
+1190        assert!(!dedup.insert(WINDOW_SIZE + 1));
+1191        assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1);
+1192        assert_eq!(dedup.window, 1 << (WINDOW_SIZE - 2));
+1193    }
+1194
+1195    #[test]
+1196    fn dedup_has_missing() {
+1197        let mut dedup = Dedup::new();
+1198
+1199        dedup.insert(0);
+1200        assert!(!dedup.missing_in_interval(0, 0));
+1201
+1202        dedup.insert(1);
+1203        assert!(!dedup.missing_in_interval(0, 1));
+1204
+1205        dedup.insert(3);
+1206        assert!(dedup.missing_in_interval(1, 3));
+1207
+1208        dedup.insert(4);
+1209        assert!(!dedup.missing_in_interval(3, 4));
+1210        assert!(dedup.missing_in_interval(0, 4));
+1211
+1212        dedup.insert(2);
+1213        assert!(!dedup.missing_in_interval(0, 4));
+1214    }
+1215
+1216    #[test]
+1217    fn dedup_outside_of_window_has_missing() {
+1218        let mut dedup = Dedup::new();
+1219
+1220        for i in 0..140 {
+1221            dedup.insert(i);
+1222        }
+1223
+1224        // 0 and 4 are outside of the window
+1225        assert!(!dedup.missing_in_interval(0, 4));
+1226        dedup.insert(160);
+1227        assert!(!dedup.missing_in_interval(0, 4));
+1228        assert!(!dedup.missing_in_interval(0, 140));
+1229        assert!(dedup.missing_in_interval(0, 160));
+1230    }
+1231
+1232    #[test]
+1233    fn dedup_smallest_missing() {
+1234        let mut dedup = Dedup::new();
+1235
+1236        dedup.insert(0);
+1237        assert_eq!(dedup.smallest_missing_in_interval(0, 0), None);
+1238
+1239        dedup.insert(1);
+1240        assert_eq!(dedup.smallest_missing_in_interval(0, 1), None);
+1241
+1242        dedup.insert(5);
+1243        dedup.insert(7);
+1244        assert_eq!(dedup.smallest_missing_in_interval(0, 7), Some(2));
+1245        assert_eq!(dedup.smallest_missing_in_interval(5, 7), Some(6));
+1246
+1247        dedup.insert(2);
+1248        assert_eq!(dedup.smallest_missing_in_interval(1, 7), Some(3));
+1249
+1250        dedup.insert(170);
+1251        dedup.insert(172);
+1252        dedup.insert(300);
+1253        assert_eq!(dedup.smallest_missing_in_interval(170, 172), None);
+1254
+1255        dedup.insert(500);
+1256        assert_eq!(dedup.smallest_missing_in_interval(0, 500), Some(372));
+1257        assert_eq!(dedup.smallest_missing_in_interval(0, 373), Some(372));
+1258        assert_eq!(dedup.smallest_missing_in_interval(0, 372), None);
+1259    }
+1260
+1261    #[test]
+1262    fn pending_acks_first_packet_is_not_considered_reordered() {
+1263        let mut acks = PendingAcks::new();
+1264        let mut dedup = Dedup::new();
+1265        dedup.insert(0);
+1266        acks.packet_received(Instant::now(), 0, true, &dedup);
+1267        assert!(!acks.immediate_ack_required);
+1268    }
+1269
+1270    #[test]
+1271    fn pending_acks_after_immediate_ack_set() {
+1272        let mut acks = PendingAcks::new();
+1273        let mut dedup = Dedup::new();
+1274
+1275        // Receive ack-eliciting packet
+1276        dedup.insert(0);
+1277        let now = Instant::now();
+1278        acks.insert_one(0, now);
+1279        acks.packet_received(now, 0, true, &dedup);
+1280
+1281        // Sanity check
+1282        assert!(!acks.ranges.is_empty());
+1283        assert!(!acks.can_send());
+1284
+1285        // Can send ACK after max_ack_delay exceeded
+1286        acks.set_immediate_ack_required();
+1287        assert!(acks.can_send());
+1288    }
+1289
+1290    #[test]
+1291    fn pending_acks_ack_delay() {
+1292        let mut acks = PendingAcks::new();
+1293        let mut dedup = Dedup::new();
+1294
+1295        let t1 = Instant::now();
+1296        let t2 = t1 + Duration::from_millis(2);
+1297        let t3 = t2 + Duration::from_millis(5);
+1298        assert_eq!(acks.ack_delay(t1), Duration::from_millis(0));
+1299        assert_eq!(acks.ack_delay(t2), Duration::from_millis(0));
+1300        assert_eq!(acks.ack_delay(t3), Duration::from_millis(0));
+1301
+1302        // In-order packet
+1303        dedup.insert(0);
+1304        acks.insert_one(0, t1);
+1305        acks.packet_received(t1, 0, true, &dedup);
+1306        assert_eq!(acks.ack_delay(t1), Duration::from_millis(0));
+1307        assert_eq!(acks.ack_delay(t2), Duration::from_millis(2));
+1308        assert_eq!(acks.ack_delay(t3), Duration::from_millis(7));
+1309
+1310        // Out of order (higher than expected)
+1311        dedup.insert(3);
+1312        acks.insert_one(3, t2);
+1313        acks.packet_received(t2, 3, true, &dedup);
+1314        assert_eq!(acks.ack_delay(t2), Duration::from_millis(0));
+1315        assert_eq!(acks.ack_delay(t3), Duration::from_millis(5));
+1316
+1317        // Out of order (lower than expected, so previous instant is kept)
+1318        dedup.insert(2);
+1319        acks.insert_one(2, t3);
+1320        acks.packet_received(t3, 2, true, &dedup);
+1321        assert_eq!(acks.ack_delay(t3), Duration::from_millis(5));
+1322    }
+1323
+1324    #[test]
+1325    fn sent_packet_size() {
+1326        // The tracking state of sent packets should be minimal, and not grow
+1327        // over time.
+1328        assert!(std::mem::size_of::<SentPacket>() <= 128);
+1329    }
+1330}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/state.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/state.rs.html new file mode 100644 index 000000000..61310b436 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/state.rs.html @@ -0,0 +1,414 @@ +state.rs - source

iroh_quinn_proto/connection/
state.rs

1use bytes::Bytes;
+2use tracing::trace;
+3
+4use crate::frame::Close;
+5use crate::{ApplicationClose, ConnectionClose, ConnectionError, TransportError, TransportErrorCode};
+6
+7#[allow(unreachable_pub)] // fuzzing only
+8#[derive(Debug, Clone)]
+9pub struct State {
+10    /// Nested [`InnerState`] to enforce all state transitions are done in this module.
+11    inner: InnerState,
+12}
+13
+14impl State {
+15    pub(super) fn as_handshake_mut(&mut self) -> Option<&mut Handshake> {
+16        if let InnerState::Handshake(ref mut hs) = self.inner {
+17            Some(hs)
+18        } else {
+19            None
+20        }
+21    }
+22
+23    pub(super) fn as_handshake(&self) -> Option<&Handshake> {
+24        if let InnerState::Handshake(ref hs) = self.inner {
+25            Some(hs)
+26        } else {
+27            None
+28        }
+29    }
+30
+31    pub(super) fn as_closed(&self) -> Option<&CloseReason> {
+32        if let InnerState::Closed {
+33            ref remote_reason, ..
+34        } = self.inner
+35        {
+36            Some(remote_reason)
+37        } else {
+38            None
+39        }
+40    }
+41
+42    #[allow(unreachable_pub)] // fuzzing only
+43    #[cfg(any(test, fuzzing))]
+44    pub fn established() -> Self {
+45        Self {
+46            inner: InnerState::Established,
+47        }
+48    }
+49
+50    pub(super) fn handshake(hs: Handshake) -> Self {
+51        Self {
+52            inner: InnerState::Handshake(hs),
+53        }
+54    }
+55
+56    pub(super) fn move_to_handshake(&mut self, hs: Handshake) {
+57        self.inner = InnerState::Handshake(hs);
+58        trace!("connection state: handshake");
+59    }
+60
+61    pub(super) fn move_to_established(&mut self) {
+62        self.inner = InnerState::Established;
+63        trace!("connection state: established");
+64    }
+65
+66    /// Moves to the drained state.
+67    ///
+68    /// Panics if the state was already drained.
+69    pub(super) fn move_to_drained(&mut self, error: Option<ConnectionError>) {
+70        let (error, is_local) = if let Some(error) = error {
+71            (Some(error), false)
+72        } else {
+73            let error = match &mut self.inner {
+74                InnerState::Draining { error, .. } => error.take(),
+75                InnerState::Drained { .. } => panic!("invalid state transition drained -> drained"),
+76                InnerState::Closed { error_read, .. } if *error_read => None,
+77                InnerState::Closed { remote_reason, .. } => {
+78                    let error = match remote_reason.clone().into() {
+79                        ConnectionError::ConnectionClosed(close) => {
+80                            if close.error_code == TransportErrorCode::PROTOCOL_VIOLATION {
+81                                ConnectionError::TransportError(TransportError::new(
+82                                    close.error_code,
+83                                    std::string::String::from_utf8_lossy(&close.reason[..])
+84                                        .to_string(),
+85                                ))
+86                            } else {
+87                                ConnectionError::ConnectionClosed(close)
+88                            }
+89                        }
+90                        e => e,
+91                    };
+92                    Some(error)
+93                }
+94                InnerState::Handshake(_) | InnerState::Established => None,
+95            };
+96            (error, self.is_local_close())
+97        };
+98        self.inner = InnerState::Drained { error, is_local };
+99        trace!("connection state: drained");
+100    }
+101
+102    /// Moves to a draining state.
+103    ///
+104    /// Panics if the state is already draining or drained.
+105    pub(super) fn move_to_draining(&mut self, error: Option<ConnectionError>) {
+106        assert!(
+107            matches!(
+108                self.inner,
+109                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+110            ),
+111            "invalid state transition {:?} -> draining",
+112            self.as_type()
+113        );
+114        let is_local = self.is_local_close();
+115        self.inner = InnerState::Draining { error, is_local };
+116        trace!("connection state: draining");
+117    }
+118
+119    fn is_local_close(&self) -> bool {
+120        match self.inner {
+121            InnerState::Handshake(_) => false,
+122            InnerState::Established => false,
+123            InnerState::Closed { is_local, .. } => is_local,
+124            InnerState::Draining { is_local, .. } => is_local,
+125            InnerState::Drained { is_local, .. } => is_local,
+126        }
+127    }
+128
+129    /// Enters the Closing connection state, due to changes in the [`Connection`] state.
+130    ///
+131    /// This is the closing state from
+132    /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1> due to the local side
+133    /// having initiated immediate close.
+134    ///
+135    /// Crucially, this is to be used when internal state changes result in initiating an
+136    /// immediate close. The resulting error will be surfaced as a [`ConnectionLost`] event
+137    /// in [`Connection::poll`].
+138    ///
+139    /// # Panics
+140    ///
+141    /// Panics if the state is later than established.
+142    ///
+143    /// [`ConnectionLost`]: crate::Event::ConnectionLost
+144    /// [`Connection::poll`]: super::Connection::poll
+145    pub(super) fn move_to_closed<R: Into<CloseReason>>(&mut self, reason: R) {
+146        assert!(
+147            matches!(
+148                self.inner,
+149                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+150            ),
+151            "invalid state transition {:?} -> closed",
+152            self.as_type()
+153        );
+154        let remote_reason = reason.into();
+155        let is_local = false;
+156        trace!(?remote_reason, ?is_local, "connection state: closed");
+157        self.inner = InnerState::Closed {
+158            error_read: false,
+159            remote_reason,
+160            is_local,
+161        };
+162    }
+163
+164    /// Enters the Closing connection state, initiated by explicit API calls.
+165    ///
+166    /// This is the closing state from
+167    /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1> due to the local side
+168    /// having initiated immediate close.
+169    ///
+170    /// Crucially, this is to be used when immediate close is entered due to an API
+171    /// being called. It means the close will NOT surface as a [`ConnectionLost`] event in
+172    /// [`Connection::poll`].
+173    ///
+174    /// See [`Self::move_to_closed`] for when the internal state changes resulted in
+175    /// initiating an immediate close.
+176    ///
+177    /// # Panics
+178    ///
+179    /// Panics if the state is later than established.
+180    ///
+181    /// [`ConnectionLost`]: crate::Event::ConnectionLost
+182    /// [`Connection::poll`]: super::Connection::poll
+183    pub(super) fn move_to_closed_local<R: Into<CloseReason>>(&mut self, reason: R) {
+184        assert!(
+185            matches!(
+186                self.inner,
+187                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+188            ),
+189            "invalid state transition {:?} -> closed (local)",
+190            self.as_type()
+191        );
+192        let remote_reason = reason.into();
+193        let is_local = true;
+194        trace!(?remote_reason, ?is_local, "connection state: closed");
+195        self.inner = InnerState::Closed {
+196            error_read: false,
+197            remote_reason,
+198            is_local,
+199        };
+200    }
+201
+202    pub(super) fn is_handshake(&self) -> bool {
+203        matches!(self.inner, InnerState::Handshake(_))
+204    }
+205
+206    pub(super) fn is_established(&self) -> bool {
+207        matches!(self.inner, InnerState::Established)
+208    }
+209
+210    pub(super) fn is_closed(&self) -> bool {
+211        matches!(
+212            self.inner,
+213            InnerState::Closed { .. } | InnerState::Draining { .. } | InnerState::Drained { .. }
+214        )
+215    }
+216
+217    pub(super) fn is_drained(&self) -> bool {
+218        matches!(self.inner, InnerState::Drained { .. })
+219    }
+220
+221    pub(super) fn take_error(&mut self) -> Option<ConnectionError> {
+222        match &mut self.inner {
+223            InnerState::Draining { error, is_local } => {
+224                if !*is_local {
+225                    error.take()
+226                } else {
+227                    None
+228                }
+229            }
+230            InnerState::Drained { error, is_local } => {
+231                if !*is_local {
+232                    error.take()
+233                } else {
+234                    None
+235                }
+236            }
+237            InnerState::Closed {
+238                remote_reason,
+239                is_local: local_reason,
+240                error_read,
+241            } => {
+242                if *error_read {
+243                    None
+244                } else {
+245                    *error_read = true;
+246                    if *local_reason {
+247                        None
+248                    } else {
+249                        Some(remote_reason.clone().into())
+250                    }
+251                }
+252            }
+253            InnerState::Handshake(_) | InnerState::Established => None,
+254        }
+255    }
+256
+257    pub(super) fn as_type(&self) -> StateType {
+258        match self.inner {
+259            InnerState::Handshake(_) => StateType::Handshake,
+260            InnerState::Established => StateType::Established,
+261            InnerState::Closed { .. } => StateType::Closed,
+262            InnerState::Draining { .. } => StateType::Draining,
+263            InnerState::Drained { .. } => StateType::Drained,
+264        }
+265    }
+266}
+267
+268/// The state a [`Connection`] can be in.
+269///
+270/// [`Connection`]: super::Connection
+271#[derive(Debug, Clone)]
+272pub(super) enum StateType {
+273    /// Before the handshake is *confirmed*.
+274    Handshake,
+275    /// Once the handshake is *confirmed*.
+276    Established,
+277    /// The connection is closed, waiting for remote to confirm close.
+278    ///
+279    /// Specifically <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1>.
+280    ///
+281    /// So the side that initiates an immediate close will stay in this state while it is
+282    /// waiting for the remote to also send a CONNECTION_CLOSE. The side that receives a
+283    /// connection close will skip straight to [`StateType::Draining`].
+284    Closed,
+285    /// The connection is draining, giving time to gracefully discard any in-flight packets.
+286    ///
+287    /// Specifically <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.2>.
+288    ///
+289    /// See [`StateType::Closed`] above for more details.
+290    Draining,
+291    /// The connection is drained, waiting for the application to drop us.
+292    ///
+293    /// This is a terminal state in which the connection does nothing and can never do
+294    /// anything again. Waiting for the application to drop the [`Connection`] struct.
+295    ///
+296    /// [`Connection`]: super::Connection
+297    Drained,
+298}
+299
+300#[derive(Debug, Clone)]
+301pub(super) enum CloseReason {
+302    TransportError(TransportError),
+303    Connection(ConnectionClose),
+304    Application(ApplicationClose),
+305}
+306
+307impl From<TransportError> for CloseReason {
+308    fn from(x: TransportError) -> Self {
+309        Self::TransportError(x)
+310    }
+311}
+312impl From<ConnectionClose> for CloseReason {
+313    fn from(x: ConnectionClose) -> Self {
+314        Self::Connection(x)
+315    }
+316}
+317impl From<ApplicationClose> for CloseReason {
+318    fn from(x: ApplicationClose) -> Self {
+319        Self::Application(x)
+320    }
+321}
+322
+323impl From<Close> for CloseReason {
+324    fn from(value: Close) -> Self {
+325        match value {
+326            Close::Application(reason) => Self::Application(reason),
+327            Close::Connection(reason) => Self::Connection(reason),
+328        }
+329    }
+330}
+331
+332impl From<CloseReason> for ConnectionError {
+333    fn from(value: CloseReason) -> Self {
+334        match value {
+335            CloseReason::TransportError(err) => Self::TransportError(err),
+336            CloseReason::Connection(reason) => Self::ConnectionClosed(reason),
+337            CloseReason::Application(reason) => Self::ApplicationClosed(reason),
+338        }
+339    }
+340}
+341
+342impl From<CloseReason> for Close {
+343    fn from(value: CloseReason) -> Self {
+344        match value {
+345            CloseReason::TransportError(err) => Self::Connection(err.into()),
+346            CloseReason::Connection(reason) => Self::Connection(reason),
+347            CloseReason::Application(reason) => Self::Application(reason),
+348        }
+349    }
+350}
+351
+352#[derive(Debug, Clone)]
+353enum InnerState {
+354    /// See [`StateType::Handshake`].
+355    Handshake(Handshake),
+356    /// See [`StateType::Established`].
+357    Established,
+358    /// See [`StateType::Closed`].
+359    Closed {
+360        /// The reason the remote closed the connection, or the reason we are sending to the remote.
+361        remote_reason: CloseReason,
+362        /// Set to true if we closed the connection locally.
+363        is_local: bool,
+364        /// Did we read this as error already?
+365        error_read: bool,
+366    },
+367    /// See [`StateType::Draining`].
+368    Draining {
+369        /// Why the connection was lost, if it has been.
+370        error: Option<ConnectionError>,
+371        /// Set to true if we closed the connection locally.
+372        is_local: bool,
+373    },
+374    /// See [`StateType::Drained`].
+375    /// Waiting for application to call close so we can dispose of the resources.
+376    Drained {
+377        /// Why the connection was lost, if it has been.
+378        error: Option<ConnectionError>,
+379        /// Set to true if we closed the connection locally.
+380        is_local: bool,
+381    },
+382}
+383
+384#[allow(unreachable_pub)] // fuzzing only
+385#[derive(Debug, Clone)]
+386pub struct Handshake {
+387    /// Whether the remote CID has been set by the peer yet.
+388    ///
+389    /// Always set for servers.
+390    pub(super) remote_cid_set: bool,
+391    /// Stateless retry token received in the first Initial by a server.
+392    ///
+393    /// Must be present in every Initial. Always empty for clients.
+394    pub(super) expected_token: Bytes,
+395    /// First cryptographic message.
+396    ///
+397    /// Only set for clients.
+398    pub(super) client_hello: Option<Bytes>,
+399    /// Whether the server address is allowed to migrate.
+400    ///
+401    /// We allow the server to migrate during the handshake as long as we have not
+402    /// received an authenticated handshake packet: it can send a response from a
+403    /// different address than we sent the initial to.  This allows us to send the
+404    /// initial packet over multiple paths - by means of an IPv6 ULA address that copies
+405    /// the packets sent to it to multiple destinations - and accept one response.
+406    ///
+407    /// This is only ever set to true if for a client which hasn't yet received an
+408    /// authenticated handshake packet.  It is set back to false in
+409    /// [`super::Connection::on_packet_authenticated`].
+410    ///
+411    /// THIS IS NOT RFC 9000 COMPLIANT!  A server is not allowed to migrate addresses,
+412    /// other than using the preferred-address transport parameter.
+413    pub(super) allow_server_migration: bool,
+414}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/stats.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/stats.rs.html new file mode 100644 index 000000000..eb1377f25 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/stats.rs.html @@ -0,0 +1,205 @@ +stats.rs - source

iroh_quinn_proto/connection/
stats.rs

1//! Connection statistics
+2
+3use crate::Duration;
+4use crate::FrameType;
+5
+6/// Statistics about UDP datagrams transmitted or received on a connection
+7///
+8/// All QUIC packets are carried by UDP datagrams. Hence, these statistics cover all traffic on a connection.
+9#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
+10#[non_exhaustive]
+11pub struct UdpStats {
+12    /// The amount of UDP datagrams observed
+13    pub datagrams: u64,
+14    /// The total amount of bytes which have been transferred inside UDP datagrams
+15    pub bytes: u64,
+16    /// The amount of I/O operations executed
+17    ///
+18    /// Can be less than `datagrams` when GSO, GRO, and/or batched system calls are in use.
+19    pub ios: u64,
+20}
+21
+22impl UdpStats {
+23    pub(crate) fn on_sent(&mut self, datagrams: u64, bytes: usize) {
+24        self.datagrams += datagrams;
+25        self.bytes += bytes as u64;
+26        self.ios += 1;
+27    }
+28}
+29
+30/// Number of frames transmitted or received of each frame type
+31#[derive(Default, Copy, Clone)]
+32#[non_exhaustive]
+33#[allow(missing_docs)]
+34pub struct FrameStats {
+35    pub acks: u64,
+36    pub path_acks: u64,
+37    pub ack_frequency: u64,
+38    pub crypto: u64,
+39    pub connection_close: u64,
+40    pub data_blocked: u64,
+41    pub datagram: u64,
+42    pub handshake_done: u8,
+43    pub immediate_ack: u64,
+44    pub max_data: u64,
+45    pub max_stream_data: u64,
+46    pub max_streams_bidi: u64,
+47    pub max_streams_uni: u64,
+48    pub new_connection_id: u64,
+49    pub path_new_connection_id: u64,
+50    pub new_token: u64,
+51    pub path_challenge: u64,
+52    pub path_response: u64,
+53    pub ping: u64,
+54    pub reset_stream: u64,
+55    pub retire_connection_id: u64,
+56    pub path_retire_connection_id: u64,
+57    pub stream_data_blocked: u64,
+58    pub streams_blocked_bidi: u64,
+59    pub streams_blocked_uni: u64,
+60    pub stop_sending: u64,
+61    pub stream: u64,
+62    pub observed_addr: u64,
+63    pub path_abandon: u64,
+64    pub path_status_available: u64,
+65    pub path_status_backup: u64,
+66    pub max_path_id: u64,
+67    pub paths_blocked: u64,
+68    pub path_cids_blocked: u64,
+69    pub add_address: u64,
+70    pub reach_out: u64,
+71    pub remove_address: u64,
+72}
+73
+74impl FrameStats {
+75    pub(crate) fn record(&mut self, frame_type: FrameType) {
+76        use FrameType::*;
+77        // Increments the field. Added for readability
+78        macro_rules! inc {
+79            ($field_name: ident) => {{ self.$field_name = self.$field_name.saturating_add(1) }};
+80        }
+81        match frame_type {
+82            Padding => {}
+83            Ping => inc!(ping),
+84            Ack | AckEcn => inc!(acks),
+85            PathAck | PathAckEcn => inc!(path_acks),
+86            ResetStream => inc!(reset_stream),
+87            StopSending => inc!(stop_sending),
+88            Crypto => inc!(crypto),
+89            Datagram(_) => inc!(datagram),
+90            NewToken => inc!(new_token),
+91            MaxData => inc!(max_data),
+92            MaxStreamData => inc!(max_stream_data),
+93            MaxStreamsBidi => inc!(max_streams_bidi),
+94            MaxStreamsUni => inc!(max_streams_uni),
+95            DataBlocked => inc!(data_blocked),
+96            Stream(_) => inc!(stream),
+97            StreamDataBlocked => inc!(stream_data_blocked),
+98            StreamsBlockedUni => inc!(streams_blocked_uni),
+99            StreamsBlockedBidi => inc!(streams_blocked_bidi),
+100            NewConnectionId => inc!(new_connection_id),
+101            PathNewConnectionId => inc!(path_new_connection_id),
+102            RetireConnectionId => inc!(retire_connection_id),
+103            PathRetireConnectionId => inc!(path_retire_connection_id),
+104            PathChallenge => inc!(path_challenge),
+105            PathResponse => inc!(path_response),
+106            ConnectionClose | ApplicationClose => inc!(connection_close),
+107            AckFrequency => inc!(ack_frequency),
+108            ImmediateAck => inc!(immediate_ack),
+109            HandshakeDone => inc!(handshake_done),
+110            ObservedIpv4Addr | ObservedIpv6Addr => inc!(observed_addr),
+111            PathAbandon => inc!(path_abandon),
+112            PathStatusAvailable => inc!(path_status_available),
+113            PathStatusBackup => inc!(path_status_backup),
+114            MaxPathId => inc!(max_path_id),
+115            PathsBlocked => inc!(paths_blocked),
+116            PathCidsBlocked => inc!(path_cids_blocked),
+117            AddIpv4Address | AddIpv6Address => inc!(add_address),
+118            ReachOutAtIpv4 | ReachOutAtIpv6 => inc!(reach_out),
+119            RemoveAddress => inc!(remove_address),
+120        };
+121    }
+122}
+123
+124impl std::fmt::Debug for FrameStats {
+125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+126        f.debug_struct("FrameStats")
+127            .field("ACK", &self.acks)
+128            .field("ACK_FREQUENCY", &self.ack_frequency)
+129            .field("CONNECTION_CLOSE", &self.connection_close)
+130            .field("CRYPTO", &self.crypto)
+131            .field("DATA_BLOCKED", &self.data_blocked)
+132            .field("DATAGRAM", &self.datagram)
+133            .field("HANDSHAKE_DONE", &self.handshake_done)
+134            .field("IMMEDIATE_ACK", &self.immediate_ack)
+135            .field("MAX_DATA", &self.max_data)
+136            .field("MAX_PATH_ID", &self.max_path_id)
+137            .field("MAX_STREAM_DATA", &self.max_stream_data)
+138            .field("MAX_STREAMS_BIDI", &self.max_streams_bidi)
+139            .field("MAX_STREAMS_UNI", &self.max_streams_uni)
+140            .field("NEW_CONNECTION_ID", &self.new_connection_id)
+141            .field("NEW_TOKEN", &self.new_token)
+142            .field("PATHS_BLOCKED", &self.paths_blocked)
+143            .field("PATH_ABANDON", &self.path_abandon)
+144            .field("PATH_ACK", &self.path_acks)
+145            .field("PATH_STATUS_AVAILABLE", &self.path_status_available)
+146            .field("PATH_STATUS_BACKUP", &self.path_status_backup)
+147            .field("PATH_CHALLENGE", &self.path_challenge)
+148            .field("PATH_CIDS_BLOCKED", &self.path_cids_blocked)
+149            .field("PATH_NEW_CONNECTION_ID", &self.path_new_connection_id)
+150            .field("PATH_RESPONSE", &self.path_response)
+151            .field("PATH_RETIRE_CONNECTION_ID", &self.path_retire_connection_id)
+152            .field("PING", &self.ping)
+153            .field("RESET_STREAM", &self.reset_stream)
+154            .field("RETIRE_CONNECTION_ID", &self.retire_connection_id)
+155            .field("STREAM_DATA_BLOCKED", &self.stream_data_blocked)
+156            .field("STREAMS_BLOCKED_BIDI", &self.streams_blocked_bidi)
+157            .field("STREAMS_BLOCKED_UNI", &self.streams_blocked_uni)
+158            .field("STOP_SENDING", &self.stop_sending)
+159            .field("STREAM", &self.stream)
+160            .finish()
+161    }
+162}
+163
+164/// Statistics related to a transmission path
+165#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
+166#[non_exhaustive]
+167pub struct PathStats {
+168    /// Current best estimate of this connection's latency (round-trip-time)
+169    pub rtt: Duration,
+170    /// Statistics about datagrams and bytes sent on this path
+171    pub udp_tx: UdpStats,
+172    /// Statistics about datagrams and bytes received on this path
+173    pub udp_rx: UdpStats,
+174    /// Current congestion window of the connection
+175    pub cwnd: u64,
+176    /// Congestion events on the connection
+177    pub congestion_events: u64,
+178    /// The amount of packets lost on this path
+179    pub lost_packets: u64,
+180    /// The amount of bytes lost on this path
+181    pub lost_bytes: u64,
+182    /// The amount of PLPMTUD probe packets sent on this path (also counted by `udp_tx.datagrams`)
+183    pub sent_plpmtud_probes: u64,
+184    /// The amount of PLPMTUD probe packets lost on this path (ignored by `lost_packets` and
+185    /// `lost_bytes`)
+186    pub lost_plpmtud_probes: u64,
+187    /// The number of times a black hole was detected in the path
+188    pub black_holes_detected: u64,
+189    /// Largest UDP payload size the path currently supports
+190    pub current_mtu: u16,
+191}
+192
+193/// Connection statistics
+194#[derive(Debug, Default, Clone)]
+195#[non_exhaustive]
+196pub struct ConnectionStats {
+197    /// Statistics about UDP datagrams transmitted on a connection
+198    pub udp_tx: UdpStats,
+199    /// Statistics about UDP datagrams received on a connection
+200    pub udp_rx: UdpStats,
+201    /// Statistics about frames transmitted on a connection
+202    pub frame_tx: FrameStats,
+203    /// Statistics about frames received on a connection
+204    pub frame_rx: FrameStats,
+205}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html new file mode 100644 index 000000000..44a9bbfb2 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html @@ -0,0 +1,554 @@ +mod.rs - source

iroh_quinn_proto/connection/streams/
mod.rs

1use std::{
+2    collections::{BinaryHeap, hash_map},
+3    io,
+4};
+5
+6use bytes::Bytes;
+7use thiserror::Error;
+8use tracing::trace;
+9
+10use super::spaces::Retransmits;
+11use crate::{
+12    Dir, StreamId, VarInt,
+13    connection::streams::state::{get_or_insert_recv, get_or_insert_send},
+14    frame,
+15};
+16
+17mod recv;
+18use recv::Recv;
+19pub use recv::{Chunks, ReadError, ReadableError};
+20
+21mod send;
+22pub(crate) use send::{ByteSlice, BytesArray};
+23use send::{BytesSource, Send, SendState};
+24pub use send::{FinishError, WriteError, Written};
+25
+26mod state;
+27#[allow(unreachable_pub)] // fuzzing only
+28pub use state::StreamsState;
+29
+30/// Access to streams
+31pub struct Streams<'a> {
+32    pub(super) state: &'a mut StreamsState,
+33    pub(super) conn_state: &'a super::State,
+34}
+35
+36#[allow(clippy::needless_lifetimes)] // Needed for cfg(fuzzing)
+37impl<'a> Streams<'a> {
+38    #[cfg(fuzzing)]
+39    pub fn new(state: &'a mut StreamsState, conn_state: &'a super::State) -> Self {
+40        Self { state, conn_state }
+41    }
+42
+43    /// Open a single stream if possible
+44    ///
+45    /// Returns `None` if the streams in the given direction are currently exhausted.
+46    pub fn open(&mut self, dir: Dir) -> Option<StreamId> {
+47        if self.conn_state.is_closed() {
+48            return None;
+49        }
+50
+51        // TODO: Queue STREAM_ID_BLOCKED if this fails
+52        if self.state.next[dir as usize] >= self.state.max[dir as usize] {
+53            return None;
+54        }
+55
+56        self.state.next[dir as usize] += 1;
+57        let id = StreamId::new(self.state.side, dir, self.state.next[dir as usize] - 1);
+58        self.state.insert(false, id);
+59        self.state.send_streams += 1;
+60        Some(id)
+61    }
+62
+63    /// Accept a remotely initiated stream of a certain directionality, if possible
+64    ///
+65    /// Returns `None` if there are no new incoming streams for this connection.
+66    /// Has no impact on the data flow-control or stream concurrency limits.
+67    pub fn accept(&mut self, dir: Dir) -> Option<StreamId> {
+68        if self.state.next_remote[dir as usize] == self.state.next_reported_remote[dir as usize] {
+69            return None;
+70        }
+71
+72        let x = self.state.next_reported_remote[dir as usize];
+73        self.state.next_reported_remote[dir as usize] = x + 1;
+74        if dir == Dir::Bi {
+75            self.state.send_streams += 1;
+76        }
+77
+78        Some(StreamId::new(!self.state.side, dir, x))
+79    }
+80
+81    #[cfg(fuzzing)]
+82    pub fn state(&mut self) -> &mut StreamsState {
+83        self.state
+84    }
+85
+86    /// The number of streams that may have unacknowledged data.
+87    pub fn send_streams(&self) -> usize {
+88        self.state.send_streams
+89    }
+90
+91    /// The number of remotely initiated open streams of a certain directionality.
+92    ///
+93    /// Includes remotely initiated streams, which have not been accepted via [`accept`](Self::accept).
+94    /// These streams count against the respective concurrency limit reported by
+95    /// [`Connection::max_concurrent_streams`](super::Connection::max_concurrent_streams).
+96    pub fn remote_open_streams(&self, dir: Dir) -> u64 {
+97        // total opened - total closed = total opened - ( total permitted - total permitted unclosed )
+98        self.state.next_remote[dir as usize]
+99            - (self.state.max_remote[dir as usize]
+100                - self.state.allocated_remote_count[dir as usize])
+101    }
+102}
+103
+104/// Access to streams
+105pub struct RecvStream<'a> {
+106    pub(super) id: StreamId,
+107    pub(super) state: &'a mut StreamsState,
+108    pub(super) pending: &'a mut Retransmits,
+109}
+110
+111impl RecvStream<'_> {
+112    /// Read from the given recv stream
+113    ///
+114    /// `max_length` limits the maximum size of the returned `Bytes` value; passing `usize::MAX`
+115    /// will yield the best performance. `ordered` will make sure the returned chunk's offset will
+116    /// have an offset exactly equal to the previously returned offset plus the previously returned
+117    /// bytes' length.
+118    ///
+119    /// Yields `Ok(None)` if the stream was finished. Otherwise, yields a segment of data and its
+120    /// offset in the stream. If `ordered` is `false`, segments may be received in any order, and
+121    /// the `Chunk`'s `offset` field can be used to determine ordering in the caller.
+122    ///
+123    /// While most applications will prefer to consume stream data in order, unordered reads can
+124    /// improve performance when packet loss occurs and data cannot be retransmitted before the flow
+125    /// control window is filled. On any given stream, you can switch from ordered to unordered
+126    /// reads, but ordered reads on streams that have seen previous unordered reads will return
+127    /// `ReadError::IllegalOrderedRead`.
+128    pub fn read(&mut self, ordered: bool) -> Result<Chunks<'_>, ReadableError> {
+129        Chunks::new(self.id, ordered, self.state, self.pending)
+130    }
+131
+132    /// Stop accepting data on the given receive stream
+133    ///
+134    /// Discards unread data and notifies the peer to stop transmitting. Once stopped, further
+135    /// attempts to operate on a stream will yield `ClosedStream` errors.
+136    pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+137        let mut entry = match self.state.recv.entry(self.id) {
+138            hash_map::Entry::Occupied(s) => s,
+139            hash_map::Entry::Vacant(_) => return Err(ClosedStream { _private: () }),
+140        };
+141        let stream = get_or_insert_recv(self.state.stream_receive_window)(entry.get_mut());
+142
+143        let (read_credits, stop_sending) = stream.stop()?;
+144        if stop_sending.should_transmit() {
+145            self.pending.stop_sending.push(frame::StopSending {
+146                id: self.id,
+147                error_code,
+148            });
+149        }
+150
+151        // We need to keep stopped streams around until they're finished or reset so we can update
+152        // connection-level flow control to account for discarded data. Otherwise, we can discard
+153        // state immediately.
+154        if !stream.final_offset_unknown() {
+155            let recv = entry.remove().expect("must have recv when stopping");
+156            self.state.stream_recv_freed(self.id, recv);
+157        }
+158
+159        if self.state.add_read_credits(read_credits).should_transmit() {
+160            self.pending.max_data = true;
+161        }
+162
+163        Ok(())
+164    }
+165
+166    /// Check whether this stream has been reset by the peer, returning the reset error code if so
+167    ///
+168    /// After returning `Ok(Some(_))` once, stream state will be discarded and all future calls will
+169    /// return `Err(ClosedStream)`.
+170    pub fn received_reset(&mut self) -> Result<Option<VarInt>, ClosedStream> {
+171        let hash_map::Entry::Occupied(entry) = self.state.recv.entry(self.id) else {
+172            return Err(ClosedStream { _private: () });
+173        };
+174        let Some(s) = entry.get().as_ref().and_then(|s| s.as_open_recv()) else {
+175            return Ok(None);
+176        };
+177        if s.stopped {
+178            return Err(ClosedStream { _private: () });
+179        }
+180        let Some(code) = s.reset_code() else {
+181            return Ok(None);
+182        };
+183
+184        // Clean up state after application observes the reset, since there's no reason for the
+185        // application to attempt to read or stop the stream once it knows it's reset
+186        let (_, recv) = entry.remove_entry();
+187        self.state
+188            .stream_recv_freed(self.id, recv.expect("must have recv on reset"));
+189        self.state.queue_max_stream_id(self.pending);
+190
+191        Ok(Some(code))
+192    }
+193}
+194
+195/// Access to streams
+196pub struct SendStream<'a> {
+197    pub(super) id: StreamId,
+198    pub(super) state: &'a mut StreamsState,
+199    pub(super) pending: &'a mut Retransmits,
+200    pub(super) conn_state: &'a super::State,
+201}
+202
+203#[allow(clippy::needless_lifetimes)] // Needed for cfg(fuzzing)
+204impl<'a> SendStream<'a> {
+205    #[cfg(fuzzing)]
+206    pub fn new(
+207        id: StreamId,
+208        state: &'a mut StreamsState,
+209        pending: &'a mut Retransmits,
+210        conn_state: &'a super::State,
+211    ) -> Self {
+212        Self {
+213            id,
+214            state,
+215            pending,
+216            conn_state,
+217        }
+218    }
+219
+220    /// Send data on the given stream
+221    ///
+222    /// Returns the number of bytes successfully written.
+223    pub fn write(&mut self, data: &[u8]) -> Result<usize, WriteError> {
+224        Ok(self.write_source(&mut ByteSlice::from_slice(data))?.bytes)
+225    }
+226
+227    /// Send data on the given stream
+228    ///
+229    /// Returns the number of bytes and chunks successfully written.
+230    /// Note that this method might also write a partial chunk. In this case
+231    /// [`Written::chunks`] will not count this chunk as fully written. However
+232    /// the chunk will be advanced and contain only non-written data after the call.
+233    pub fn write_chunks(&mut self, data: &mut [Bytes]) -> Result<Written, WriteError> {
+234        self.write_source(&mut BytesArray::from_chunks(data))
+235    }
+236
+237    fn write_source<'b, B: BytesSource<'b>>(
+238        &mut self,
+239        source: &'b mut B,
+240    ) -> Result<Written, WriteError> {
+241        if self.conn_state.is_closed() {
+242            trace!(%self.id, "write blocked; connection draining");
+243            return Err(WriteError::Blocked);
+244        }
+245
+246        let limit = self.state.write_limit();
+247
+248        let max_send_data = self.state.max_send_data(self.id);
+249
+250        let stream = self
+251            .state
+252            .send
+253            .get_mut(&self.id)
+254            .map(get_or_insert_send(max_send_data))
+255            .ok_or(WriteError::ClosedStream)?;
+256
+257        if limit == 0 {
+258            trace!(
+259                stream = %self.id, max_data = self.state.max_data, data_sent = self.state.data_sent,
+260                "write blocked by connection-level flow control or send window"
+261            );
+262            if !stream.connection_blocked {
+263                stream.connection_blocked = true;
+264                self.state.connection_blocked.push(self.id);
+265            }
+266            return Err(WriteError::Blocked);
+267        }
+268
+269        let was_pending = stream.is_pending();
+270        let written = stream.write(source, limit)?;
+271        self.state.data_sent += written.bytes as u64;
+272        self.state.unacked_data += written.bytes as u64;
+273        trace!(stream = %self.id, "wrote {} bytes", written.bytes);
+274        if !was_pending {
+275            self.state.pending.push_pending(self.id, stream.priority);
+276        }
+277        Ok(written)
+278    }
+279
+280    /// Check if this stream was stopped, get the reason if it was
+281    pub fn stopped(&self) -> Result<Option<VarInt>, ClosedStream> {
+282        match self.state.send.get(&self.id).as_ref() {
+283            Some(Some(s)) => Ok(s.stop_reason),
+284            Some(None) => Ok(None),
+285            None => Err(ClosedStream { _private: () }),
+286        }
+287    }
+288
+289    /// Finish a send stream, signalling that no more data will be sent.
+290    ///
+291    /// If this fails, no [`StreamEvent::Finished`] will be generated.
+292    ///
+293    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+294    pub fn finish(&mut self) -> Result<(), FinishError> {
+295        let max_send_data = self.state.max_send_data(self.id);
+296        let stream = self
+297            .state
+298            .send
+299            .get_mut(&self.id)
+300            .map(get_or_insert_send(max_send_data))
+301            .ok_or(FinishError::ClosedStream)?;
+302
+303        let was_pending = stream.is_pending();
+304        stream.finish()?;
+305        if !was_pending {
+306            self.state.pending.push_pending(self.id, stream.priority);
+307        }
+308
+309        Ok(())
+310    }
+311
+312    /// Abandon transmitting data on a stream
+313    ///
+314    /// # Panics
+315    /// - when applied to a receive stream
+316    pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+317        let max_send_data = self.state.max_send_data(self.id);
+318        let stream = self
+319            .state
+320            .send
+321            .get_mut(&self.id)
+322            .map(get_or_insert_send(max_send_data))
+323            .ok_or(ClosedStream { _private: () })?;
+324
+325        if matches!(stream.state, SendState::ResetSent) {
+326            // Redundant reset call
+327            return Err(ClosedStream { _private: () });
+328        }
+329
+330        // Restore the portion of the send window consumed by the data that we aren't about to
+331        // send. We leave flow control alone because the peer's responsible for issuing additional
+332        // credit based on the final offset communicated in the RESET_STREAM frame we send.
+333        self.state.unacked_data -= stream.pending.unacked();
+334        stream.reset();
+335        self.pending.reset_stream.push((self.id, error_code));
+336
+337        // Don't reopen an already-closed stream we haven't forgotten yet
+338        Ok(())
+339    }
+340
+341    /// Set the priority of a stream
+342    ///
+343    /// # Panics
+344    /// - when applied to a receive stream
+345    pub fn set_priority(&mut self, priority: i32) -> Result<(), ClosedStream> {
+346        let max_send_data = self.state.max_send_data(self.id);
+347        let stream = self
+348            .state
+349            .send
+350            .get_mut(&self.id)
+351            .map(get_or_insert_send(max_send_data))
+352            .ok_or(ClosedStream { _private: () })?;
+353
+354        stream.priority = priority;
+355        Ok(())
+356    }
+357
+358    /// Get the priority of a stream
+359    ///
+360    /// # Panics
+361    /// - when applied to a receive stream
+362    pub fn priority(&self) -> Result<i32, ClosedStream> {
+363        let stream = self
+364            .state
+365            .send
+366            .get(&self.id)
+367            .ok_or(ClosedStream { _private: () })?;
+368
+369        Ok(stream.as_ref().map(|s| s.priority).unwrap_or_default())
+370    }
+371}
+372
+373/// A queue of streams with pending outgoing data, sorted by priority
+374struct PendingStreamsQueue {
+375    streams: BinaryHeap<PendingStream>,
+376    /// The next stream to write out. This is `Some` when `TransportConfig::send_fairness(false)` and writing a stream is
+377    /// interrupted while the stream still has some pending data. See `reinsert_pending()`.
+378    next: Option<PendingStream>,
+379    /// A monotonically decreasing counter, used to implement round-robin scheduling for streams of the same priority.
+380    /// Underflowing is not a practical concern, as it is initialized to u64::MAX and only decremented by 1 in `push_pending`
+381    recency: u64,
+382}
+383
+384impl PendingStreamsQueue {
+385    fn new() -> Self {
+386        Self {
+387            streams: BinaryHeap::new(),
+388            next: None,
+389            recency: u64::MAX,
+390        }
+391    }
+392
+393    /// Reinsert a stream that was pending and still contains unsent data.
+394    fn reinsert_pending(&mut self, id: StreamId, priority: i32) {
+395        assert!(self.next.is_none());
+396
+397        self.next = Some(PendingStream {
+398            priority,
+399            recency: self.recency, // the value here doesn't really matter
+400            id,
+401        });
+402    }
+403
+404    /// Push a pending stream ID with the given priority, queued after any already-queued streams for the priority
+405    fn push_pending(&mut self, id: StreamId, priority: i32) {
+406        // Note that in the case where fairness is disabled, if we have a reinserted stream we don't
+407        // bump it even if priority > next.priority. In order to minimize fragmentation we
+408        // always try to complete a stream once part of it has been written.
+409
+410        // As the recency counter is monotonically decreasing, we know that using its value to sort this stream will queue it
+411        // after all other queued streams of the same priority.
+412        // This is enough to implement round-robin scheduling for streams that are still pending even after being handled,
+413        // as in that case they are removed from the `BinaryHeap`, handled, and then immediately reinserted.
+414        self.recency -= 1;
+415        self.streams.push(PendingStream {
+416            priority,
+417            recency: self.recency,
+418            id,
+419        });
+420    }
+421
+422    fn pop(&mut self) -> Option<PendingStream> {
+423        self.next.take().or_else(|| self.streams.pop())
+424    }
+425
+426    fn clear(&mut self) {
+427        self.next = None;
+428        self.streams.clear();
+429    }
+430
+431    fn iter(&self) -> impl Iterator<Item = &PendingStream> {
+432        self.next.iter().chain(self.streams.iter())
+433    }
+434
+435    #[cfg(test)]
+436    fn len(&self) -> usize {
+437        self.streams.len() + self.next.is_some() as usize
+438    }
+439}
+440
+441/// The [`StreamId`] of a stream with pending data queued, ordered by its priority and recency
+442#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
+443struct PendingStream {
+444    /// The priority of the stream
+445    // Note that this field should be kept above the `recency` field, in order for the `Ord` derive to be correct
+446    // (See https://doc.rust-lang.org/stable/std/cmp/trait.Ord.html#derivable)
+447    priority: i32,
+448    /// A tie-breaker for streams of the same priority, used to improve fairness by implementing round-robin scheduling:
+449    /// Larger values are prioritized, so it is initialised to `u64::MAX`, and when a stream writes data, we know
+450    /// that it currently has the highest recency value, so it is deprioritized by setting its recency to 1 less than the
+451    /// previous lowest recency value, such that all other streams of this priority will get processed once before we get back
+452    /// round to this one
+453    recency: u64,
+454    /// The ID of the stream
+455    // The way this type is used ensures that every instance has a unique `recency` value, so this field should be kept below
+456    // the `priority` and `recency` fields, so that it does not interfere with the behaviour of the `Ord` derive
+457    id: StreamId,
+458}
+459
+460/// Application events about streams
+461#[derive(Debug, PartialEq, Eq)]
+462pub enum StreamEvent {
+463    /// One or more new streams has been opened and might be readable
+464    Opened {
+465        /// Directionality for which streams have been opened
+466        dir: Dir,
+467    },
+468    /// A currently open stream likely has data or errors waiting to be read
+469    Readable {
+470        /// Which stream is now readable
+471        id: StreamId,
+472    },
+473    /// A formerly write-blocked stream might be ready for a write or have been stopped
+474    ///
+475    /// Only generated for streams that are currently open.
+476    Writable {
+477        /// Which stream is now writable
+478        id: StreamId,
+479    },
+480    /// A finished stream has been fully acknowledged or stopped
+481    Finished {
+482        /// Which stream has been finished
+483        id: StreamId,
+484    },
+485    /// The peer asked us to stop sending on an outgoing stream
+486    Stopped {
+487        /// Which stream has been stopped
+488        id: StreamId,
+489        /// Error code supplied by the peer
+490        error_code: VarInt,
+491    },
+492    /// At least one new stream of a certain directionality may be opened
+493    Available {
+494        /// Directionality for which streams are newly available
+495        dir: Dir,
+496    },
+497}
+498
+499/// Indicates whether a frame needs to be transmitted
+500///
+501/// This type wraps around bool and uses the `#[must_use]` attribute in order
+502/// to prevent accidental loss of the frame transmission requirement.
+503#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
+504#[must_use = "A frame might need to be enqueued"]
+505pub struct ShouldTransmit(bool);
+506
+507impl ShouldTransmit {
+508    /// Returns whether a frame should be transmitted
+509    pub fn should_transmit(self) -> bool {
+510        self.0
+511    }
+512}
+513
+514/// Error indicating that a stream has not been opened or has already been finished or reset
+515#[derive(Debug, Default, Error, Clone, PartialEq, Eq)]
+516#[error("closed stream")]
+517pub struct ClosedStream {
+518    _private: (),
+519}
+520
+521impl From<ClosedStream> for io::Error {
+522    fn from(x: ClosedStream) -> Self {
+523        Self::new(io::ErrorKind::NotConnected, x)
+524    }
+525}
+526
+527#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+528enum StreamHalf {
+529    Send,
+530    Recv,
+531}
+532
+533/// A helper trait to unify Bytes, Vec<u8> and &[u8] as sources of bytes
+534pub(super) trait BytesOrSlice<'a>: AsRef<[u8]> + 'a {
+535    fn len(&self) -> usize {
+536        self.as_ref().len()
+537    }
+538    fn is_empty(&self) -> bool {
+539        self.as_ref().is_empty()
+540    }
+541    fn into_bytes(self) -> Bytes;
+542}
+543
+544impl BytesOrSlice<'_> for Bytes {
+545    fn into_bytes(self) -> Bytes {
+546        self
+547    }
+548}
+549
+550impl<'a> BytesOrSlice<'a> for &'a [u8] {
+551    fn into_bytes(self) -> Bytes {
+552        Bytes::copy_from_slice(self)
+553    }
+554}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/streams/recv.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/streams/recv.rs.html new file mode 100644 index 000000000..80fd97bc1 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/streams/recv.rs.html @@ -0,0 +1,544 @@ +recv.rs - source

iroh_quinn_proto/connection/streams/
recv.rs

1use std::collections::hash_map::Entry;
+2use std::mem;
+3
+4use thiserror::Error;
+5use tracing::debug;
+6
+7use super::state::get_or_insert_recv;
+8use super::{ClosedStream, Retransmits, ShouldTransmit, StreamId, StreamsState};
+9use crate::connection::assembler::{Assembler, Chunk, IllegalOrderedRead};
+10use crate::connection::streams::state::StreamRecv;
+11use crate::{TransportError, VarInt, frame};
+12
+13#[derive(Debug, Default)]
+14pub(super) struct Recv {
+15    // NB: when adding or removing fields, remember to update `reinit`.
+16    state: RecvState,
+17    pub(super) assembler: Assembler,
+18    sent_max_stream_data: u64,
+19    pub(super) end: u64,
+20    pub(super) stopped: bool,
+21}
+22
+23impl Recv {
+24    pub(super) fn new(initial_max_data: u64) -> Box<Self> {
+25        Box::new(Self {
+26            state: RecvState::default(),
+27            assembler: Assembler::new(),
+28            sent_max_stream_data: initial_max_data,
+29            end: 0,
+30            stopped: false,
+31        })
+32    }
+33
+34    /// Reset to the initial state
+35    pub(super) fn reinit(&mut self, initial_max_data: u64) {
+36        self.state = RecvState::default();
+37        self.assembler.reinit();
+38        self.sent_max_stream_data = initial_max_data;
+39        self.end = 0;
+40        self.stopped = false;
+41    }
+42
+43    /// Process a STREAM frame
+44    ///
+45    /// Return value is `(number_of_new_bytes_ingested, stream_is_closed)`
+46    pub(super) fn ingest(
+47        &mut self,
+48        frame: frame::Stream,
+49        payload_len: usize,
+50        received: u64,
+51        max_data: u64,
+52    ) -> Result<(u64, bool), TransportError> {
+53        let end = frame.offset + frame.data.len() as u64;
+54        if end >= 2u64.pow(62) {
+55            return Err(TransportError::FLOW_CONTROL_ERROR(
+56                "maximum stream offset too large",
+57            ));
+58        }
+59
+60        if let Some(final_offset) = self.final_offset()
+61            && (end > final_offset || (frame.fin && end != final_offset))
+62        {
+63            debug!(end, final_offset, "final size error");
+64            return Err(TransportError::FINAL_SIZE_ERROR(""));
+65        }
+66
+67        let new_bytes = self.credit_consumed_by(end, received, max_data)?;
+68
+69        // Stopped streams don't need to wait for the actual data, they just need to know
+70        // how much there was.
+71        if frame.fin
+72            && !self.stopped
+73            && let RecvState::Recv { ref mut size } = self.state
+74        {
+75            *size = Some(end);
+76        }
+77
+78        self.end = self.end.max(end);
+79        // Don't bother storing data or releasing stream-level flow control credit if the stream's
+80        // already stopped
+81        if !self.stopped {
+82            self.assembler.insert(frame.offset, frame.data, payload_len);
+83        }
+84
+85        Ok((new_bytes, frame.fin && self.stopped))
+86    }
+87
+88    pub(super) fn stop(&mut self) -> Result<(u64, ShouldTransmit), ClosedStream> {
+89        if self.stopped {
+90            return Err(ClosedStream { _private: () });
+91        }
+92
+93        self.stopped = true;
+94        self.assembler.clear();
+95        // Issue flow control credit for unread data
+96        let read_credits = self.end - self.assembler.bytes_read();
+97        // This may send a spurious STOP_SENDING if we've already received all data, but it's a bit
+98        // fiddly to distinguish that from the case where we've received a FIN but are missing some
+99        // data that the peer might still be trying to retransmit, in which case a STOP_SENDING is
+100        // still useful.
+101        Ok((read_credits, ShouldTransmit(self.is_receiving())))
+102    }
+103
+104    /// Returns the window that should be advertised in a `MAX_STREAM_DATA` frame
+105    ///
+106    /// The method returns a tuple which consists of the window that should be
+107    /// announced, as well as a boolean parameter which indicates if a new
+108    /// transmission of the value is recommended. If the boolean value is
+109    /// `false` the new window should only be transmitted if a previous transmission
+110    /// had failed.
+111    pub(super) fn max_stream_data(&mut self, stream_receive_window: u64) -> (u64, ShouldTransmit) {
+112        let max_stream_data = self.assembler.bytes_read() + stream_receive_window;
+113
+114        // Only announce a window update if it's significant enough
+115        // to make it worthwhile sending a MAX_STREAM_DATA frame.
+116        // We use here a fraction of the configured stream receive window to make
+117        // the decision, and accommodate for streams using bigger windows requiring
+118        // less updates. A fixed size would also work - but it would need to be
+119        // smaller than `stream_receive_window` in order to make sure the stream
+120        // does not get stuck.
+121        let diff = max_stream_data - self.sent_max_stream_data;
+122        let transmit = self.can_send_flow_control() && diff >= (stream_receive_window / 8);
+123        (max_stream_data, ShouldTransmit(transmit))
+124    }
+125
+126    /// Records that a `MAX_STREAM_DATA` announcing a certain window was sent
+127    ///
+128    /// This will suppress enqueuing further `MAX_STREAM_DATA` frames unless
+129    /// either the previous transmission was not acknowledged or the window
+130    /// further increased.
+131    pub(super) fn record_sent_max_stream_data(&mut self, sent_value: u64) {
+132        if sent_value > self.sent_max_stream_data {
+133            self.sent_max_stream_data = sent_value;
+134        }
+135    }
+136
+137    /// Whether the total amount of data that the peer will send on this stream is unknown
+138    ///
+139    /// True until we've received either a reset or the final frame.
+140    ///
+141    /// Implies that the sender might benefit from stream-level flow control updates, and we might
+142    /// need to issue connection-level flow control updates due to flow control budget use by this
+143    /// stream in the future, even if it's been stopped.
+144    pub(super) fn final_offset_unknown(&self) -> bool {
+145        matches!(self.state, RecvState::Recv { size: None })
+146    }
+147
+148    /// Whether stream-level flow control updates should be sent for this stream
+149    pub(super) fn can_send_flow_control(&self) -> bool {
+150        // Stream-level flow control is redundant if the sender has already sent the whole stream,
+151        // and moot if we no longer want data on this stream.
+152        self.final_offset_unknown() && !self.stopped
+153    }
+154
+155    /// Whether data is still being accepted from the peer
+156    pub(super) fn is_receiving(&self) -> bool {
+157        matches!(self.state, RecvState::Recv { .. })
+158    }
+159
+160    fn final_offset(&self) -> Option<u64> {
+161        match self.state {
+162            RecvState::Recv { size } => size,
+163            RecvState::ResetRecvd { size, .. } => Some(size),
+164        }
+165    }
+166
+167    /// Returns `false` iff the reset was redundant
+168    pub(super) fn reset(
+169        &mut self,
+170        error_code: VarInt,
+171        final_offset: VarInt,
+172        received: u64,
+173        max_data: u64,
+174    ) -> Result<bool, TransportError> {
+175        // Validate final_offset
+176        if let Some(offset) = self.final_offset() {
+177            if offset != final_offset.into_inner() {
+178                return Err(TransportError::FINAL_SIZE_ERROR("inconsistent value"));
+179            }
+180        } else if self.end > u64::from(final_offset) {
+181            return Err(TransportError::FINAL_SIZE_ERROR(
+182                "lower than high water mark",
+183            ));
+184        }
+185        self.credit_consumed_by(final_offset.into(), received, max_data)?;
+186
+187        if matches!(self.state, RecvState::ResetRecvd { .. }) {
+188            return Ok(false);
+189        }
+190        self.state = RecvState::ResetRecvd {
+191            size: final_offset.into(),
+192            error_code,
+193        };
+194        // Nuke buffers so that future reads fail immediately, which ensures future reads don't
+195        // issue flow control credit redundant to that already issued. We could instead special-case
+196        // reset streams during read, but it's unclear if there's any benefit to retaining data for
+197        // reset streams.
+198        self.assembler.clear();
+199        Ok(true)
+200    }
+201
+202    pub(super) fn reset_code(&self) -> Option<VarInt> {
+203        match self.state {
+204            RecvState::ResetRecvd { error_code, .. } => Some(error_code),
+205            _ => None,
+206        }
+207    }
+208
+209    /// Compute the amount of flow control credit consumed, or return an error if more was consumed
+210    /// than issued
+211    fn credit_consumed_by(
+212        &self,
+213        offset: u64,
+214        received: u64,
+215        max_data: u64,
+216    ) -> Result<u64, TransportError> {
+217        let prev_end = self.end;
+218        let new_bytes = offset.saturating_sub(prev_end);
+219        if offset > self.sent_max_stream_data || received + new_bytes > max_data {
+220            debug!(
+221                received,
+222                new_bytes,
+223                max_data,
+224                offset,
+225                stream_max_data = self.sent_max_stream_data,
+226                "flow control error"
+227            );
+228            return Err(TransportError::FLOW_CONTROL_ERROR(""));
+229        }
+230
+231        Ok(new_bytes)
+232    }
+233}
+234
+235/// Chunks returned from [`RecvStream::read()`][crate::RecvStream::read].
+236///
+237/// ### Note: Finalization Needed
+238/// Bytes read from the stream are not released from the congestion window until
+239/// either [`Self::finalize()`] is called, or this type is dropped.
+240///
+241/// It is recommended that you call [`Self::finalize()`] because it returns a flag
+242/// telling you whether reading from the stream has resulted in the need to transmit a packet.
+243///
+244/// If this type is leaked, the stream will remain blocked on the remote peer until
+245/// another read from the stream is done.
+246pub struct Chunks<'a> {
+247    id: StreamId,
+248    ordered: bool,
+249    streams: &'a mut StreamsState,
+250    pending: &'a mut Retransmits,
+251    state: ChunksState,
+252    read: u64,
+253}
+254
+255impl<'a> Chunks<'a> {
+256    pub(super) fn new(
+257        id: StreamId,
+258        ordered: bool,
+259        streams: &'a mut StreamsState,
+260        pending: &'a mut Retransmits,
+261    ) -> Result<Self, ReadableError> {
+262        let mut entry = match streams.recv.entry(id) {
+263            Entry::Occupied(entry) => entry,
+264            Entry::Vacant(_) => return Err(ReadableError::ClosedStream),
+265        };
+266
+267        let mut recv =
+268            match get_or_insert_recv(streams.stream_receive_window)(entry.get_mut()).stopped {
+269                true => return Err(ReadableError::ClosedStream),
+270                false => entry.remove().unwrap().into_inner(), // this can't fail due to the previous get_or_insert_with
+271            };
+272
+273        recv.assembler.ensure_ordering(ordered)?;
+274        Ok(Self {
+275            id,
+276            ordered,
+277            streams,
+278            pending,
+279            state: ChunksState::Readable(recv),
+280            read: 0,
+281        })
+282    }
+283
+284    /// Next
+285    ///
+286    /// Should call finalize() when done calling this.
+287    pub fn next(&mut self, max_length: usize) -> Result<Option<Chunk>, ReadError> {
+288        let rs = match self.state {
+289            ChunksState::Readable(ref mut rs) => rs,
+290            ChunksState::Reset(error_code) => {
+291                return Err(ReadError::Reset(error_code));
+292            }
+293            ChunksState::Finished => {
+294                return Ok(None);
+295            }
+296            ChunksState::Finalized => panic!("must not call next() after finalize()"),
+297        };
+298
+299        if let Some(chunk) = rs.assembler.read(max_length, self.ordered) {
+300            self.read += chunk.bytes.len() as u64;
+301            return Ok(Some(chunk));
+302        }
+303
+304        match rs.state {
+305            RecvState::ResetRecvd { error_code, .. } => {
+306                debug_assert_eq!(self.read, 0, "reset streams have empty buffers");
+307                let state = mem::replace(&mut self.state, ChunksState::Reset(error_code));
+308                // At this point if we have `rs` self.state must be `ChunksState::Readable`
+309                let recv = match state {
+310                    ChunksState::Readable(recv) => StreamRecv::Open(recv),
+311                    _ => unreachable!("state must be ChunkState::Readable"),
+312                };
+313                self.streams.stream_recv_freed(self.id, recv);
+314                Err(ReadError::Reset(error_code))
+315            }
+316            RecvState::Recv { size } => {
+317                if size == Some(rs.end) && rs.assembler.bytes_read() == rs.end {
+318                    let state = mem::replace(&mut self.state, ChunksState::Finished);
+319                    // At this point if we have `rs` self.state must be `ChunksState::Readable`
+320                    let recv = match state {
+321                        ChunksState::Readable(recv) => StreamRecv::Open(recv),
+322                        _ => unreachable!("state must be ChunkState::Readable"),
+323                    };
+324                    self.streams.stream_recv_freed(self.id, recv);
+325                    Ok(None)
+326                } else {
+327                    // We don't need a distinct `ChunksState` variant for a blocked stream because
+328                    // retrying a read harmlessly re-traces our steps back to returning
+329                    // `Err(Blocked)` again. The buffers can't refill and the stream's own state
+330                    // can't change so long as this `Chunks` exists.
+331                    Err(ReadError::Blocked)
+332                }
+333            }
+334        }
+335    }
+336
+337    /// Mark the read data as consumed from the stream.
+338    ///
+339    /// The number of read bytes will be released from the congestion window,
+340    /// allowing the remote peer to send more data if it was previously blocked.
+341    ///
+342    /// If [`ShouldTransmit::should_transmit()`] returns `true`,
+343    /// a packet needs to be sent to the peer informing them that the stream is unblocked.
+344    /// This means that you should call [`Connection::poll_transmit()`][crate::Connection::poll_transmit]
+345    /// and send the returned packet as soon as is reasonable, to unblock the remote peer.
+346    pub fn finalize(mut self) -> ShouldTransmit {
+347        self.finalize_inner()
+348    }
+349
+350    fn finalize_inner(&mut self) -> ShouldTransmit {
+351        let state = mem::replace(&mut self.state, ChunksState::Finalized);
+352        if let ChunksState::Finalized = state {
+353            // Noop on repeated calls
+354            return ShouldTransmit(false);
+355        }
+356
+357        // We issue additional stream ID credit after the application is notified that a previously
+358        // open stream has finished or been reset and we've therefore disposed of its state, as
+359        // recorded by `stream_freed` calls in `next`.
+360        let mut should_transmit = self.streams.queue_max_stream_id(self.pending);
+361
+362        // If the stream hasn't finished, we may need to issue stream-level flow control credit
+363        if let ChunksState::Readable(mut rs) = state {
+364            let (_, max_stream_data) = rs.max_stream_data(self.streams.stream_receive_window);
+365            should_transmit |= max_stream_data.0;
+366            if max_stream_data.0 {
+367                self.pending.max_stream_data.insert(self.id);
+368            }
+369            // Return the stream to storage for future use
+370            self.streams
+371                .recv
+372                .insert(self.id, Some(StreamRecv::Open(rs)));
+373        }
+374
+375        // Issue connection-level flow control credit for any data we read regardless of state
+376        let max_data = self.streams.add_read_credits(self.read);
+377        self.pending.max_data |= max_data.0;
+378        should_transmit |= max_data.0;
+379        ShouldTransmit(should_transmit)
+380    }
+381}
+382
+383impl Drop for Chunks<'_> {
+384    fn drop(&mut self) {
+385        let _ = self.finalize_inner();
+386    }
+387}
+388
+389enum ChunksState {
+390    Readable(Box<Recv>),
+391    Reset(VarInt),
+392    Finished,
+393    Finalized,
+394}
+395
+396/// Errors triggered when reading from a recv stream
+397#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+398pub enum ReadError {
+399    /// No more data is currently available on this stream.
+400    ///
+401    /// If more data on this stream is received from the peer, an `Event::StreamReadable` will be
+402    /// generated for this stream, indicating that retrying the read might succeed.
+403    #[error("blocked")]
+404    Blocked,
+405    /// The peer abandoned transmitting data on this stream.
+406    ///
+407    /// Carries an application-defined error code.
+408    #[error("reset by peer: code {0}")]
+409    Reset(VarInt),
+410}
+411
+412/// Errors triggered when opening a recv stream for reading
+413#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+414pub enum ReadableError {
+415    /// The stream has not been opened or was already stopped, finished, or reset
+416    #[error("closed stream")]
+417    ClosedStream,
+418    /// Attempted an ordered read following an unordered read
+419    ///
+420    /// Performing an unordered read allows discontinuities to arise in the receive buffer of a
+421    /// stream which cannot be recovered, making further ordered reads impossible.
+422    #[error("ordered read after unordered read")]
+423    IllegalOrderedRead,
+424}
+425
+426impl From<IllegalOrderedRead> for ReadableError {
+427    fn from(_: IllegalOrderedRead) -> Self {
+428        Self::IllegalOrderedRead
+429    }
+430}
+431
+432#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+433enum RecvState {
+434    Recv { size: Option<u64> },
+435    ResetRecvd { size: u64, error_code: VarInt },
+436}
+437
+438impl Default for RecvState {
+439    fn default() -> Self {
+440        Self::Recv { size: None }
+441    }
+442}
+443
+444#[cfg(test)]
+445mod tests {
+446    use bytes::Bytes;
+447
+448    use crate::{Dir, Side};
+449
+450    use super::*;
+451
+452    #[test]
+453    fn reordered_frames_while_stopped() {
+454        const INITIAL_BYTES: u64 = 3;
+455        const INITIAL_OFFSET: u64 = 3;
+456        const RECV_WINDOW: u64 = 8;
+457        let mut s = Recv::new(RECV_WINDOW);
+458        let mut data_recvd = 0;
+459        // Receive bytes 3..6
+460        let (new_bytes, is_closed) = s
+461            .ingest(
+462                frame::Stream {
+463                    id: StreamId::new(Side::Client, Dir::Uni, 0),
+464                    offset: INITIAL_OFFSET,
+465                    fin: false,
+466                    data: Bytes::from_static(&[0; INITIAL_BYTES as usize]),
+467                },
+468                123,
+469                data_recvd,
+470                data_recvd + 1024,
+471            )
+472            .unwrap();
+473        data_recvd += new_bytes;
+474        assert_eq!(new_bytes, INITIAL_OFFSET + INITIAL_BYTES);
+475        assert!(!is_closed);
+476
+477        let (credits, transmit) = s.stop().unwrap();
+478        assert!(transmit.should_transmit());
+479        assert_eq!(
+480            credits,
+481            INITIAL_OFFSET + INITIAL_BYTES,
+482            "full connection flow control credit is issued by stop"
+483        );
+484
+485        let (max_stream_data, transmit) = s.max_stream_data(RECV_WINDOW);
+486        assert!(!transmit.should_transmit());
+487        assert_eq!(
+488            max_stream_data, RECV_WINDOW,
+489            "stream flow control credit isn't issued by stop"
+490        );
+491
+492        // Receive byte 7
+493        let (new_bytes, is_closed) = s
+494            .ingest(
+495                frame::Stream {
+496                    id: StreamId::new(Side::Client, Dir::Uni, 0),
+497                    offset: RECV_WINDOW - 1,
+498                    fin: false,
+499                    data: Bytes::from_static(&[0; 1]),
+500                },
+501                123,
+502                data_recvd,
+503                data_recvd + 1024,
+504            )
+505            .unwrap();
+506        data_recvd += new_bytes;
+507        assert_eq!(new_bytes, RECV_WINDOW - (INITIAL_OFFSET + INITIAL_BYTES));
+508        assert!(!is_closed);
+509
+510        let (max_stream_data, transmit) = s.max_stream_data(RECV_WINDOW);
+511        assert!(!transmit.should_transmit());
+512        assert_eq!(
+513            max_stream_data, RECV_WINDOW,
+514            "stream flow control credit isn't issued after stop"
+515        );
+516
+517        // Receive bytes 0..3
+518        let (new_bytes, is_closed) = s
+519            .ingest(
+520                frame::Stream {
+521                    id: StreamId::new(Side::Client, Dir::Uni, 0),
+522                    offset: 0,
+523                    fin: false,
+524                    data: Bytes::from_static(&[0; INITIAL_OFFSET as usize]),
+525                },
+526                123,
+527                data_recvd,
+528                data_recvd + 1024,
+529            )
+530            .unwrap();
+531        assert_eq!(
+532            new_bytes, 0,
+533            "reordered frames don't issue connection-level flow control for stopped streams"
+534        );
+535        assert!(!is_closed);
+536
+537        let (max_stream_data, transmit) = s.max_stream_data(RECV_WINDOW);
+538        assert!(!transmit.should_transmit());
+539        assert_eq!(
+540            max_stream_data, RECV_WINDOW,
+541            "stream flow control credit isn't issued after stop"
+542        );
+543    }
+544}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/streams/send.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/streams/send.rs.html new file mode 100644 index 000000000..4d8f979d7 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/streams/send.rs.html @@ -0,0 +1,414 @@ +send.rs - source

iroh_quinn_proto/connection/streams/
send.rs

1use bytes::Bytes;
+2use thiserror::Error;
+3
+4use crate::{
+5    VarInt,
+6    connection::{send_buffer::SendBuffer, streams::BytesOrSlice},
+7    frame,
+8};
+9
+10#[derive(Debug)]
+11pub(super) struct Send {
+12    pub(super) max_data: u64,
+13    pub(super) state: SendState,
+14    pub(super) pending: SendBuffer,
+15    pub(super) priority: i32,
+16    /// Whether a frame containing a FIN bit must be transmitted, even if we don't have any new data
+17    pub(super) fin_pending: bool,
+18    /// Whether this stream is in the `connection_blocked` list of `Streams`
+19    pub(super) connection_blocked: bool,
+20    /// The reason the peer wants us to stop, if `STOP_SENDING` was received
+21    pub(super) stop_reason: Option<VarInt>,
+22}
+23
+24impl Send {
+25    pub(super) fn new(max_data: VarInt) -> Box<Self> {
+26        Box::new(Self {
+27            max_data: max_data.into(),
+28            state: SendState::Ready,
+29            pending: SendBuffer::new(),
+30            priority: 0,
+31            fin_pending: false,
+32            connection_blocked: false,
+33            stop_reason: None,
+34        })
+35    }
+36
+37    /// Whether the stream has been reset
+38    pub(super) fn is_reset(&self) -> bool {
+39        matches!(self.state, SendState::ResetSent)
+40    }
+41
+42    pub(super) fn finish(&mut self) -> Result<(), FinishError> {
+43        if let Some(error_code) = self.stop_reason {
+44            Err(FinishError::Stopped(error_code))
+45        } else if self.state == SendState::Ready {
+46            self.state = SendState::DataSent {
+47                finish_acked: false,
+48            };
+49            self.fin_pending = true;
+50            Ok(())
+51        } else {
+52            Err(FinishError::ClosedStream)
+53        }
+54    }
+55
+56    pub(super) fn write<'a, S: BytesSource<'a>>(
+57        &mut self,
+58        source: &'a mut S,
+59        limit: u64,
+60    ) -> Result<Written, WriteError> {
+61        if !self.is_writable() {
+62            return Err(WriteError::ClosedStream);
+63        }
+64        if let Some(error_code) = self.stop_reason {
+65            return Err(WriteError::Stopped(error_code));
+66        }
+67        let budget = self.max_data - self.pending.offset();
+68        if budget == 0 {
+69            return Err(WriteError::Blocked);
+70        }
+71        let mut limit = limit.min(budget) as usize;
+72
+73        let mut result = Written::default();
+74        loop {
+75            let (chunk, chunks_consumed) = source.pop_chunk(limit);
+76            result.chunks += chunks_consumed;
+77            result.bytes += chunk.len();
+78
+79            if chunk.is_empty() {
+80                break;
+81            }
+82
+83            limit -= chunk.len();
+84            self.pending.write(chunk);
+85        }
+86
+87        Ok(result)
+88    }
+89
+90    /// Update stream state due to a reset sent by the local application
+91    pub(super) fn reset(&mut self) {
+92        use SendState::*;
+93        if let DataSent { .. } | Ready = self.state {
+94            self.state = ResetSent;
+95        }
+96    }
+97
+98    /// Handle STOP_SENDING
+99    ///
+100    /// Returns true if the stream was stopped due to this frame, and false
+101    /// if it had been stopped before
+102    pub(super) fn try_stop(&mut self, error_code: VarInt) -> bool {
+103        if self.stop_reason.is_none() {
+104            self.stop_reason = Some(error_code);
+105            true
+106        } else {
+107            false
+108        }
+109    }
+110
+111    /// Returns whether the stream has been finished and all data has been acknowledged by the peer
+112    pub(super) fn ack(&mut self, frame: frame::StreamMeta) -> bool {
+113        self.pending.ack(frame.offsets);
+114        match self.state {
+115            SendState::DataSent {
+116                ref mut finish_acked,
+117            } => {
+118                *finish_acked |= frame.fin;
+119                *finish_acked && self.pending.is_fully_acked()
+120            }
+121            _ => false,
+122        }
+123    }
+124
+125    /// Handle increase to stream-level flow control limit
+126    ///
+127    /// Returns whether the stream was unblocked
+128    pub(super) fn increase_max_data(&mut self, offset: u64) -> bool {
+129        if offset <= self.max_data || self.state != SendState::Ready {
+130            return false;
+131        }
+132        let was_blocked = self.pending.offset() == self.max_data;
+133        self.max_data = offset;
+134        was_blocked
+135    }
+136
+137    pub(super) fn offset(&self) -> u64 {
+138        self.pending.offset()
+139    }
+140
+141    pub(super) fn is_pending(&self) -> bool {
+142        self.pending.has_unsent_data() || self.fin_pending
+143    }
+144
+145    pub(super) fn is_writable(&self) -> bool {
+146        matches!(self.state, SendState::Ready)
+147    }
+148}
+149
+150/// A [`BytesSource`] implementation for `&'a mut [Bytes]`
+151///
+152/// The type allows to dequeue [`Bytes`] chunks from an array of chunks, up to
+153/// a configured limit.
+154pub(crate) struct BytesArray<'a> {
+155    /// The wrapped slice of `Bytes`
+156    chunks: &'a mut [Bytes],
+157    /// The amount of chunks consumed from this source
+158    consumed: usize,
+159}
+160
+161impl<'a> BytesArray<'a> {
+162    pub(crate) fn from_chunks(chunks: &'a mut [Bytes]) -> Self {
+163        Self {
+164            chunks,
+165            consumed: 0,
+166        }
+167    }
+168}
+169
+170impl<'a> BytesSource<'a> for BytesArray<'a> {
+171    fn pop_chunk<'b>(&'b mut self, limit: usize) -> (impl BytesOrSlice<'b>, usize)
+172    where
+173        'a: 'b,
+174    {
+175        // The loop exists to skip empty chunks while still marking them as
+176        // consumed
+177        let mut chunks_consumed = 0;
+178
+179        while self.consumed < self.chunks.len() {
+180            let chunk = &mut self.chunks[self.consumed];
+181
+182            if chunk.len() <= limit {
+183                let chunk = std::mem::take(chunk);
+184                self.consumed += 1;
+185                chunks_consumed += 1;
+186                if chunk.is_empty() {
+187                    continue;
+188                }
+189                return (chunk, chunks_consumed);
+190            } else if limit > 0 {
+191                let chunk = chunk.split_to(limit);
+192                return (chunk, chunks_consumed);
+193            } else {
+194                break;
+195            }
+196        }
+197
+198        (Bytes::new(), chunks_consumed)
+199    }
+200}
+201
+202/// A [`BytesSource`] implementation for `&[u8]`
+203///
+204/// The type allows to dequeue a single [`Bytes`] chunk, which will be lazily
+205/// created from a reference. This allows to defer the allocation until it is
+206/// known how much data needs to be copied.
+207pub(crate) struct ByteSlice<'a> {
+208    /// The wrapped byte slice
+209    data: &'a [u8],
+210}
+211
+212impl<'a> ByteSlice<'a> {
+213    pub(crate) fn from_slice(data: &'a [u8]) -> Self {
+214        Self { data }
+215    }
+216}
+217
+218impl<'a> BytesSource<'a> for ByteSlice<'a> {
+219    fn pop_chunk<'b>(&'b mut self, limit: usize) -> (impl BytesOrSlice<'b>, usize)
+220    where
+221        'a: 'b,
+222    {
+223        let limit = limit.min(self.data.len());
+224        if limit == 0 {
+225            return (&[][..], 0);
+226        }
+227
+228        let chunk = &self.data[..limit];
+229        self.data = &self.data[chunk.len()..];
+230
+231        let chunks_consumed = usize::from(self.data.is_empty());
+232        (chunk, chunks_consumed)
+233    }
+234}
+235
+236/// A source of one or more buffers which can be converted into `Bytes` buffers on demand
+237///
+238/// The purpose of this data type is to defer conversion as long as possible,
+239/// so that no heap allocation is required in case no data is writable.
+240pub(super) trait BytesSource<'a> {
+241    /// Returns the next chunk from the source of owned chunks.
+242    ///
+243    /// This method will consume parts of the source.
+244    /// Calling it will yield `Bytes` elements up to the configured `limit`.
+245    ///
+246    /// The method returns a tuple:
+247    /// - The first item is the yielded `Bytes` element. The element will be
+248    ///   empty if the limit is zero or no more data is available.
+249    /// - The second item returns how many complete chunks inside the source had
+250    ///   had been consumed. This can be less than 1, if a chunk inside the
+251    ///   source had been truncated in order to adhere to the limit. It can also
+252    ///   be more than 1, if zero-length chunks had been skipped.
+253    fn pop_chunk<'b>(&'b mut self, limit: usize) -> (impl BytesOrSlice<'b>, usize)
+254    where
+255        'a: 'b;
+256}
+257
+258/// Indicates how many bytes and chunks had been transferred in a write operation
+259#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
+260pub struct Written {
+261    /// The amount of bytes which had been written
+262    pub bytes: usize,
+263    /// The amount of full chunks which had been written
+264    ///
+265    /// If a chunk was only partially written, it will not be counted by this field.
+266    pub chunks: usize,
+267}
+268
+269/// Errors triggered while writing to a send stream
+270#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+271pub enum WriteError {
+272    /// The peer is not able to accept additional data, or the connection is congested.
+273    ///
+274    /// If the peer issues additional flow control credit, a [`StreamEvent::Writable`] event will
+275    /// be generated, indicating that retrying the write might succeed.
+276    ///
+277    /// [`StreamEvent::Writable`]: crate::StreamEvent::Writable
+278    #[error("unable to accept further writes")]
+279    Blocked,
+280    /// The peer is no longer accepting data on this stream, and it has been implicitly reset. The
+281    /// stream cannot be finished or further written to.
+282    ///
+283    /// Carries an application-defined error code.
+284    ///
+285    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+286    #[error("stopped by peer: code {0}")]
+287    Stopped(VarInt),
+288    /// The stream has not been opened or has already been finished or reset
+289    #[error("closed stream")]
+290    ClosedStream,
+291}
+292
+293#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+294pub(super) enum SendState {
+295    /// Sending new data
+296    Ready,
+297    /// Stream was finished; now sending retransmits only
+298    DataSent { finish_acked: bool },
+299    /// Sent RESET
+300    ResetSent,
+301}
+302
+303/// Reasons why attempting to finish a stream might fail
+304#[derive(Debug, Error, Clone, PartialEq, Eq)]
+305pub enum FinishError {
+306    /// The peer is no longer accepting data on this stream. No
+307    /// [`StreamEvent::Finished`] event will be emitted for this stream.
+308    ///
+309    /// Carries an application-defined error code.
+310    ///
+311    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+312    #[error("stopped by peer: code {0}")]
+313    Stopped(VarInt),
+314    /// The stream has not been opened or was already finished or reset
+315    #[error("closed stream")]
+316    ClosedStream,
+317}
+318
+319#[cfg(test)]
+320mod tests {
+321    use super::*;
+322
+323    #[test]
+324    fn bytes_array() {
+325        let full = b"Hello World 123456789 ABCDEFGHJIJKLMNOPQRSTUVWXYZ".to_owned();
+326        for limit in 0..full.len() {
+327            let mut chunks = [
+328                Bytes::from_static(b""),
+329                Bytes::from_static(b"Hello "),
+330                Bytes::from_static(b"Wo"),
+331                Bytes::from_static(b""),
+332                Bytes::from_static(b"r"),
+333                Bytes::from_static(b"ld"),
+334                Bytes::from_static(b""),
+335                Bytes::from_static(b" 12345678"),
+336                Bytes::from_static(b"9 ABCDE"),
+337                Bytes::from_static(b"F"),
+338                Bytes::from_static(b"GHJIJKLMNOPQRSTUVWXYZ"),
+339            ];
+340            let num_chunks = chunks.len();
+341            let last_chunk_len = chunks[chunks.len() - 1].len();
+342
+343            let mut array = BytesArray::from_chunks(&mut chunks);
+344
+345            let mut buf = Vec::new();
+346            let mut chunks_popped = 0;
+347            let mut chunks_consumed = 0;
+348            let mut remaining = limit;
+349            loop {
+350                let (chunk, consumed) = array.pop_chunk(remaining);
+351                chunks_consumed += consumed;
+352
+353                if !chunk.is_empty() {
+354                    buf.extend_from_slice(chunk.as_ref());
+355                    remaining -= chunk.len();
+356                    chunks_popped += 1;
+357                } else {
+358                    break;
+359                }
+360            }
+361
+362            assert_eq!(&buf[..], &full[..limit]);
+363
+364            if limit == full.len() {
+365                // Full consumption of the last chunk
+366                assert_eq!(chunks_consumed, num_chunks);
+367                // Since there are empty chunks, we consume more than there are popped
+368                assert_eq!(chunks_consumed, chunks_popped + 3);
+369            } else if limit > full.len() - last_chunk_len {
+370                // Partial consumption of the last chunk
+371                assert_eq!(chunks_consumed, num_chunks - 1);
+372                assert_eq!(chunks_consumed, chunks_popped + 2);
+373            }
+374        }
+375    }
+376
+377    #[test]
+378    fn byte_slice() {
+379        let full = b"Hello World 123456789 ABCDEFGHJIJKLMNOPQRSTUVWXYZ".to_owned();
+380        for limit in 0..full.len() {
+381            let mut array = ByteSlice::from_slice(&full[..]);
+382
+383            let mut buf = Vec::new();
+384            let mut chunks_popped = 0;
+385            let mut chunks_consumed = 0;
+386            let mut remaining = limit;
+387            loop {
+388                let (chunk, consumed) = array.pop_chunk(remaining);
+389                chunks_consumed += consumed;
+390
+391                if !chunk.is_empty() {
+392                    buf.extend_from_slice(chunk.as_ref());
+393                    remaining -= chunk.len();
+394                    chunks_popped += 1;
+395                } else {
+396                    break;
+397                }
+398            }
+399
+400            assert_eq!(&buf[..], &full[..limit]);
+401            if limit != 0 {
+402                assert_eq!(chunks_popped, 1);
+403            } else {
+404                assert_eq!(chunks_popped, 0);
+405            }
+406
+407            if limit == full.len() {
+408                assert_eq!(chunks_consumed, 1);
+409            } else {
+410                assert_eq!(chunks_consumed, 0);
+411            }
+412        }
+413    }
+414}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/streams/state.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/streams/state.rs.html new file mode 100644 index 000000000..dce69a065 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/streams/state.rs.html @@ -0,0 +1,2050 @@ +state.rs - source

iroh_quinn_proto/connection/streams/
state.rs

1use std::{
+2    collections::{VecDeque, hash_map},
+3    convert::TryFrom,
+4    mem,
+5};
+6
+7use rustc_hash::FxHashMap;
+8use tracing::{debug, trace};
+9
+10use super::{
+11    PendingStreamsQueue, Recv, Retransmits, Send, SendState, ShouldTransmit, StreamEvent,
+12    StreamHalf,
+13};
+14use crate::{
+15    Dir, MAX_STREAM_COUNT, Side, StreamId, TransportError, VarInt,
+16    connection::{PacketBuilder, stats::FrameStats},
+17    frame::{self, FrameStruct},
+18    transport_parameters::TransportParameters,
+19};
+20
+21/// Wrapper around `Recv` that facilitates reusing `Recv` instances
+22#[derive(Debug)]
+23pub(super) enum StreamRecv {
+24    /// A `Recv` that is ready to be opened
+25    Free(Box<Recv>),
+26    /// A `Recv` that has been opened
+27    Open(Box<Recv>),
+28}
+29
+30impl StreamRecv {
+31    /// Returns a reference to the inner `Recv` if the stream is open
+32    pub(super) fn as_open_recv(&self) -> Option<&Recv> {
+33        match self {
+34            Self::Open(r) => Some(r),
+35            _ => None,
+36        }
+37    }
+38
+39    // Returns a mutable reference to the inner `Recv` if the stream is open
+40    pub(super) fn as_open_recv_mut(&mut self) -> Option<&mut Recv> {
+41        match self {
+42            Self::Open(r) => Some(r),
+43            _ => None,
+44        }
+45    }
+46
+47    // Returns the inner `Recv`
+48    pub(super) fn into_inner(self) -> Box<Recv> {
+49        match self {
+50            Self::Free(r) | Self::Open(r) => r,
+51        }
+52    }
+53
+54    // Reinitialize the stream so the inner `Recv` can be reused
+55    pub(super) fn free(self, initial_max_data: u64) -> Self {
+56        match self {
+57            Self::Free(_) => unreachable!("Self::Free on reinit()"),
+58            Self::Open(mut recv) => {
+59                recv.reinit(initial_max_data);
+60                Self::Free(recv)
+61            }
+62        }
+63    }
+64}
+65
+66#[allow(unreachable_pub)] // fuzzing only
+67pub struct StreamsState {
+68    pub(super) side: Side,
+69    // Set of streams that are currently open, or could be immediately opened by the peer
+70    pub(super) send: FxHashMap<StreamId, Option<Box<Send>>>,
+71    pub(super) recv: FxHashMap<StreamId, Option<StreamRecv>>,
+72    pub(super) free_recv: Vec<StreamRecv>,
+73    pub(super) next: [u64; 2],
+74    /// Maximum number of locally-initiated streams that may be opened over the lifetime of the
+75    /// connection so far, per direction
+76    pub(super) max: [u64; 2],
+77    /// Maximum number of remotely-initiated streams that may be opened over the lifetime of the
+78    /// connection so far, per direction
+79    pub(super) max_remote: [u64; 2],
+80    /// Value of `max_remote` most recently transmitted to the peer in a `MAX_STREAMS` frame
+81    sent_max_remote: [u64; 2],
+82    /// Number of streams that we've given the peer permission to open and which aren't fully closed
+83    pub(super) allocated_remote_count: [u64; 2],
+84    /// Size of the desired stream flow control window. May be smaller than `allocated_remote_count`
+85    /// due to `set_max_concurrent` calls.
+86    max_concurrent_remote_count: [u64; 2],
+87    /// Whether `max_concurrent_remote_count` has ever changed
+88    flow_control_adjusted: bool,
+89    /// Lowest remotely-initiated stream index that haven't actually been opened by the peer
+90    pub(super) next_remote: [u64; 2],
+91    /// Whether the remote endpoint has opened any streams the application doesn't know about yet,
+92    /// per directionality
+93    opened: [bool; 2],
+94    // Next to report to the application, once opened
+95    pub(super) next_reported_remote: [u64; 2],
+96    /// Number of outbound streams
+97    ///
+98    /// This differs from `self.send.len()` in that it does not include streams that the peer is
+99    /// permitted to open but which have not yet been opened.
+100    pub(super) send_streams: usize,
+101    /// Streams with outgoing data queued, sorted by priority
+102    pub(super) pending: PendingStreamsQueue,
+103
+104    events: VecDeque<StreamEvent>,
+105    /// Streams blocked on connection-level flow control or stream window space
+106    ///
+107    /// Streams are only added to this list when a write fails.
+108    pub(super) connection_blocked: Vec<StreamId>,
+109    /// Connection-level flow control budget dictated by the peer
+110    pub(super) max_data: u64,
+111    /// The initial receive window
+112    receive_window: u64,
+113    /// Limit on incoming data, which is transmitted through `MAX_DATA` frames
+114    local_max_data: u64,
+115    /// The last value of `MAX_DATA` which had been queued for transmission in
+116    /// an outgoing `MAX_DATA` frame
+117    sent_max_data: VarInt,
+118    /// Sum of current offsets of all send streams.
+119    pub(super) data_sent: u64,
+120    /// Sum of end offsets of all receive streams. Includes gaps, so it's an upper bound.
+121    data_recvd: u64,
+122    /// Total quantity of unacknowledged outgoing data
+123    pub(super) unacked_data: u64,
+124    /// Configured upper bound for `unacked_data`.
+125    ///
+126    /// Note this may be less than `unacked_data` if the user has set a new value.
+127    pub(super) send_window: u64,
+128    /// Configured upper bound for how much unacked data the peer can send us per stream
+129    pub(super) stream_receive_window: u64,
+130
+131    // Pertinent state from the TransportParameters supplied by the peer
+132    initial_max_stream_data_uni: VarInt,
+133    initial_max_stream_data_bidi_local: VarInt,
+134    initial_max_stream_data_bidi_remote: VarInt,
+135
+136    /// The shrink to be applied to local_max_data when receive_window is shrunk
+137    receive_window_shrink_debt: u64,
+138}
+139
+140impl StreamsState {
+141    #[allow(unreachable_pub)] // fuzzing only
+142    pub fn new(
+143        side: Side,
+144        max_remote_uni: VarInt,
+145        max_remote_bi: VarInt,
+146        send_window: u64,
+147        receive_window: VarInt,
+148        stream_receive_window: VarInt,
+149    ) -> Self {
+150        let mut this = Self {
+151            side,
+152            send: FxHashMap::default(),
+153            recv: FxHashMap::default(),
+154            free_recv: Vec::new(),
+155            next: [0, 0],
+156            max: [0, 0],
+157            max_remote: [max_remote_bi.into(), max_remote_uni.into()],
+158            sent_max_remote: [max_remote_bi.into(), max_remote_uni.into()],
+159            allocated_remote_count: [max_remote_bi.into(), max_remote_uni.into()],
+160            max_concurrent_remote_count: [max_remote_bi.into(), max_remote_uni.into()],
+161            flow_control_adjusted: false,
+162            next_remote: [0, 0],
+163            opened: [false, false],
+164            next_reported_remote: [0, 0],
+165            send_streams: 0,
+166            pending: PendingStreamsQueue::new(),
+167            events: VecDeque::new(),
+168            connection_blocked: Vec::new(),
+169            max_data: 0,
+170            receive_window: receive_window.into(),
+171            local_max_data: receive_window.into(),
+172            sent_max_data: receive_window,
+173            data_sent: 0,
+174            data_recvd: 0,
+175            unacked_data: 0,
+176            send_window,
+177            stream_receive_window: stream_receive_window.into(),
+178            initial_max_stream_data_uni: 0u32.into(),
+179            initial_max_stream_data_bidi_local: 0u32.into(),
+180            initial_max_stream_data_bidi_remote: 0u32.into(),
+181            receive_window_shrink_debt: 0,
+182        };
+183
+184        for dir in Dir::iter() {
+185            for i in 0..this.max_remote[dir as usize] {
+186                this.insert(true, StreamId::new(!side, dir, i));
+187            }
+188        }
+189
+190        this
+191    }
+192
+193    pub(crate) fn set_params(&mut self, params: &TransportParameters) {
+194        self.initial_max_stream_data_uni = params.initial_max_stream_data_uni;
+195        self.initial_max_stream_data_bidi_local = params.initial_max_stream_data_bidi_local;
+196        self.initial_max_stream_data_bidi_remote = params.initial_max_stream_data_bidi_remote;
+197        self.max[Dir::Bi as usize] = params.initial_max_streams_bidi.into();
+198        self.max[Dir::Uni as usize] = params.initial_max_streams_uni.into();
+199        self.received_max_data(params.initial_max_data);
+200        for i in 0..self.max_remote[Dir::Bi as usize] {
+201            let id = StreamId::new(!self.side, Dir::Bi, i);
+202            if let Some(s) = self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+203                s.max_data = params.initial_max_stream_data_bidi_local.into();
+204            }
+205        }
+206    }
+207
+208    /// Ensure we have space for at least a full flow control window of remotely-initiated streams
+209    /// to be open, and notify the peer if the window has moved
+210    fn ensure_remote_streams(&mut self, dir: Dir) {
+211        let new_count = self.max_concurrent_remote_count[dir as usize]
+212            .saturating_sub(self.allocated_remote_count[dir as usize]);
+213        for i in 0..new_count {
+214            let id = StreamId::new(!self.side, dir, self.max_remote[dir as usize] + i);
+215            self.insert(true, id);
+216        }
+217        self.allocated_remote_count[dir as usize] += new_count;
+218        self.max_remote[dir as usize] += new_count;
+219    }
+220
+221    pub(crate) fn zero_rtt_rejected(&mut self) {
+222        // Revert to initial state for outgoing streams
+223        for dir in Dir::iter() {
+224            for i in 0..self.next[dir as usize] {
+225                // We don't bother calling `stream_freed` here because we explicitly reset affected
+226                // counters below.
+227                let id = StreamId::new(self.side, dir, i);
+228                self.send.remove(&id).unwrap();
+229                if let Dir::Bi = dir {
+230                    self.recv.remove(&id).unwrap();
+231                }
+232            }
+233            self.next[dir as usize] = 0;
+234
+235            // If 0-RTT was rejected, any flow control frames we sent were lost.
+236            if self.flow_control_adjusted {
+237                // Conservative approximation of whatever we sent in transport parameters
+238                self.sent_max_remote[dir as usize] = 0;
+239            }
+240        }
+241
+242        self.pending.clear();
+243        self.send_streams = 0;
+244        self.data_sent = 0;
+245        self.connection_blocked.clear();
+246    }
+247
+248    /// Process incoming stream frame
+249    ///
+250    /// If successful, returns whether a `MAX_DATA` frame needs to be transmitted
+251    pub(crate) fn received(
+252        &mut self,
+253        frame: frame::Stream,
+254        payload_len: usize,
+255    ) -> Result<ShouldTransmit, TransportError> {
+256        let id = frame.id;
+257        self.validate_receive_id(id).inspect_err(|_e| {
+258            debug!("received illegal STREAM frame");
+259        })?;
+260
+261        let rs = match self
+262            .recv
+263            .get_mut(&id)
+264            .map(get_or_insert_recv(self.stream_receive_window))
+265        {
+266            Some(rs) => rs,
+267            None => {
+268                trace!("dropping frame for closed stream");
+269                return Ok(ShouldTransmit(false));
+270            }
+271        };
+272
+273        if !rs.is_receiving() {
+274            trace!("dropping frame for finished stream");
+275            return Ok(ShouldTransmit(false));
+276        }
+277
+278        let (new_bytes, closed) =
+279            rs.ingest(frame, payload_len, self.data_recvd, self.local_max_data)?;
+280        self.data_recvd = self.data_recvd.saturating_add(new_bytes);
+281
+282        if !rs.stopped {
+283            self.on_stream_frame(true, id);
+284            return Ok(ShouldTransmit(false));
+285        }
+286
+287        // Stopped streams become closed instantly on FIN, so check whether we need to clean up
+288        if closed {
+289            let rs = self.recv.remove(&id).flatten().unwrap();
+290            self.stream_recv_freed(id, rs);
+291        }
+292
+293        // We don't buffer data on stopped streams, so issue flow control credit immediately
+294        Ok(self.add_read_credits(new_bytes))
+295    }
+296
+297    /// Process incoming RESET_STREAM frame
+298    ///
+299    /// If successful, returns whether a `MAX_DATA` frame needs to be transmitted
+300    #[allow(unreachable_pub)] // fuzzing only
+301    pub fn received_reset(
+302        &mut self,
+303        frame: frame::ResetStream,
+304    ) -> Result<ShouldTransmit, TransportError> {
+305        let frame::ResetStream {
+306            id,
+307            error_code,
+308            final_offset,
+309        } = frame;
+310        self.validate_receive_id(id).inspect_err(|_e| {
+311            debug!("received illegal RESET_STREAM frame");
+312        })?;
+313
+314        let rs = match self
+315            .recv
+316            .get_mut(&id)
+317            .map(get_or_insert_recv(self.stream_receive_window))
+318        {
+319            Some(stream) => stream,
+320            None => {
+321                trace!("received RESET_STREAM on closed stream");
+322                return Ok(ShouldTransmit(false));
+323            }
+324        };
+325
+326        // State transition
+327        if !rs.reset(
+328            error_code,
+329            final_offset,
+330            self.data_recvd,
+331            self.local_max_data,
+332        )? {
+333            // Redundant reset
+334            return Ok(ShouldTransmit(false));
+335        }
+336        let bytes_read = rs.assembler.bytes_read();
+337        let stopped = rs.stopped;
+338        let end = rs.end;
+339        if stopped {
+340            // Stopped streams should be disposed immediately on reset
+341            let rs = self.recv.remove(&id).flatten().unwrap();
+342            self.stream_recv_freed(id, rs);
+343        }
+344        self.on_stream_frame(!stopped, id);
+345
+346        // Update connection-level flow control
+347        Ok(if bytes_read != final_offset.into_inner() {
+348            // bytes_read is always <= end, so this won't underflow.
+349            self.data_recvd = self
+350                .data_recvd
+351                .saturating_add(u64::from(final_offset) - end);
+352            self.add_read_credits(u64::from(final_offset) - bytes_read)
+353        } else {
+354            ShouldTransmit(false)
+355        })
+356    }
+357
+358    /// Process incoming `STOP_SENDING` frame
+359    #[allow(unreachable_pub)] // fuzzing only
+360    pub fn received_stop_sending(&mut self, id: StreamId, error_code: VarInt) {
+361        let max_send_data = self.max_send_data(id);
+362        let stream = match self
+363            .send
+364            .get_mut(&id)
+365            .map(get_or_insert_send(max_send_data))
+366        {
+367            Some(ss) => ss,
+368            None => return,
+369        };
+370
+371        if stream.try_stop(error_code) {
+372            self.events
+373                .push_back(StreamEvent::Stopped { id, error_code });
+374            self.on_stream_frame(false, id);
+375        }
+376    }
+377
+378    pub(crate) fn reset_acked(&mut self, id: StreamId) {
+379        match self.send.entry(id) {
+380            hash_map::Entry::Vacant(_) => {}
+381            hash_map::Entry::Occupied(e) => {
+382                if let Some(SendState::ResetSent) = e.get().as_ref().map(|s| s.state) {
+383                    e.remove_entry();
+384                    self.stream_freed(id, StreamHalf::Send);
+385                }
+386            }
+387        }
+388    }
+389
+390    /// Whether any stream data is queued, regardless of control frames
+391    pub(crate) fn can_send_stream_data(&self) -> bool {
+392        // Reset streams may linger in the pending stream list, but will never produce stream frames
+393        self.pending.iter().any(|stream| {
+394            self.send
+395                .get(&stream.id)
+396                .and_then(|s| s.as_ref())
+397                .is_some_and(|s| !s.is_reset())
+398        })
+399    }
+400
+401    /// Whether MAX_STREAM_DATA frames could be sent for stream `id`
+402    pub(crate) fn can_send_flow_control(&self, id: StreamId) -> bool {
+403        self.recv
+404            .get(&id)
+405            .and_then(|s| s.as_ref())
+406            .and_then(|s| s.as_open_recv())
+407            .is_some_and(|s| s.can_send_flow_control())
+408    }
+409
+410    pub(in crate::connection) fn write_control_frames<'a, 'b>(
+411        &mut self,
+412        builder: &mut PacketBuilder<'a, 'b>,
+413        pending: &mut Retransmits,
+414        stats: &mut FrameStats,
+415    ) {
+416        // RESET_STREAM
+417        while builder.frame_space_remaining() > frame::ResetStream::SIZE_BOUND {
+418            let (id, error_code) = match pending.reset_stream.pop() {
+419                Some(x) => x,
+420                None => break,
+421            };
+422            let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+423                Some(x) => x,
+424                None => continue,
+425            };
+426            let frame = frame::ResetStream {
+427                id,
+428                error_code,
+429                final_offset: VarInt::try_from(stream.offset()).expect("impossibly large offset"),
+430            };
+431            builder.write_frame(frame, stats);
+432        }
+433
+434        // STOP_SENDING
+435        while builder.frame_space_remaining() > frame::StopSending::SIZE_BOUND {
+436            let frame = match pending.stop_sending.pop() {
+437                Some(x) => x,
+438                None => break,
+439            };
+440            // We may need to transmit STOP_SENDING even for streams whose state we have discarded,
+441            // because we are able to discard local state for stopped streams immediately upon
+442            // receiving FIN, even if the peer still has arbitrarily large amounts of data to
+443            // (re)transmit due to loss or unconventional sending strategy. We could fine-tune this
+444            // a little by dropping the frame if we specifically know the stream's been reset by the
+445            // peer, but we discard that information as soon as the application consumes it, so it
+446            // can't be relied upon regardless.
+447            builder.write_frame(frame, stats);
+448        }
+449
+450        // MAX_DATA
+451        if pending.max_data && builder.frame_space_remaining() > 9 {
+452            pending.max_data = false;
+453
+454            // `local_max_data` can grow bigger than `VarInt`.
+455            // For transmission inside QUIC frames we need to clamp it to the
+456            // maximum allowed `VarInt` size.
+457            let max = VarInt::try_from(self.local_max_data).unwrap_or(VarInt::MAX);
+458
+459            if max > self.sent_max_data {
+460                // Record that a `MAX_DATA` announcing a certain window was sent. This will
+461                // suppress enqueuing further `MAX_DATA` frames unless either the previous
+462                // transmission was not acknowledged or the window further increased.
+463                self.sent_max_data = max;
+464            }
+465
+466            builder.write_frame(frame::MaxData(max), stats);
+467        }
+468
+469        // MAX_STREAM_DATA
+470        while builder.frame_space_remaining() > 17 {
+471            let id = match pending.max_stream_data.iter().next() {
+472                Some(x) => *x,
+473                None => break,
+474            };
+475            pending.max_stream_data.remove(&id);
+476            let rs = match self
+477                .recv
+478                .get_mut(&id)
+479                .and_then(|s| s.as_mut())
+480                .and_then(|s| s.as_open_recv_mut())
+481            {
+482                Some(x) => x,
+483                None => continue,
+484            };
+485            if !rs.can_send_flow_control() {
+486                continue;
+487            }
+488
+489            let (max, _) = rs.max_stream_data(self.stream_receive_window);
+490            rs.record_sent_max_stream_data(max);
+491            builder.write_frame(frame::MaxStreamData { id, offset: max }, stats);
+492        }
+493
+494        // MAX_STREAMS
+495        for dir in Dir::iter() {
+496            if !pending.max_stream_id[dir as usize] || builder.frame_space_remaining() <= 9 {
+497                continue;
+498            }
+499
+500            pending.max_stream_id[dir as usize] = false;
+501            self.sent_max_remote[dir as usize] = self.max_remote[dir as usize];
+502            let count = self.max_remote[dir as usize];
+503            builder.write_frame(frame::MaxStreams { dir, count }, stats);
+504        }
+505    }
+506
+507    pub(in crate::connection) fn write_stream_frames<'a, 'b>(
+508        &mut self,
+509        builder: &mut PacketBuilder<'a, 'b>,
+510        fair: bool,
+511        stats: &mut FrameStats,
+512    ) {
+513        while builder.frame_space_remaining() > frame::Stream::SIZE_BOUND {
+514            // Pop the stream of the highest priority that currently has pending data. If
+515            // the stream still has some pending data left after writing, it will be
+516            // reinserted, otherwise not
+517            let Some(stream) = self.pending.pop() else {
+518                break;
+519            };
+520
+521            let id = stream.id;
+522
+523            let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+524                Some(s) => s,
+525                // Stream was reset with pending data and the reset was acknowledged
+526                None => continue,
+527            };
+528
+529            // Reset streams aren't removed from the pending list and still exist while the peer
+530            // hasn't acknowledged the reset, but should not generate STREAM frames, so we need to
+531            // check for them explicitly.
+532            if stream.is_reset() {
+533                continue;
+534            }
+535
+536            // Now that we know the `StreamId`, we can better account for how many bytes
+537            // are required to encode it.
+538            let max_buf_size = builder.frame_space_remaining() - 1 - VarInt::size(id.into());
+539            let (offsets, encode_length) = stream.pending.poll_transmit(max_buf_size);
+540            let fin = offsets.end == stream.pending.offset()
+541                && matches!(stream.state, SendState::DataSent { .. });
+542            if fin {
+543                stream.fin_pending = false;
+544            }
+545
+546            if stream.is_pending() {
+547                // If the stream still has pending data, reinsert it, possibly with an updated priority value
+548                // Fairness with other streams is achieved by implementing round-robin scheduling,
+549                // so that the other streams will have a chance to write data
+550                // before we touch this stream again.
+551                if fair {
+552                    self.pending.push_pending(id, stream.priority);
+553                } else {
+554                    self.pending.reinsert_pending(id, stream.priority);
+555                }
+556            }
+557
+558            let range = offsets.clone();
+559            let meta = frame::StreamMeta { id, offsets, fin };
+560            builder.write_frame(meta.encoder(encode_length), stats);
+561            stream.pending.get_into(range, builder.buf);
+562        }
+563    }
+564
+565    #[cfg(test)]
+566    fn write_frames_for_test(&mut self, capacity: usize, fair: bool) -> frame::StreamMetaVec {
+567        let buf = &mut Vec::with_capacity(capacity);
+568        let mut tbuf = crate::connection::TransmitBuf::new(buf, std::num::NonZeroUsize::MIN, 1_200);
+569        tbuf.start_new_datagram_with_size(capacity);
+570        let builder = &mut PacketBuilder::simple_data_buf(&mut tbuf);
+571        let stats = &mut FrameStats::default();
+572        self.write_stream_frames(builder, fair, stats);
+573        builder.sent_frames().stream_frames.clone()
+574    }
+575
+576    /// Notify the application that new streams were opened or a stream became readable.
+577    fn on_stream_frame(&mut self, notify_readable: bool, stream: StreamId) {
+578        if stream.initiator() == self.side {
+579            // Notifying about the opening of locally-initiated streams would be redundant.
+580            if notify_readable {
+581                self.events.push_back(StreamEvent::Readable { id: stream });
+582            }
+583            return;
+584        }
+585        let next = &mut self.next_remote[stream.dir() as usize];
+586        if stream.index() >= *next {
+587            *next = stream.index() + 1;
+588            self.opened[stream.dir() as usize] = true;
+589        } else if notify_readable {
+590            self.events.push_back(StreamEvent::Readable { id: stream });
+591        }
+592    }
+593
+594    pub(crate) fn received_ack_of(&mut self, frame: frame::StreamMeta) {
+595        let mut entry = match self.send.entry(frame.id) {
+596            hash_map::Entry::Vacant(_) => return,
+597            hash_map::Entry::Occupied(e) => e,
+598        };
+599
+600        let stream = match entry.get_mut().as_mut() {
+601            Some(s) => s,
+602            None => {
+603                // Because we only call this after sending data on this stream,
+604                // this closure should be unreachable. If we did somehow screw that up,
+605                // then we might hit an underflow below with unpredictable effects down
+606                // the line. Best to short-circuit.
+607                return;
+608            }
+609        };
+610
+611        if stream.is_reset() {
+612            // We account for outstanding data on reset streams at time of reset
+613            return;
+614        }
+615        let id = frame.id;
+616        self.unacked_data -= frame.offsets.end - frame.offsets.start;
+617        if !stream.ack(frame) {
+618            // The stream is unfinished or may still need retransmits
+619            return;
+620        }
+621
+622        entry.remove_entry();
+623        self.stream_freed(id, StreamHalf::Send);
+624        self.events.push_back(StreamEvent::Finished { id });
+625    }
+626
+627    pub(crate) fn retransmit(&mut self, frame: frame::StreamMeta) {
+628        let stream = match self.send.get_mut(&frame.id).and_then(|s| s.as_mut()) {
+629            // Loss of data on a closed stream is a noop
+630            None => return,
+631            Some(x) => x,
+632        };
+633        if !stream.is_pending() {
+634            self.pending.push_pending(frame.id, stream.priority);
+635        }
+636        stream.fin_pending |= frame.fin;
+637        stream.pending.retransmit(frame.offsets);
+638    }
+639
+640    pub(crate) fn retransmit_all_for_0rtt(&mut self) {
+641        for dir in Dir::iter() {
+642            for index in 0..self.next[dir as usize] {
+643                let id = StreamId::new(Side::Client, dir, index);
+644                let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+645                    Some(stream) => stream,
+646                    None => continue,
+647                };
+648                if stream.pending.is_fully_acked() && !stream.fin_pending {
+649                    // Stream data can't be acked in 0-RTT, so we must not have sent anything on
+650                    // this stream
+651                    continue;
+652                }
+653                if !stream.is_pending() {
+654                    self.pending.push_pending(id, stream.priority);
+655                }
+656                stream.pending.retransmit_all_for_0rtt();
+657            }
+658        }
+659    }
+660
+661    pub(crate) fn received_max_streams(
+662        &mut self,
+663        dir: Dir,
+664        count: u64,
+665    ) -> Result<(), TransportError> {
+666        if count > MAX_STREAM_COUNT {
+667            return Err(TransportError::FRAME_ENCODING_ERROR(
+668                "unrepresentable stream limit",
+669            ));
+670        }
+671
+672        let current = &mut self.max[dir as usize];
+673        if count > *current {
+674            *current = count;
+675            self.events.push_back(StreamEvent::Available { dir });
+676        }
+677
+678        Ok(())
+679    }
+680
+681    /// Handle increase to connection-level flow control limit
+682    pub(crate) fn received_max_data(&mut self, n: VarInt) {
+683        self.max_data = self.max_data.max(n.into());
+684    }
+685
+686    pub(crate) fn received_max_stream_data(
+687        &mut self,
+688        id: StreamId,
+689        offset: u64,
+690    ) -> Result<(), TransportError> {
+691        if id.initiator() != self.side && id.dir() == Dir::Uni {
+692            debug!("got MAX_STREAM_DATA on recv-only {}", id);
+693            return Err(TransportError::STREAM_STATE_ERROR(
+694                "MAX_STREAM_DATA on recv-only stream",
+695            ));
+696        }
+697
+698        let write_limit = self.write_limit();
+699        let max_send_data = self.max_send_data(id);
+700        if let Some(ss) = self
+701            .send
+702            .get_mut(&id)
+703            .map(get_or_insert_send(max_send_data))
+704        {
+705            if ss.increase_max_data(offset) {
+706                if write_limit > 0 {
+707                    self.events.push_back(StreamEvent::Writable { id });
+708                } else if !ss.connection_blocked {
+709                    // The stream is still blocked on the connection flow control
+710                    // window. In order to get unblocked when the window relaxes
+711                    // it needs to be in the connection blocked list.
+712                    ss.connection_blocked = true;
+713                    self.connection_blocked.push(id);
+714                }
+715            }
+716        } else if id.initiator() == self.side && self.is_local_unopened(id) {
+717            debug!("got MAX_STREAM_DATA on unopened {}", id);
+718            return Err(TransportError::STREAM_STATE_ERROR(
+719                "MAX_STREAM_DATA on unopened stream",
+720            ));
+721        }
+722
+723        self.on_stream_frame(false, id);
+724        Ok(())
+725    }
+726
+727    /// Returns the maximum amount of data this is allowed to be written on the connection
+728    pub(crate) fn write_limit(&self) -> u64 {
+729        (self.max_data - self.data_sent)
+730            // `send_window` can be set after construction to something *less* than `unacked_data`
+731            .min(self.send_window.saturating_sub(self.unacked_data))
+732    }
+733
+734    /// Yield stream events
+735    pub(crate) fn poll(&mut self) -> Option<StreamEvent> {
+736        if let Some(dir) = Dir::iter().find(|&i| mem::replace(&mut self.opened[i as usize], false))
+737        {
+738            return Some(StreamEvent::Opened { dir });
+739        }
+740
+741        if self.write_limit() > 0 {
+742            while let Some(id) = self.connection_blocked.pop() {
+743                let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+744                    None => continue,
+745                    Some(s) => s,
+746                };
+747
+748                debug_assert!(stream.connection_blocked);
+749                stream.connection_blocked = false;
+750
+751                // If it's no longer sensible to write to a stream (even to detect an error) then don't
+752                // report it.
+753                if stream.is_writable() && stream.max_data > stream.offset() {
+754                    return Some(StreamEvent::Writable { id });
+755                }
+756            }
+757        }
+758
+759        self.events.pop_front()
+760    }
+761
+762    /// Queues MAX_STREAM_ID frames in `pending` if needed
+763    ///
+764    /// Returns whether any frames were queued.
+765    pub(crate) fn queue_max_stream_id(&mut self, pending: &mut Retransmits) -> bool {
+766        let mut queued = false;
+767        for dir in Dir::iter() {
+768            let diff = self.max_remote[dir as usize] - self.sent_max_remote[dir as usize];
+769            // To reduce traffic, only announce updates if at least 1/8 of the flow control window
+770            // has been consumed.
+771            if diff > self.max_concurrent_remote_count[dir as usize] / 8 {
+772                pending.max_stream_id[dir as usize] = true;
+773                queued = true;
+774            }
+775        }
+776        queued
+777    }
+778
+779    /// Check for errors entailed by the peer's use of `id` as a send stream
+780    fn validate_receive_id(&mut self, id: StreamId) -> Result<(), TransportError> {
+781        if self.side == id.initiator() {
+782            match id.dir() {
+783                Dir::Uni => {
+784                    return Err(TransportError::STREAM_STATE_ERROR(
+785                        "illegal operation on send-only stream",
+786                    ));
+787                }
+788                Dir::Bi if id.index() >= self.next[Dir::Bi as usize] => {
+789                    return Err(TransportError::STREAM_STATE_ERROR(
+790                        "operation on unopened stream",
+791                    ));
+792                }
+793                Dir::Bi => {}
+794            };
+795        } else {
+796            let limit = self.max_remote[id.dir() as usize];
+797            if id.index() >= limit {
+798                return Err(TransportError::STREAM_LIMIT_ERROR(""));
+799            }
+800        }
+801        Ok(())
+802    }
+803
+804    /// Whether a locally initiated stream has never been open
+805    pub(crate) fn is_local_unopened(&self, id: StreamId) -> bool {
+806        id.index() >= self.next[id.dir() as usize]
+807    }
+808
+809    pub(crate) fn set_max_concurrent(&mut self, dir: Dir, count: VarInt) {
+810        self.flow_control_adjusted = true;
+811        self.max_concurrent_remote_count[dir as usize] = count.into();
+812        self.ensure_remote_streams(dir);
+813    }
+814
+815    pub(crate) fn max_concurrent(&self, dir: Dir) -> u64 {
+816        self.allocated_remote_count[dir as usize]
+817    }
+818
+819    pub(crate) fn set_send_window(&mut self, send_window: u64) {
+820        self.send_window = send_window;
+821    }
+822
+823    /// Set the receive_window and returns whether the receive_window has been
+824    /// expanded or shrunk: true if expanded, false if shrunk.
+825    pub(crate) fn set_receive_window(&mut self, receive_window: VarInt) -> bool {
+826        let receive_window = receive_window.into();
+827        let mut expanded = false;
+828        if receive_window > self.receive_window {
+829            self.local_max_data = self
+830                .local_max_data
+831                .saturating_add(receive_window - self.receive_window);
+832            expanded = true;
+833        } else {
+834            let diff = self.receive_window - receive_window;
+835            self.receive_window_shrink_debt = self.receive_window_shrink_debt.saturating_add(diff);
+836        }
+837        self.receive_window = receive_window;
+838        expanded
+839    }
+840
+841    pub(super) fn insert(&mut self, remote: bool, id: StreamId) {
+842        let bi = id.dir() == Dir::Bi;
+843        // bidirectional OR (unidirectional AND NOT remote)
+844        if bi || !remote {
+845            assert!(self.send.insert(id, None).is_none());
+846        }
+847        // bidirectional OR (unidirectional AND remote)
+848        if bi || remote {
+849            let recv = self.free_recv.pop();
+850            assert!(self.recv.insert(id, recv).is_none());
+851        }
+852    }
+853
+854    /// Adds credits to the connection flow control window
+855    ///
+856    /// Returns whether a `MAX_DATA` frame should be enqueued as soon as possible.
+857    /// This will only be the case if the window update would is significant
+858    /// enough. As soon as a window update with a `MAX_DATA` frame has been
+859    /// queued, the [`Recv::record_sent_max_stream_data`] function should be called to
+860    /// suppress sending further updates until the window increases significantly
+861    /// again.
+862    pub(super) fn add_read_credits(&mut self, credits: u64) -> ShouldTransmit {
+863        if credits > self.receive_window_shrink_debt {
+864            let net_credits = credits - self.receive_window_shrink_debt;
+865            self.local_max_data = self.local_max_data.saturating_add(net_credits);
+866            self.receive_window_shrink_debt = 0;
+867        } else {
+868            self.receive_window_shrink_debt -= credits;
+869        }
+870
+871        if self.local_max_data > VarInt::MAX.into_inner() {
+872            return ShouldTransmit(false);
+873        }
+874
+875        // Only announce a window update if it's significant enough
+876        // to make it worthwhile sending a MAX_DATA frame.
+877        // We use a fraction of the configured connection receive window to make
+878        // the decision, to accommodate for connection using bigger windows requiring
+879        // less updates.
+880        let diff = self.local_max_data - self.sent_max_data.into_inner();
+881        ShouldTransmit(diff >= (self.receive_window / 8))
+882    }
+883
+884    /// Update counters for removal of a stream
+885    pub(super) fn stream_freed(&mut self, id: StreamId, half: StreamHalf) {
+886        if id.initiator() != self.side {
+887            let fully_free = id.dir() == Dir::Uni
+888                || match half {
+889                    StreamHalf::Send => !self.recv.contains_key(&id),
+890                    StreamHalf::Recv => !self.send.contains_key(&id),
+891                };
+892            if fully_free {
+893                self.allocated_remote_count[id.dir() as usize] -= 1;
+894                self.ensure_remote_streams(id.dir());
+895            }
+896        }
+897        if half == StreamHalf::Send {
+898            self.send_streams -= 1;
+899        }
+900    }
+901
+902    pub(super) fn stream_recv_freed(&mut self, id: StreamId, recv: StreamRecv) {
+903        self.free_recv.push(recv.free(self.stream_receive_window));
+904        self.stream_freed(id, StreamHalf::Recv);
+905    }
+906
+907    pub(super) fn max_send_data(&self, id: StreamId) -> VarInt {
+908        let remote = self.side != id.initiator();
+909        match id.dir() {
+910            Dir::Uni => self.initial_max_stream_data_uni,
+911            // Remote/local appear reversed here because the transport parameters are named from
+912            // the perspective of the peer.
+913            Dir::Bi if remote => self.initial_max_stream_data_bidi_local,
+914            Dir::Bi => self.initial_max_stream_data_bidi_remote,
+915        }
+916    }
+917}
+918
+919#[inline]
+920pub(super) fn get_or_insert_send(
+921    max_data: VarInt,
+922) -> impl Fn(&mut Option<Box<Send>>) -> &mut Box<Send> {
+923    move |opt| opt.get_or_insert_with(|| Send::new(max_data))
+924}
+925
+926#[inline]
+927pub(super) fn get_or_insert_recv(
+928    initial_max_data: u64,
+929) -> impl FnMut(&mut Option<StreamRecv>) -> &mut Recv {
+930    move |opt| {
+931        *opt = opt.take().map(|s| match s {
+932            StreamRecv::Free(recv) => StreamRecv::Open(recv),
+933            s => s,
+934        });
+935        opt.get_or_insert_with(|| StreamRecv::Open(Recv::new(initial_max_data)))
+936            .as_open_recv_mut()
+937            .unwrap()
+938    }
+939}
+940
+941#[cfg(test)]
+942mod tests {
+943    use super::*;
+944    use crate::{
+945        ReadableError, RecvStream, SendStream, TransportErrorCode, WriteError,
+946        connection::State as ConnState, connection::Streams,
+947    };
+948    use bytes::Bytes;
+949
+950    fn make(side: Side) -> StreamsState {
+951        StreamsState::new(
+952            side,
+953            128u32.into(),
+954            128u32.into(),
+955            1024 * 1024,
+956            (1024 * 1024u32).into(),
+957            (1024 * 1024u32).into(),
+958        )
+959    }
+960
+961    #[test]
+962    fn trivial_flow_control() {
+963        let mut client = StreamsState::new(
+964            Side::Client,
+965            1u32.into(),
+966            1u32.into(),
+967            1024 * 1024,
+968            (1024 * 1024u32).into(),
+969            (1024 * 1024u32).into(),
+970        );
+971        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+972        let initial_max = client.local_max_data;
+973        const MESSAGE_SIZE: usize = 2048;
+974        assert_eq!(
+975            client
+976                .received(
+977                    frame::Stream {
+978                        id,
+979                        offset: 0,
+980                        fin: true,
+981                        data: Bytes::from_static(&[0; MESSAGE_SIZE]),
+982                    },
+983                    2048
+984                )
+985                .unwrap(),
+986            ShouldTransmit(false)
+987        );
+988        assert_eq!(client.data_recvd, 2048);
+989        assert_eq!(client.local_max_data - initial_max, 0);
+990
+991        let mut pending = Retransmits::default();
+992        let mut recv = RecvStream {
+993            id,
+994            state: &mut client,
+995            pending: &mut pending,
+996        };
+997
+998        let mut chunks = recv.read(true).unwrap();
+999        assert_eq!(
+1000            chunks.next(MESSAGE_SIZE).unwrap().unwrap().bytes.len(),
+1001            MESSAGE_SIZE
+1002        );
+1003        assert!(chunks.next(0).unwrap().is_none());
+1004        let should_transmit = chunks.finalize();
+1005        assert!(should_transmit.0);
+1006        assert!(pending.max_stream_id[Dir::Uni as usize]);
+1007        assert_eq!(client.local_max_data - initial_max, MESSAGE_SIZE as u64);
+1008    }
+1009
+1010    #[test]
+1011    fn reset_flow_control() {
+1012        let mut client = make(Side::Client);
+1013        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1014        let initial_max = client.local_max_data;
+1015        assert_eq!(
+1016            client
+1017                .received(
+1018                    frame::Stream {
+1019                        id,
+1020                        offset: 0,
+1021                        fin: false,
+1022                        data: Bytes::from_static(&[0; 2048]),
+1023                    },
+1024                    2048
+1025                )
+1026                .unwrap(),
+1027            ShouldTransmit(false)
+1028        );
+1029        assert_eq!(client.data_recvd, 2048);
+1030        assert_eq!(client.local_max_data - initial_max, 0);
+1031
+1032        let mut pending = Retransmits::default();
+1033        let mut recv = RecvStream {
+1034            id,
+1035            state: &mut client,
+1036            pending: &mut pending,
+1037        };
+1038
+1039        let mut chunks = recv.read(true).unwrap();
+1040        chunks.next(1024).unwrap();
+1041        let _ = chunks.finalize();
+1042        assert_eq!(client.local_max_data - initial_max, 1024);
+1043        assert_eq!(
+1044            client
+1045                .received_reset(frame::ResetStream {
+1046                    id,
+1047                    error_code: 0u32.into(),
+1048                    final_offset: 4096u32.into(),
+1049                })
+1050                .unwrap(),
+1051            ShouldTransmit(false)
+1052        );
+1053
+1054        assert_eq!(client.data_recvd, 4096);
+1055        assert_eq!(client.local_max_data - initial_max, 4096);
+1056
+1057        // Ensure reading after a reset doesn't issue redundant credit
+1058        let mut recv = RecvStream {
+1059            id,
+1060            state: &mut client,
+1061            pending: &mut pending,
+1062        };
+1063        let mut chunks = recv.read(true).unwrap();
+1064        assert_eq!(
+1065            chunks.next(1024).unwrap_err(),
+1066            crate::ReadError::Reset(0u32.into())
+1067        );
+1068        let _ = chunks.finalize();
+1069        assert_eq!(client.data_recvd, 4096);
+1070        assert_eq!(client.local_max_data - initial_max, 4096);
+1071    }
+1072
+1073    #[test]
+1074    fn reset_after_empty_frame_flow_control() {
+1075        let mut client = make(Side::Client);
+1076        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1077        let initial_max = client.local_max_data;
+1078        assert_eq!(
+1079            client
+1080                .received(
+1081                    frame::Stream {
+1082                        id,
+1083                        offset: 4096,
+1084                        fin: false,
+1085                        data: Bytes::from_static(&[0; 0]),
+1086                    },
+1087                    0
+1088                )
+1089                .unwrap(),
+1090            ShouldTransmit(false)
+1091        );
+1092        assert_eq!(client.data_recvd, 4096);
+1093        assert_eq!(client.local_max_data - initial_max, 0);
+1094        assert_eq!(
+1095            client
+1096                .received_reset(frame::ResetStream {
+1097                    id,
+1098                    error_code: 0u32.into(),
+1099                    final_offset: 4096u32.into(),
+1100                })
+1101                .unwrap(),
+1102            ShouldTransmit(false)
+1103        );
+1104        assert_eq!(client.data_recvd, 4096);
+1105        assert_eq!(client.local_max_data - initial_max, 4096);
+1106    }
+1107
+1108    #[test]
+1109    fn duplicate_reset_flow_control() {
+1110        let mut client = make(Side::Client);
+1111        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1112        assert_eq!(
+1113            client
+1114                .received_reset(frame::ResetStream {
+1115                    id,
+1116                    error_code: 0u32.into(),
+1117                    final_offset: 4096u32.into(),
+1118                })
+1119                .unwrap(),
+1120            ShouldTransmit(false)
+1121        );
+1122        assert_eq!(client.data_recvd, 4096);
+1123        assert_eq!(
+1124            client
+1125                .received_reset(frame::ResetStream {
+1126                    id,
+1127                    error_code: 0u32.into(),
+1128                    final_offset: 4096u32.into(),
+1129                })
+1130                .unwrap(),
+1131            ShouldTransmit(false)
+1132        );
+1133        assert_eq!(client.data_recvd, 4096);
+1134    }
+1135
+1136    #[test]
+1137    fn recv_stopped() {
+1138        let mut client = make(Side::Client);
+1139        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1140        let initial_max = client.local_max_data;
+1141        assert_eq!(
+1142            client
+1143                .received(
+1144                    frame::Stream {
+1145                        id,
+1146                        offset: 0,
+1147                        fin: false,
+1148                        data: Bytes::from_static(&[0; 32]),
+1149                    },
+1150                    32
+1151                )
+1152                .unwrap(),
+1153            ShouldTransmit(false)
+1154        );
+1155        assert_eq!(client.local_max_data, initial_max);
+1156
+1157        let mut pending = Retransmits::default();
+1158        let mut recv = RecvStream {
+1159            id,
+1160            state: &mut client,
+1161            pending: &mut pending,
+1162        };
+1163
+1164        recv.stop(0u32.into()).unwrap();
+1165        assert_eq!(recv.pending.stop_sending.len(), 1);
+1166        assert!(!recv.pending.max_data);
+1167
+1168        assert!(recv.stop(0u32.into()).is_err());
+1169        assert_eq!(recv.read(true).err(), Some(ReadableError::ClosedStream));
+1170        assert_eq!(recv.read(false).err(), Some(ReadableError::ClosedStream));
+1171
+1172        assert_eq!(client.local_max_data - initial_max, 32);
+1173        assert_eq!(
+1174            client
+1175                .received(
+1176                    frame::Stream {
+1177                        id,
+1178                        offset: 32,
+1179                        fin: true,
+1180                        data: Bytes::from_static(&[0; 16]),
+1181                    },
+1182                    16
+1183                )
+1184                .unwrap(),
+1185            ShouldTransmit(false)
+1186        );
+1187        assert_eq!(client.local_max_data - initial_max, 48);
+1188        assert!(!client.recv.contains_key(&id));
+1189    }
+1190
+1191    #[test]
+1192    fn stopped_reset() {
+1193        let mut client = make(Side::Client);
+1194        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1195        // Server opens stream
+1196        assert_eq!(
+1197            client
+1198                .received(
+1199                    frame::Stream {
+1200                        id,
+1201                        offset: 0,
+1202                        fin: false,
+1203                        data: Bytes::from_static(&[0; 32])
+1204                    },
+1205                    32
+1206                )
+1207                .unwrap(),
+1208            ShouldTransmit(false)
+1209        );
+1210
+1211        let mut pending = Retransmits::default();
+1212        let mut recv = RecvStream {
+1213            id,
+1214            state: &mut client,
+1215            pending: &mut pending,
+1216        };
+1217
+1218        recv.stop(0u32.into()).unwrap();
+1219        assert_eq!(pending.stop_sending.len(), 1);
+1220        assert!(!pending.max_data);
+1221
+1222        // Server complies
+1223        let prev_max = client.max_remote[Dir::Uni as usize];
+1224        assert_eq!(
+1225            client
+1226                .received_reset(frame::ResetStream {
+1227                    id,
+1228                    error_code: 0u32.into(),
+1229                    final_offset: 32u32.into(),
+1230                })
+1231                .unwrap(),
+1232            ShouldTransmit(false)
+1233        );
+1234        assert!(!client.recv.contains_key(&id), "stream state is freed");
+1235        assert_eq!(client.max_remote[Dir::Uni as usize], prev_max + 1);
+1236    }
+1237
+1238    #[test]
+1239    fn send_stopped() {
+1240        let mut server = make(Side::Server);
+1241        server.set_params(&TransportParameters {
+1242            initial_max_streams_uni: 1u32.into(),
+1243            initial_max_data: 42u32.into(),
+1244            initial_max_stream_data_uni: 42u32.into(),
+1245            ..TransportParameters::default()
+1246        });
+1247
+1248        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1249        let id = Streams {
+1250            state: &mut server,
+1251            conn_state: &state,
+1252        }
+1253        .open(Dir::Uni)
+1254        .unwrap();
+1255
+1256        let mut stream = SendStream {
+1257            id,
+1258            state: &mut server,
+1259            pending: &mut pending,
+1260            conn_state: &state,
+1261        };
+1262
+1263        let error_code = 0u32.into();
+1264        stream.state.received_stop_sending(id, error_code);
+1265        assert!(
+1266            stream
+1267                .state
+1268                .events
+1269                .contains(&StreamEvent::Stopped { id, error_code })
+1270        );
+1271        stream.state.events.clear();
+1272
+1273        assert_eq!(stream.write(&[]), Err(WriteError::Stopped(error_code)));
+1274
+1275        stream.reset(0u32.into()).unwrap();
+1276        assert_eq!(stream.write(&[]), Err(WriteError::ClosedStream));
+1277
+1278        // A duplicate frame is a no-op
+1279        stream.state.received_stop_sending(id, error_code);
+1280        assert!(stream.state.events.is_empty());
+1281    }
+1282
+1283    #[test]
+1284    fn final_offset_flow_control() {
+1285        let mut client = make(Side::Client);
+1286        assert_eq!(
+1287            client
+1288                .received_reset(frame::ResetStream {
+1289                    id: StreamId::new(Side::Server, Dir::Uni, 0),
+1290                    error_code: 0u32.into(),
+1291                    final_offset: VarInt::MAX,
+1292                })
+1293                .unwrap_err()
+1294                .code,
+1295            TransportErrorCode::FLOW_CONTROL_ERROR
+1296        );
+1297    }
+1298
+1299    #[test]
+1300    fn stream_priority() {
+1301        let mut server = make(Side::Server);
+1302        server.set_params(&TransportParameters {
+1303            initial_max_streams_bidi: 3u32.into(),
+1304            initial_max_data: 10u32.into(),
+1305            initial_max_stream_data_bidi_remote: 10u32.into(),
+1306            ..TransportParameters::default()
+1307        });
+1308
+1309        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1310        let mut streams = Streams {
+1311            state: &mut server,
+1312            conn_state: &state,
+1313        };
+1314
+1315        let id_high = streams.open(Dir::Bi).unwrap();
+1316        let id_mid = streams.open(Dir::Bi).unwrap();
+1317        let id_low = streams.open(Dir::Bi).unwrap();
+1318
+1319        let mut mid = SendStream {
+1320            id: id_mid,
+1321            state: &mut server,
+1322            pending: &mut pending,
+1323            conn_state: &state,
+1324        };
+1325        mid.write(b"mid").unwrap();
+1326
+1327        let mut low = SendStream {
+1328            id: id_low,
+1329            state: &mut server,
+1330            pending: &mut pending,
+1331            conn_state: &state,
+1332        };
+1333        low.set_priority(-1).unwrap();
+1334        low.write(b"low").unwrap();
+1335
+1336        let mut high = SendStream {
+1337            id: id_high,
+1338            state: &mut server,
+1339            pending: &mut pending,
+1340            conn_state: &state,
+1341        };
+1342        high.set_priority(1).unwrap();
+1343        high.write(b"high").unwrap();
+1344
+1345        let meta = server.write_frames_for_test(40, true);
+1346        assert_eq!(meta[0].id, id_high);
+1347        assert_eq!(meta[1].id, id_mid);
+1348        assert_eq!(meta[2].id, id_low);
+1349
+1350        assert!(!server.can_send_stream_data());
+1351        assert_eq!(server.pending.len(), 0);
+1352    }
+1353
+1354    #[test]
+1355    fn requeue_stream_priority() {
+1356        let mut server = make(Side::Server);
+1357        server.set_params(&TransportParameters {
+1358            initial_max_streams_bidi: 3u32.into(),
+1359            initial_max_data: 1000u32.into(),
+1360            initial_max_stream_data_bidi_remote: 1000u32.into(),
+1361            ..TransportParameters::default()
+1362        });
+1363
+1364        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1365        let mut streams = Streams {
+1366            state: &mut server,
+1367            conn_state: &state,
+1368        };
+1369
+1370        let id_high = streams.open(Dir::Bi).unwrap();
+1371        let id_mid = streams.open(Dir::Bi).unwrap();
+1372
+1373        let mut mid = SendStream {
+1374            id: id_mid,
+1375            state: &mut server,
+1376            pending: &mut pending,
+1377            conn_state: &state,
+1378        };
+1379        assert_eq!(mid.write(b"mid").unwrap(), 3);
+1380        assert_eq!(server.pending.len(), 1);
+1381
+1382        let mut high = SendStream {
+1383            id: id_high,
+1384            state: &mut server,
+1385            pending: &mut pending,
+1386            conn_state: &state,
+1387        };
+1388        high.set_priority(1).unwrap();
+1389        assert_eq!(high.write(&[0; 200]).unwrap(), 200);
+1390        assert_eq!(server.pending.len(), 2);
+1391
+1392        // Requeue the high priority stream to lowest priority. The initial send
+1393        // still uses high priority since it's queued that way. After that it will
+1394        // switch to low priority
+1395        let mut high = SendStream {
+1396            id: id_high,
+1397            state: &mut server,
+1398            pending: &mut pending,
+1399            conn_state: &state,
+1400        };
+1401        high.set_priority(-1).unwrap();
+1402
+1403        let meta = server.write_frames_for_test(40, true);
+1404        assert_eq!(meta.len(), 1);
+1405        assert_eq!(meta[0].id, id_high);
+1406
+1407        // After requeuing we should end up with 2 priorities - not 3
+1408        assert_eq!(server.pending.len(), 2);
+1409
+1410        // Send the remaining data. The initial mid priority one should go first now
+1411        let meta = server.write_frames_for_test(1000 - 40, true);
+1412        assert_eq!(meta.len(), 2);
+1413        assert_eq!(meta[0].id, id_mid);
+1414        assert_eq!(meta[1].id, id_high);
+1415
+1416        assert!(!server.can_send_stream_data());
+1417        assert_eq!(server.pending.len(), 0);
+1418    }
+1419
+1420    #[test]
+1421    fn same_stream_priority() {
+1422        for fair in [true, false] {
+1423            let mut server = make(Side::Server);
+1424            server.set_params(&TransportParameters {
+1425                initial_max_streams_bidi: 3u32.into(),
+1426                initial_max_data: 300u32.into(),
+1427                initial_max_stream_data_bidi_remote: 300u32.into(),
+1428                ..TransportParameters::default()
+1429            });
+1430
+1431            let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1432            let mut streams = Streams {
+1433                state: &mut server,
+1434                conn_state: &state,
+1435            };
+1436
+1437            // a, b and c all have the same priority
+1438            let id_a = streams.open(Dir::Bi).unwrap();
+1439            let id_b = streams.open(Dir::Bi).unwrap();
+1440            let id_c = streams.open(Dir::Bi).unwrap();
+1441
+1442            let mut stream_a = SendStream {
+1443                id: id_a,
+1444                state: &mut server,
+1445                pending: &mut pending,
+1446                conn_state: &state,
+1447            };
+1448            stream_a.write(&[b'a'; 100]).unwrap();
+1449
+1450            let mut stream_b = SendStream {
+1451                id: id_b,
+1452                state: &mut server,
+1453                pending: &mut pending,
+1454                conn_state: &state,
+1455            };
+1456            stream_b.write(&[b'b'; 100]).unwrap();
+1457
+1458            let mut stream_c = SendStream {
+1459                id: id_c,
+1460                state: &mut server,
+1461                pending: &mut pending,
+1462                conn_state: &state,
+1463            };
+1464            stream_c.write(&[b'c'; 100]).unwrap();
+1465
+1466            let mut metas = vec![];
+1467
+1468            // loop until all the streams are written
+1469            loop {
+1470                let meta = server.write_frames_for_test(40, fair);
+1471                if meta.is_empty() {
+1472                    break;
+1473                }
+1474                metas.extend(meta);
+1475            }
+1476
+1477            assert!(!server.can_send_stream_data());
+1478            assert_eq!(server.pending.len(), 0);
+1479
+1480            let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>();
+1481            if fair {
+1482                // When fairness is enabled, if we run out of buffer space to write out a stream,
+1483                // the stream is re-queued after all the streams with the same priority.
+1484                assert_eq!(
+1485                    stream_ids,
+1486                    vec![id_a, id_b, id_c, id_a, id_b, id_c, id_a, id_b, id_c]
+1487                );
+1488            } else {
+1489                // When fairness is disabled the stream is re-queued before all the other streams
+1490                // with the same priority.
+1491                assert_eq!(
+1492                    stream_ids,
+1493                    vec![id_a, id_a, id_a, id_b, id_b, id_b, id_c, id_c, id_c]
+1494                );
+1495            }
+1496        }
+1497    }
+1498
+1499    #[test]
+1500    fn unfair_priority_bump() {
+1501        let mut server = make(Side::Server);
+1502        server.set_params(&TransportParameters {
+1503            initial_max_streams_bidi: 3u32.into(),
+1504            initial_max_data: 300u32.into(),
+1505            initial_max_stream_data_bidi_remote: 300u32.into(),
+1506            ..TransportParameters::default()
+1507        });
+1508
+1509        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1510        let mut streams = Streams {
+1511            state: &mut server,
+1512            conn_state: &state,
+1513        };
+1514
+1515        // a, and b have the same priority, c has higher priority
+1516        let id_a = streams.open(Dir::Bi).unwrap();
+1517        let id_b = streams.open(Dir::Bi).unwrap();
+1518        let id_c = streams.open(Dir::Bi).unwrap();
+1519
+1520        let mut stream_a = SendStream {
+1521            id: id_a,
+1522            state: &mut server,
+1523            pending: &mut pending,
+1524            conn_state: &state,
+1525        };
+1526        stream_a.write(&[b'a'; 100]).unwrap();
+1527
+1528        let mut stream_b = SendStream {
+1529            id: id_b,
+1530            state: &mut server,
+1531            pending: &mut pending,
+1532            conn_state: &state,
+1533        };
+1534        stream_b.write(&[b'b'; 100]).unwrap();
+1535
+1536        let mut metas = vec![];
+1537
+1538        // Write the first chunk of stream_a
+1539        let meta = server.write_frames_for_test(40, false);
+1540        assert!(!meta.is_empty());
+1541        metas.extend(meta);
+1542
+1543        // Queue stream_c which has higher priority
+1544        let mut stream_c = SendStream {
+1545            id: id_c,
+1546            state: &mut server,
+1547            pending: &mut pending,
+1548            conn_state: &state,
+1549        };
+1550        stream_c.set_priority(1).unwrap();
+1551        stream_c.write(&[b'b'; 100]).unwrap();
+1552
+1553        // loop until all the streams are written
+1554        loop {
+1555            let meta = server.write_frames_for_test(40, false);
+1556            if meta.is_empty() {
+1557                break;
+1558            }
+1559            metas.extend(meta);
+1560        }
+1561
+1562        assert!(!server.can_send_stream_data());
+1563        assert_eq!(server.pending.len(), 0);
+1564
+1565        let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>();
+1566        assert_eq!(
+1567            stream_ids,
+1568            // stream_c bumps stream_b but doesn't bump stream_a which had already been partly
+1569            // written out
+1570            vec![id_a, id_a, id_a, id_c, id_c, id_c, id_b, id_b, id_b]
+1571        );
+1572    }
+1573
+1574    #[test]
+1575    fn stop_finished() {
+1576        let mut client = make(Side::Client);
+1577        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1578        // Server finishes stream
+1579        let _ = client
+1580            .received(
+1581                frame::Stream {
+1582                    id,
+1583                    offset: 0,
+1584                    fin: true,
+1585                    data: Bytes::from_static(&[0; 32]),
+1586                },
+1587                32,
+1588            )
+1589            .unwrap();
+1590        let mut pending = Retransmits::default();
+1591        let mut stream = RecvStream {
+1592            id,
+1593            state: &mut client,
+1594            pending: &mut pending,
+1595        };
+1596        stream.stop(0u32.into()).unwrap();
+1597        assert!(client.recv.get_mut(&id).is_none(), "stream is freed");
+1598    }
+1599
+1600    // Verify that a stream that's been reset doesn't cause the appearance of pending data
+1601    #[test]
+1602    fn reset_stream_cannot_send() {
+1603        let mut server = make(Side::Server);
+1604        server.set_params(&TransportParameters {
+1605            initial_max_streams_uni: 1u32.into(),
+1606            initial_max_data: 42u32.into(),
+1607            initial_max_stream_data_uni: 42u32.into(),
+1608            ..TransportParameters::default()
+1609        });
+1610        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1611        let mut streams = Streams {
+1612            state: &mut server,
+1613            conn_state: &state,
+1614        };
+1615
+1616        let id = streams.open(Dir::Uni).unwrap();
+1617        let mut stream = SendStream {
+1618            id,
+1619            state: &mut server,
+1620            pending: &mut pending,
+1621            conn_state: &state,
+1622        };
+1623        stream.write(b"hello").unwrap();
+1624        stream.reset(0u32.into()).unwrap();
+1625
+1626        assert_eq!(pending.reset_stream, &[(id, 0u32.into())]);
+1627        assert!(!server.can_send_stream_data());
+1628    }
+1629
+1630    #[test]
+1631    fn stream_limit_fixed() {
+1632        let mut client = make(Side::Client);
+1633        // Open streams 0-127
+1634        assert_eq!(
+1635            client.received(
+1636                frame::Stream {
+1637                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1638                    offset: 0,
+1639                    fin: true,
+1640                    data: Bytes::from_static(&[]),
+1641                },
+1642                0
+1643            ),
+1644            Ok(ShouldTransmit(false))
+1645        );
+1646        // Try to open stream 128, exceeding limit
+1647        assert_eq!(
+1648            client
+1649                .received(
+1650                    frame::Stream {
+1651                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1652                        offset: 0,
+1653                        fin: true,
+1654                        data: Bytes::from_static(&[]),
+1655                    },
+1656                    0
+1657                )
+1658                .unwrap_err()
+1659                .code,
+1660            TransportErrorCode::STREAM_LIMIT_ERROR
+1661        );
+1662
+1663        // Free stream 127
+1664        let mut pending = Retransmits::default();
+1665        let mut stream = RecvStream {
+1666            id: StreamId::new(Side::Server, Dir::Uni, 127),
+1667            state: &mut client,
+1668            pending: &mut pending,
+1669        };
+1670        stream.stop(0u32.into()).unwrap();
+1671
+1672        // Open stream 128
+1673        assert_eq!(
+1674            client.received(
+1675                frame::Stream {
+1676                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1677                    offset: 0,
+1678                    fin: true,
+1679                    data: Bytes::from_static(&[]),
+1680                },
+1681                0
+1682            ),
+1683            Ok(ShouldTransmit(false))
+1684        );
+1685    }
+1686
+1687    #[test]
+1688    fn stream_limit_grows() {
+1689        let mut client = make(Side::Client);
+1690        // Open streams 0-127
+1691        assert_eq!(
+1692            client.received(
+1693                frame::Stream {
+1694                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1695                    offset: 0,
+1696                    fin: true,
+1697                    data: Bytes::from_static(&[]),
+1698                },
+1699                0
+1700            ),
+1701            Ok(ShouldTransmit(false))
+1702        );
+1703        // Try to open stream 128, exceeding limit
+1704        assert_eq!(
+1705            client
+1706                .received(
+1707                    frame::Stream {
+1708                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1709                        offset: 0,
+1710                        fin: true,
+1711                        data: Bytes::from_static(&[]),
+1712                    },
+1713                    0
+1714                )
+1715                .unwrap_err()
+1716                .code,
+1717            TransportErrorCode::STREAM_LIMIT_ERROR
+1718        );
+1719
+1720        // Relax limit by one
+1721        client.set_max_concurrent(Dir::Uni, 129u32.into());
+1722
+1723        // Open stream 128
+1724        assert_eq!(
+1725            client.received(
+1726                frame::Stream {
+1727                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1728                    offset: 0,
+1729                    fin: true,
+1730                    data: Bytes::from_static(&[]),
+1731                },
+1732                0
+1733            ),
+1734            Ok(ShouldTransmit(false))
+1735        );
+1736    }
+1737
+1738    #[test]
+1739    fn stream_limit_shrinks() {
+1740        let mut client = make(Side::Client);
+1741        // Open streams 0-127
+1742        assert_eq!(
+1743            client.received(
+1744                frame::Stream {
+1745                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1746                    offset: 0,
+1747                    fin: true,
+1748                    data: Bytes::from_static(&[]),
+1749                },
+1750                0
+1751            ),
+1752            Ok(ShouldTransmit(false))
+1753        );
+1754
+1755        // Tighten limit by one
+1756        client.set_max_concurrent(Dir::Uni, 127u32.into());
+1757
+1758        // Free stream 127
+1759        let mut pending = Retransmits::default();
+1760        let mut stream = RecvStream {
+1761            id: StreamId::new(Side::Server, Dir::Uni, 127),
+1762            state: &mut client,
+1763            pending: &mut pending,
+1764        };
+1765        stream.stop(0u32.into()).unwrap();
+1766
+1767        // Try to open stream 128, still exceeding limit
+1768        assert_eq!(
+1769            client
+1770                .received(
+1771                    frame::Stream {
+1772                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1773                        offset: 0,
+1774                        fin: true,
+1775                        data: Bytes::from_static(&[]),
+1776                    },
+1777                    0
+1778                )
+1779                .unwrap_err()
+1780                .code,
+1781            TransportErrorCode::STREAM_LIMIT_ERROR
+1782        );
+1783
+1784        // Free stream 126
+1785        assert_eq!(
+1786            client.received_reset(frame::ResetStream {
+1787                id: StreamId::new(Side::Server, Dir::Uni, 126),
+1788                error_code: 0u32.into(),
+1789                final_offset: 0u32.into(),
+1790            }),
+1791            Ok(ShouldTransmit(false))
+1792        );
+1793        let mut pending = Retransmits::default();
+1794        let mut stream = RecvStream {
+1795            id: StreamId::new(Side::Server, Dir::Uni, 126),
+1796            state: &mut client,
+1797            pending: &mut pending,
+1798        };
+1799        stream.stop(0u32.into()).unwrap();
+1800
+1801        // Open stream 128
+1802        assert_eq!(
+1803            client.received(
+1804                frame::Stream {
+1805                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1806                    offset: 0,
+1807                    fin: true,
+1808                    data: Bytes::from_static(&[]),
+1809                },
+1810                0
+1811            ),
+1812            Ok(ShouldTransmit(false))
+1813        );
+1814    }
+1815
+1816    #[test]
+1817    fn remote_stream_capacity() {
+1818        let mut client = make(Side::Client);
+1819        for _ in 0..2 {
+1820            client.set_max_concurrent(Dir::Uni, 200u32.into());
+1821            client.set_max_concurrent(Dir::Bi, 201u32.into());
+1822            assert_eq!(client.recv.len(), 200 + 201);
+1823            assert_eq!(client.max_remote[Dir::Uni as usize], 200);
+1824            assert_eq!(client.max_remote[Dir::Bi as usize], 201);
+1825        }
+1826    }
+1827
+1828    #[test]
+1829    fn expand_receive_window() {
+1830        let mut server = make(Side::Server);
+1831        let new_receive_window = 2 * server.receive_window as u32;
+1832        let expanded = server.set_receive_window(new_receive_window.into());
+1833        assert!(expanded);
+1834        assert_eq!(server.receive_window, new_receive_window as u64);
+1835        assert_eq!(server.local_max_data, new_receive_window as u64);
+1836        assert_eq!(server.receive_window_shrink_debt, 0);
+1837        let prev_local_max_data = server.local_max_data;
+1838
+1839        // credit, expecting all of them added to local_max_data
+1840        let credits = 1024u64;
+1841        let should_transmit = server.add_read_credits(credits);
+1842        assert_eq!(server.receive_window_shrink_debt, 0);
+1843        assert_eq!(server.local_max_data, prev_local_max_data + credits);
+1844        assert!(should_transmit.should_transmit());
+1845    }
+1846
+1847    #[test]
+1848    fn shrink_receive_window() {
+1849        let mut server = make(Side::Server);
+1850        let new_receive_window = server.receive_window as u32 / 2;
+1851        let prev_local_max_data = server.local_max_data;
+1852
+1853        // shrink the receive_winbow, local_max_data is not expected to be changed
+1854        let shrink_diff = server.receive_window - new_receive_window as u64;
+1855        let expanded = server.set_receive_window(new_receive_window.into());
+1856        assert!(!expanded);
+1857        assert_eq!(server.receive_window, new_receive_window as u64);
+1858        assert_eq!(server.local_max_data, prev_local_max_data);
+1859        assert_eq!(server.receive_window_shrink_debt, shrink_diff);
+1860        let prev_local_max_data = server.local_max_data;
+1861
+1862        // credit twice, local_max_data does not change as it is absorbed by receive_window_shrink_debt
+1863        let credits = 1024u64;
+1864        for _ in 0..2 {
+1865            let expected_receive_window_shrink_debt = server.receive_window_shrink_debt - credits;
+1866            let should_transmit = server.add_read_credits(credits);
+1867            assert_eq!(
+1868                server.receive_window_shrink_debt,
+1869                expected_receive_window_shrink_debt
+1870            );
+1871            assert_eq!(server.local_max_data, prev_local_max_data);
+1872            assert!(!should_transmit.should_transmit());
+1873        }
+1874
+1875        // credit again which exceeds all remaining expected_receive_window_shrink_debt
+1876        let credits = 1024 * 512;
+1877        let prev_local_max_data = server.local_max_data;
+1878        let expected_local_max_data =
+1879            server.local_max_data + (credits - server.receive_window_shrink_debt);
+1880        let _should_transmit = server.add_read_credits(credits);
+1881        assert_eq!(server.receive_window_shrink_debt, 0);
+1882        assert_eq!(server.local_max_data, expected_local_max_data);
+1883        assert!(server.local_max_data > prev_local_max_data);
+1884
+1885        // credit again, all should be added to local_max_data
+1886        let credits = 1024 * 512;
+1887        let expected_local_max_data = server.local_max_data + credits;
+1888        let should_transmit = server.add_read_credits(credits);
+1889        assert_eq!(server.receive_window_shrink_debt, 0);
+1890        assert_eq!(server.local_max_data, expected_local_max_data);
+1891        assert!(should_transmit.should_transmit());
+1892    }
+1893
+1894    #[test]
+1895    fn expand_send_window() {
+1896        let mut server = make(Side::Server);
+1897
+1898        let initial_send_window = server.send_window;
+1899        let larger_send_window = initial_send_window * 2;
+1900
+1901        // Set `initial_max_data` larger than `send_window` so we're limited by local flow control
+1902        server.set_params(&TransportParameters {
+1903            initial_max_data: VarInt::MAX,
+1904            initial_max_stream_data_uni: VarInt::MAX,
+1905            initial_max_streams_uni: VarInt::from_u32(100),
+1906            ..TransportParameters::default()
+1907        });
+1908
+1909        assert_eq!(server.write_limit(), initial_send_window);
+1910        assert_eq!(server.poll(), None);
+1911
+1912        let mut retransmits = Retransmits::default();
+1913        let conn_state = ConnState::established();
+1914
+1915        let stream_id = Streams {
+1916            state: &mut server,
+1917            conn_state: &conn_state,
+1918        }
+1919        .open(Dir::Uni)
+1920        .expect("should be able to open a stream");
+1921
+1922        let mut stream = SendStream {
+1923            id: stream_id,
+1924            state: &mut server,
+1925            pending: &mut retransmits,
+1926            conn_state: &conn_state,
+1927        };
+1928
+1929        // Check that the stream accepts `initial_send_window` bytes
+1930        let initial_send_len = initial_send_window as usize;
+1931        let data = vec![0xFFu8; initial_send_len];
+1932
+1933        assert_eq!(stream.write(&data), Ok(initial_send_len));
+1934
+1935        // Try to write the same data again, observe that it's blocked
+1936        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+1937
+1938        // Check that we get a `Writable` event after increasing the send window
+1939        stream.state.set_send_window(larger_send_window);
+1940        assert_eq!(
+1941            stream.state.poll(),
+1942            Some(StreamEvent::Writable { id: stream_id })
+1943        );
+1944
+1945        // Check that the stream accepts the exact same amount of data again
+1946        assert_eq!(stream.write(&data), Ok(initial_send_len));
+1947        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+1948
+1949        assert_eq!(stream.state.poll(), None);
+1950
+1951        // Ack the data
+1952        stream.state.received_ack_of(frame::StreamMeta {
+1953            id: stream_id,
+1954            offsets: 0..larger_send_window,
+1955            fin: false,
+1956        });
+1957
+1958        assert_eq!(
+1959            stream.state.poll(),
+1960            Some(StreamEvent::Writable { id: stream_id })
+1961        );
+1962
+1963        // Check that our full send window is available again
+1964        assert_eq!(stream.write(&data), Ok(initial_send_len));
+1965        assert_eq!(stream.write(&data), Ok(initial_send_len));
+1966        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+1967    }
+1968
+1969    #[test]
+1970    fn shrink_send_window() {
+1971        let mut server = make(Side::Server);
+1972
+1973        let initial_send_window = server.send_window;
+1974        let smaller_send_window = server.send_window / 2;
+1975
+1976        // Set `initial_max_data` larger than `send_window` so we're limited by local flow control
+1977        server.set_params(&TransportParameters {
+1978            initial_max_data: VarInt::MAX,
+1979            initial_max_stream_data_uni: VarInt::MAX,
+1980            initial_max_streams_uni: VarInt::from_u32(100),
+1981            ..TransportParameters::default()
+1982        });
+1983
+1984        assert_eq!(server.write_limit(), initial_send_window);
+1985        assert_eq!(server.poll(), None);
+1986
+1987        let mut retransmits = Retransmits::default();
+1988        let conn_state = ConnState::established();
+1989
+1990        let stream_id = Streams {
+1991            state: &mut server,
+1992            conn_state: &conn_state,
+1993        }
+1994        .open(Dir::Uni)
+1995        .expect("should be able to open a stream");
+1996
+1997        let mut stream = SendStream {
+1998            id: stream_id,
+1999            state: &mut server,
+2000            pending: &mut retransmits,
+2001            conn_state: &conn_state,
+2002        };
+2003
+2004        let initial_send_len = initial_send_window as usize;
+2005
+2006        let data = vec![0xFFu8; initial_send_len];
+2007
+2008        // Assert that the full send window is accepted
+2009        assert_eq!(stream.write(&data), Ok(initial_send_len));
+2010        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2011
+2012        assert_eq!(stream.state.write_limit(), 0);
+2013        assert_eq!(stream.state.poll(), None);
+2014
+2015        // Shrink our send window, assert that it's still not writable
+2016        stream.state.set_send_window(smaller_send_window);
+2017        assert_eq!(stream.state.write_limit(), 0);
+2018        assert_eq!(stream.state.poll(), None);
+2019
+2020        // Assert that data is still not accepted
+2021        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2022
+2023        // Ack some data, assert that writes are still not accepted due to outstanding sends
+2024        stream.state.received_ack_of(frame::StreamMeta {
+2025            id: stream_id,
+2026            offsets: 0..smaller_send_window,
+2027            fin: false,
+2028        });
+2029
+2030        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2031
+2032        // Ack the rest of the data
+2033        stream.state.received_ack_of(frame::StreamMeta {
+2034            id: stream_id,
+2035            offsets: smaller_send_window..initial_send_window,
+2036            fin: false,
+2037        });
+2038
+2039        // This should generate a `Writable` event
+2040        assert_eq!(
+2041            stream.state.poll(),
+2042            Some(StreamEvent::Writable { id: stream_id })
+2043        );
+2044        assert_eq!(stream.state.write_limit(), smaller_send_window);
+2045
+2046        // Assert that only `smaller_send_window` bytes are accepted
+2047        assert_eq!(stream.write(&data), Ok(smaller_send_window as usize));
+2048        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2049    }
+2050}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/timer.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/timer.rs.html new file mode 100644 index 000000000..f89cc252c --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/timer.rs.html @@ -0,0 +1,498 @@ +timer.rs - source

iroh_quinn_proto/connection/
timer.rs

1use identity_hash::{IdentityHashable, IntMap};
+2
+3use crate::{
+4    Instant,
+5    connection::qlog::{QlogSink, QlogSinkWithTime},
+6};
+7
+8use super::PathId;
+9
+10#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
+11pub(crate) enum Timer {
+12    /// Per connection timers.
+13    Conn(ConnTimer),
+14    /// Per path timers.
+15    PerPath(PathId, PathTimer),
+16}
+17
+18#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
+19pub(crate) enum ConnTimer {
+20    /// When to close the connection after no activity
+21    Idle = 0,
+22    /// When the close timer expires, the connection has been gracefully terminated.
+23    Close = 1,
+24    /// When keys are discarded because they should not be needed anymore
+25    KeyDiscard = 2,
+26    /// When to send a `PING` frame to keep the connection alive
+27    KeepAlive = 3,
+28    /// When to invalidate old CID and proactively push new one via NEW_CONNECTION_ID frame
+29    PushNewCid = 4,
+30}
+31
+32impl ConnTimer {
+33    const VALUES: [Self; 5] = [
+34        Self::Idle,
+35        Self::Close,
+36        Self::KeyDiscard,
+37        Self::KeepAlive,
+38        Self::PushNewCid,
+39    ];
+40}
+41
+42#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
+43pub(crate) enum PathTimer {
+44    /// When to send an ack-eliciting probe packet or declare unacked packets lost
+45    LossDetection = 0,
+46    /// When to abandon a path after no activity
+47    PathIdle = 1,
+48    /// When to give up on validating a new path from RFC9000 migration
+49    PathValidation = 2,
+50    /// When to resend a path challenge deemed lost
+51    PathChallengeLost = 3,
+52    /// When to give up on validating a new (multi)path
+53    PathOpen = 4,
+54    /// When to send a `PING` frame to keep the path alive
+55    PathKeepAlive = 5,
+56    /// When pacing will allow us to send a packet
+57    Pacing = 6,
+58    /// When to send an immediate ACK if there are unacked ack-eliciting packets of the peer
+59    MaxAckDelay = 7,
+60    /// When to clean up state for an abandoned path
+61    DiscardPath = 8,
+62}
+63
+64impl PathTimer {
+65    const VALUES: [Self; 9] = [
+66        Self::LossDetection,
+67        Self::PathIdle,
+68        Self::PathValidation,
+69        Self::PathChallengeLost,
+70        Self::PathOpen,
+71        Self::PathKeepAlive,
+72        Self::Pacing,
+73        Self::MaxAckDelay,
+74        Self::DiscardPath,
+75    ];
+76}
+77
+78/// Keeps track of the nearest timeout for each `Timer`
+79///
+80/// The [`TimerTable`] is advanced with [`TimerTable::expire_before`].
+81#[derive(Debug, Clone, Default)]
+82pub(crate) struct TimerTable {
+83    generic: [Option<Instant>; ConnTimer::VALUES.len()],
+84    path_timers: SmallMap<PathId, PathTimerTable, STACK_TIMERS>,
+85}
+86
+87/// For how many paths we keep the timers on the stack, before spilling onto the heap.
+88const STACK_TIMERS: usize = 4;
+89
+90/// Works like a `HashMap` but stores up to `SIZE` items on the stack.
+91#[derive(Debug, Clone)]
+92struct SmallMap<K, V, const SIZE: usize> {
+93    stack: [Option<(K, V)>; SIZE],
+94    heap: Option<IntMap<K, V>>,
+95}
+96
+97impl<K, V, const SIZE: usize> Default for SmallMap<K, V, SIZE> {
+98    fn default() -> Self {
+99        Self {
+100            stack: [const { None }; SIZE],
+101            heap: None,
+102        }
+103    }
+104}
+105
+106impl<K, V, const SIZE: usize> SmallMap<K, V, SIZE>
+107where
+108    K: std::cmp::Eq + std::hash::Hash + IdentityHashable,
+109{
+110    fn insert(&mut self, key: K, value: V) -> Option<V> {
+111        // check stack for space
+112        for el in self.stack.iter_mut() {
+113            match el {
+114                Some((k, v)) => {
+115                    if *k == key {
+116                        let old_value = std::mem::replace(v, value);
+117                        return Some(old_value);
+118                    }
+119                }
+120                None => {
+121                    // make sure to remove a potentially old value from the heap
+122                    let old_heap = self.heap.as_mut().and_then(|h| h.remove(&key));
+123                    *el = Some((key, value));
+124
+125                    return old_heap;
+126                }
+127            }
+128        }
+129
+130        // No space on the stack, use the heap
+131        let heap = self.heap.get_or_insert_default();
+132        heap.insert(key, value)
+133    }
+134
+135    #[cfg(test)]
+136    fn remove(&mut self, key: &K) -> Option<V> {
+137        for el in self.stack.iter_mut() {
+138            if let Some((k, _)) = el
+139                && key == k
+140            {
+141                return el.take().map(|(_, v)| v);
+142            }
+143        }
+144
+145        self.heap.as_mut().and_then(|h| h.remove(key))
+146    }
+147
+148    #[cfg(test)]
+149    fn get(&self, key: &K) -> Option<&V> {
+150        for (k, v) in self.stack.iter().filter_map(|v| v.as_ref()) {
+151            if k == key {
+152                return Some(v);
+153            }
+154        }
+155
+156        self.heap.as_ref().and_then(|h| h.get(key))
+157    }
+158
+159    fn get_mut(&mut self, key: &K) -> Option<&mut V> {
+160        for (k, v) in self.stack.iter_mut().filter_map(|v| v.as_mut()) {
+161            if k == key {
+162                return Some(v);
+163            }
+164        }
+165
+166        self.heap.as_mut().and_then(|h| h.get_mut(key))
+167    }
+168
+169    #[cfg(test)]
+170    fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
+171        let a = self
+172            .stack
+173            .iter()
+174            .filter_map(|v| v.as_ref().map(|(k, v)| (k, v)));
+175        let b = self.heap.iter().flat_map(|h| h.iter());
+176        a.chain(b)
+177    }
+178
+179    fn values(&self) -> impl Iterator<Item = &V> {
+180        let a = self.stack.iter().filter_map(|v| v.as_ref().map(|(_, v)| v));
+181        let b = self.heap.iter().flat_map(|h| h.values());
+182        a.chain(b)
+183    }
+184
+185    fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
+186        let a = self
+187            .stack
+188            .iter_mut()
+189            .filter_map(|v| v.as_mut().map(|(k, v)| (&*k, v)));
+190        let b = self.heap.iter_mut().flat_map(|h| h.iter_mut());
+191        a.chain(b)
+192    }
+193
+194    fn retain<F>(&mut self, mut f: F)
+195    where
+196        F: FnMut(&K, &mut V) -> bool,
+197    {
+198        let mut to_remove = [false; SIZE];
+199        for (i, el) in self.stack.iter_mut().enumerate() {
+200            if let Some((key, value)) = el {
+201                to_remove[i] = !f(key, value);
+202            }
+203        }
+204        for (i, to_remove) in to_remove.into_iter().enumerate() {
+205            if to_remove {
+206                self.stack[i] = None;
+207            }
+208        }
+209
+210        if let Some(ref mut heap) = self.heap {
+211            heap.retain(f);
+212        }
+213    }
+214
+215    fn clear(&mut self) {
+216        for el in self.stack.iter_mut() {
+217            *el = None;
+218        }
+219        self.heap = None;
+220    }
+221}
+222
+223#[derive(Debug, Clone, Copy, Default)]
+224struct PathTimerTable {
+225    timers: [Option<Instant>; PathTimer::VALUES.len()],
+226}
+227
+228impl PathTimerTable {
+229    fn set(&mut self, timer: PathTimer, time: Instant) {
+230        self.timers[timer as usize] = Some(time);
+231    }
+232
+233    fn stop(&mut self, timer: PathTimer) {
+234        self.timers[timer as usize] = None;
+235    }
+236
+237    /// Remove the next timer up until `now`, including it
+238    fn expire_before(&mut self, now: Instant) -> Option<(PathTimer, Instant)> {
+239        for timer in PathTimer::VALUES {
+240            if self.timers[timer as usize].is_some()
+241                && self.timers[timer as usize].expect("checked") <= now
+242            {
+243                return self.timers[timer as usize].take().map(|time| (timer, time));
+244            }
+245        }
+246
+247        None
+248    }
+249}
+250
+251impl TimerTable {
+252    /// Sets the timer unconditionally.
+253    ///
+254    /// If the timer is already set, this will change the timer's value.
+255    pub(super) fn set(&mut self, timer: Timer, time: Instant, qlog: QlogSinkWithTime<'_>) {
+256        match timer {
+257            Timer::Conn(timer) => {
+258                self.generic[timer as usize] = Some(time);
+259            }
+260            Timer::PerPath(path_id, timer) => match self.path_timers.get_mut(&path_id) {
+261                None => {
+262                    let mut table = PathTimerTable::default();
+263                    table.set(timer, time);
+264                    self.path_timers.insert(path_id, table);
+265                }
+266                Some(table) => {
+267                    table.set(timer, time);
+268                }
+269            },
+270        }
+271        qlog.emit_timer_set(timer, time);
+272    }
+273
+274    pub(super) fn set_or_stop(
+275        &mut self,
+276        timer: Timer,
+277        time: Option<Instant>,
+278        qlog: QlogSinkWithTime<'_>,
+279    ) {
+280        match time {
+281            Some(time) => self.set(timer, time, qlog),
+282            None => self.stop(timer, qlog),
+283        }
+284    }
+285
+286    pub(super) fn stop(&mut self, timer: Timer, qlog: QlogSinkWithTime<'_>) {
+287        match timer {
+288            Timer::Conn(timer) => {
+289                self.generic[timer as usize] = None;
+290            }
+291            Timer::PerPath(path_id, timer) => {
+292                if let Some(e) = self.path_timers.get_mut(&path_id) {
+293                    e.stop(timer);
+294                }
+295            }
+296        }
+297        qlog.emit_timer_stop(timer);
+298    }
+299
+300    /// Stops all per-path timers
+301    pub(super) fn stop_per_path(&mut self, path_id: PathId, qlog: QlogSinkWithTime<'_>) {
+302        for timer in PathTimer::VALUES {
+303            if let Some(e) = self.path_timers.get_mut(&path_id) {
+304                e.stop(timer);
+305                qlog.emit_timer_stop(Timer::PerPath(path_id, timer));
+306            }
+307        }
+308    }
+309
+310    /// Get the next queued timeout
+311    pub(super) fn peek(&mut self) -> Option<Instant> {
+312        // TODO: this is currently linear in the number of paths
+313
+314        let min_generic = self.generic.iter().filter_map(|&x| x).min();
+315        let min_path = self
+316            .path_timers
+317            .values()
+318            .flat_map(|p| p.timers.iter().filter_map(|&x| x))
+319            .min();
+320
+321        match (min_generic, min_path) {
+322            (None, None) => None,
+323            (Some(val), None) => Some(val),
+324            (Some(a), Some(b)) => Some(a.min(b)),
+325            (None, Some(val)) => Some(val),
+326        }
+327    }
+328
+329    /// Remove the next timer up until `now`, including it
+330    pub(super) fn expire_before(
+331        &mut self,
+332        now: Instant,
+333        qlog: &QlogSink,
+334    ) -> Option<(Timer, Instant)> {
+335        let (timer, instant) = self.expire_before_inner(now)?;
+336        qlog.with_time(now).emit_timer_expire(timer);
+337        Some((timer, instant))
+338    }
+339
+340    fn expire_before_inner(&mut self, now: Instant) -> Option<(Timer, Instant)> {
+341        // TODO: this is currently linear in the number of paths
+342
+343        for timer in ConnTimer::VALUES {
+344            if self.generic[timer as usize].is_some()
+345                && self.generic[timer as usize].expect("checked") <= now
+346            {
+347                return self.generic[timer as usize]
+348                    .take()
+349                    .map(|time| (Timer::Conn(timer), time));
+350            }
+351        }
+352
+353        let mut res = None;
+354        for (path_id, timers) in self.path_timers.iter_mut() {
+355            if let Some((timer, time)) = timers.expire_before(now) {
+356                res = Some((Timer::PerPath(*path_id, timer), time));
+357                break;
+358            }
+359        }
+360
+361        // clear out old timers
+362        self.path_timers
+363            .retain(|_path_id, timers| timers.timers.iter().any(|t| t.is_some()));
+364        res
+365    }
+366
+367    pub(super) fn reset(&mut self) {
+368        for timer in ConnTimer::VALUES {
+369            self.generic[timer as usize] = None;
+370        }
+371        self.path_timers.clear();
+372    }
+373
+374    #[cfg(test)]
+375    pub(super) fn values(&self) -> Vec<(Timer, Instant)> {
+376        let mut values = Vec::new();
+377
+378        for timer in ConnTimer::VALUES {
+379            if let Some(time) = self.generic[timer as usize] {
+380                values.push((Timer::Conn(timer), time));
+381            }
+382        }
+383
+384        for timer in PathTimer::VALUES {
+385            for (path_id, timers) in self.path_timers.iter() {
+386                if let Some(time) = timers.timers[timer as usize] {
+387                    values.push((Timer::PerPath(*path_id, timer), time));
+388                }
+389            }
+390        }
+391
+392        values
+393    }
+394}
+395
+396#[cfg(test)]
+397mod tests {
+398    use std::time::Duration;
+399
+400    use crate::connection::qlog::QlogSink;
+401
+402    use super::*;
+403
+404    #[test]
+405    fn timer_table() {
+406        let mut timers = TimerTable::default();
+407        let sec = Duration::from_secs(1);
+408        let now = Instant::now() + Duration::from_secs(10);
+409        timers.set(
+410            Timer::Conn(ConnTimer::Idle),
+411            now - 3 * sec,
+412            QlogSink::default().with_time(now),
+413        );
+414        timers.set(
+415            Timer::Conn(ConnTimer::Close),
+416            now - 2 * sec,
+417            QlogSink::default().with_time(now),
+418        );
+419
+420        assert_eq!(timers.peek(), Some(now - 3 * sec));
+421        assert_eq!(
+422            timers.expire_before(now, &QlogSink::default()),
+423            Some((Timer::Conn(ConnTimer::Idle), now - 3 * sec))
+424        );
+425        assert_eq!(
+426            timers.expire_before(now, &QlogSink::default()),
+427            Some((Timer::Conn(ConnTimer::Close), now - 2 * sec))
+428        );
+429        assert_eq!(timers.expire_before(now, &QlogSink::default()), None);
+430    }
+431
+432    #[test]
+433    fn test_small_map() {
+434        let mut map = SmallMap::<usize, usize, 2>::default();
+435
+436        // inserts only on the stack
+437        assert_eq!(map.insert(1, 1), None);
+438        assert!(map.heap.is_none());
+439        assert_eq!(map.insert(2, 2), None);
+440        assert!(map.heap.is_none());
+441
+442        // replace on the stack
+443        assert_eq!(map.insert(1, 2), Some(1));
+444
+445        assert_eq!(map.remove(&1), Some(2));
+446        assert_eq!(map.insert(3, 3), None);
+447        assert!(map.heap.is_none());
+448
+449        // spill
+450        assert_eq!(map.insert(4, 4), None);
+451        assert!(map.heap.is_some());
+452
+453        assert_eq!(
+454            map.iter()
+455                .map(|(&a, &b)| (a, b))
+456                .collect::<Vec<(usize, usize)>>(),
+457            vec![(3, 3), (2, 2), (4, 4)]
+458        );
+459        assert_eq!(
+460            map.iter()
+461                .map(|(a, b)| (*a, *b))
+462                .collect::<Vec<(usize, usize)>>(),
+463            map.iter_mut()
+464                .map(|(a, b)| (*a, *b))
+465                .collect::<Vec<(usize, usize)>>(),
+466        );
+467
+468        assert_eq!(map.heap.as_ref().unwrap().len(), 1);
+469
+470        for i in 0..10 {
+471            map.insert(10 + i, 10 + i);
+472        }
+473        assert_eq!(map.heap.as_ref().unwrap().len(), 11);
+474        map.retain(|k, _v| *k < 10);
+475
+476        assert_eq!(map.heap.as_ref().unwrap().len(), 1);
+477
+478        assert_eq!(
+479            map.iter()
+480                .map(|(&a, &b)| (a, b))
+481                .collect::<Vec<(usize, usize)>>(),
+482            vec![(3, 3), (2, 2), (4, 4)]
+483        );
+484
+485        assert_eq!(
+486            map.iter()
+487                .map(|(a, b)| (*a, *b))
+488                .collect::<Vec<(usize, usize)>>(),
+489            map.iter_mut()
+490                .map(|(a, b)| (*a, *b))
+491                .collect::<Vec<(usize, usize)>>(),
+492        );
+493
+494        map.clear();
+495        assert_eq!(map.iter().collect::<Vec<_>>(), Vec::new());
+496        assert!(map.heap.is_none());
+497    }
+498}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/connection/transmit_buf.rs.html b/pr/395/docs/src/iroh_quinn_proto/connection/transmit_buf.rs.html new file mode 100644 index 000000000..11e97c560 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/connection/transmit_buf.rs.html @@ -0,0 +1,236 @@ +transmit_buf.rs - source

iroh_quinn_proto/connection/
transmit_buf.rs

1use std::num::NonZeroUsize;
+2
+3use bytes::BufMut;
+4use tracing::trace;
+5
+6use crate::packet::BufLen;
+7
+8/// The buffer in which to write datagrams for [`Connection::poll_transmit`]
+9///
+10/// The `poll_transmit` function writes zero or more datagrams to a buffer. Multiple
+11/// datagrams are possible in case GSO (Generic Segmentation Offload) is supported.
+12///
+13/// This buffer tracks datagrams being written to it. There is always a "current" datagram,
+14/// which is started by calling [`TransmitBuf::start_new_datagram`]. Writing to the buffer
+15/// is done through the [`BufMut`] interface.
+16///
+17/// Usually a datagram contains one QUIC packet, though QUIC-TRANSPORT 12.2 Coalescing
+18/// Packets allows for placing multiple packets into a single datagram provided all but the
+19/// last packet uses long headers. This is normally used during connection setup where often
+20/// the initial, handshake and sometimes even a 1-RTT packet can be coalesced into a single
+21/// datagram.
+22///
+23/// Inside a single packet multiple QUIC frames are written.
+24///
+25/// The buffer managed here is passed straight to the OS' `sendmsg` call (or variant) once
+26/// `poll_transmit` returns.  So needs to contain the datagrams as they are sent on the
+27/// wire.
+28///
+29/// [`Connection::poll_transmit`]: super::Connection::poll_transmit
+30#[derive(Debug)]
+31pub(super) struct TransmitBuf<'a> {
+32    /// The buffer itself, packets are written to this buffer
+33    buf: &'a mut Vec<u8>,
+34    /// Offset into the buffer at which the current datagram starts
+35    ///
+36    /// Note that when coalescing packets this might be before the start of the current
+37    /// packet.
+38    datagram_start: usize,
+39    /// The maximum offset allowed to be used for the current datagram in the buffer
+40    ///
+41    /// The first and last datagram in a batch are allowed to be smaller then the maximum
+42    /// size. All datagrams in between need to be exactly this size.
+43    buf_capacity: usize,
+44    /// The maximum number of datagrams allowed to write into [`TransmitBuf::buf`]
+45    max_datagrams: NonZeroUsize,
+46    /// The number of datagrams already (partially) written into the buffer
+47    ///
+48    /// Incremented by a call to [`TransmitBuf::start_new_datagram`].
+49    pub(super) num_datagrams: usize,
+50    /// The segment size of this GSO batch
+51    ///
+52    /// The segment size is the size of each datagram in the GSO batch, only the last
+53    /// datagram in the batch may be smaller.
+54    ///
+55    /// For the first datagram this is set to the maximum size a datagram is allowed to be:
+56    /// the current path MTU. After the first datagram is finished this is reduced to the
+57    /// size of the first datagram and can no longer change.
+58    segment_size: usize,
+59}
+60
+61impl<'a> TransmitBuf<'a> {
+62    pub(super) fn new(buf: &'a mut Vec<u8>, max_datagrams: NonZeroUsize, mtu: usize) -> Self {
+63        buf.clear();
+64        Self {
+65            buf,
+66            datagram_start: 0,
+67            buf_capacity: 0,
+68            max_datagrams,
+69            num_datagrams: 0,
+70            segment_size: mtu,
+71        }
+72    }
+73
+74    /// Starts a datagram with a custom datagram size
+75    ///
+76    /// This is a specialized version of [`TransmitBuf::start_new_datagram`] which sets the
+77    /// datagram size. Useful for e.g. PATH_CHALLENGE, tail-loss probes or MTU probes.
+78    ///
+79    /// After the first datagram you can never increase the segment size. If you decrease
+80    /// the size of a datagram in a batch, it must be the last datagram of the batch.
+81    pub(super) fn start_new_datagram_with_size(&mut self, datagram_size: usize) {
+82        // Only reserve space for this datagram, usually it is the last one in the batch.
+83        let max_capacity_hint = datagram_size;
+84        self.new_datagram_inner(datagram_size, max_capacity_hint)
+85    }
+86
+87    /// Starts a new datagram in the transmit buffer
+88    ///
+89    /// If this starts the second datagram the segment size will be set to the size of the
+90    /// first datagram.
+91    ///
+92    /// If the underlying buffer does not have enough capacity yet this will allocate enough
+93    /// capacity for all the datagrams allowed in a single batch. Use
+94    /// [`TransmitBuf::start_new_datagram_with_size`] if you know you will need less.
+95    pub(super) fn start_new_datagram(&mut self) {
+96        // We reserve the maximum space for sending `max_datagrams` upfront to avoid any
+97        // reallocations if more datagrams have to be appended later on.  Benchmarks have
+98        // shown a 5-10% throughput improvement compared to continuously resizing the
+99        // datagram buffer. While this will lead to over-allocation for small transmits
+100        // (e.g. purely containing ACKs), modern memory allocators (e.g. mimalloc and
+101        // jemalloc) will pool certain allocation sizes and therefore this is still rather
+102        // efficient.
+103        let max_capacity_hint = self.max_datagrams.get() * self.segment_size;
+104        self.new_datagram_inner(self.segment_size, max_capacity_hint)
+105    }
+106
+107    fn new_datagram_inner(&mut self, datagram_size: usize, max_capacity_hint: usize) {
+108        debug_assert!(self.num_datagrams < self.max_datagrams.into());
+109        if self.num_datagrams == 1 {
+110            // Set the segment size to the size of the first datagram.
+111            self.segment_size = self.buf.len();
+112        }
+113        if self.num_datagrams >= 1 {
+114            debug_assert!(datagram_size <= self.segment_size);
+115            if datagram_size < self.segment_size {
+116                // If this is a GSO batch and this datagram is smaller than the segment
+117                // size, this must be the last datagram in the batch.
+118                self.max_datagrams = NonZeroUsize::MIN.saturating_add(self.num_datagrams);
+119            }
+120        }
+121        self.datagram_start = self.buf.len();
+122        debug_assert_eq!(
+123            self.datagram_start % self.segment_size,
+124            0,
+125            "datagrams in a GSO batch must be aligned to the segment size"
+126        );
+127        self.buf_capacity = self.datagram_start + datagram_size;
+128        if self.buf_capacity > self.buf.capacity() {
+129            self.buf
+130                .reserve_exact(max_capacity_hint.saturating_sub(self.buf.capacity()));
+131        }
+132        self.num_datagrams += 1;
+133    }
+134
+135    /// Clips the segment size to the current size
+136    ///
+137    /// Only valid for the first datagram, when the datagram might be smaller than the
+138    /// segment size. Needed before estimating the available space in the next datagram
+139    /// based on [`TransmitBuf::segment_size`].
+140    ///
+141    /// Use [`TransmitBuf::start_new_datagram_with_size`] if you need to reduce the size of
+142    /// the last datagram in a batch.
+143    pub(super) fn clip_segment_size(&mut self) {
+144        debug_assert_eq!(self.num_datagrams, 1);
+145        if self.buf.len() < self.segment_size {
+146            trace!(
+147                segment_size = self.buf.len(),
+148                prev_segment_size = self.segment_size,
+149                "clipped datagram size"
+150            );
+151        }
+152        self.segment_size = self.buf.len();
+153        self.buf_capacity = self.buf.len();
+154    }
+155
+156    /// Returns the GSO segment size
+157    ///
+158    /// This is also the maximum size datagrams are allowed to be. The first and last
+159    /// datagram in a batch are allowed to be smaller however. After the first datagram the
+160    /// segment size is clipped to the size of the first datagram.
+161    ///
+162    /// If the last datagram was created using [`TransmitBuf::start_new_datagram_with_size`]
+163    /// the the segment size will be greater than the current datagram is allowed to be.
+164    /// Thus [`TransmitBuf::datagram_remaining_mut`] should be used if you need to know the
+165    /// amount of data that can be written into the datagram.
+166    pub(super) fn segment_size(&self) -> usize {
+167        self.segment_size
+168    }
+169
+170    /// Returns the number of datagrams written into the buffer
+171    ///
+172    /// The last datagram is not necessarily finished yet.
+173    pub(super) fn num_datagrams(&self) -> usize {
+174        self.num_datagrams
+175    }
+176
+177    /// Returns the maximum number of datagrams allowed to be written into the buffer
+178    pub(super) fn max_datagrams(&self) -> NonZeroUsize {
+179        self.max_datagrams
+180    }
+181
+182    /// Returns the start offset of the current datagram in the buffer
+183    ///
+184    /// In other words, this offset contains the first byte of the current datagram.
+185    pub(super) fn datagram_start_offset(&self) -> usize {
+186        self.datagram_start
+187    }
+188
+189    /// Returns the maximum offset in the buffer allowed for the current datagram
+190    ///
+191    /// The first and last datagram in a batch are allowed to be smaller then the maximum
+192    /// size. All datagrams in between need to be exactly this size.
+193    pub(super) fn datagram_max_offset(&self) -> usize {
+194        self.buf_capacity
+195    }
+196
+197    /// Returns the number of bytes that may still be written into this datagram
+198    pub(super) fn datagram_remaining_mut(&self) -> usize {
+199        self.buf_capacity.saturating_sub(self.buf.len())
+200    }
+201
+202    /// Returns `true` if the buffer did not have anything written into it
+203    pub(super) fn is_empty(&self) -> bool {
+204        self.len() == 0
+205    }
+206
+207    /// The number of bytes written into the buffer so far
+208    pub(super) fn len(&self) -> usize {
+209        self.buf.len()
+210    }
+211
+212    /// Returns the already written bytes in the buffer
+213    pub(super) fn as_mut_slice(&mut self) -> &mut [u8] {
+214        self.buf.as_mut_slice()
+215    }
+216}
+217
+218unsafe impl BufMut for TransmitBuf<'_> {
+219    fn remaining_mut(&self) -> usize {
+220        self.buf.remaining_mut()
+221    }
+222
+223    unsafe fn advance_mut(&mut self, cnt: usize) {
+224        unsafe { self.buf.advance_mut(cnt) };
+225    }
+226
+227    fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
+228        self.buf.chunk_mut()
+229    }
+230}
+231
+232impl BufLen for TransmitBuf<'_> {
+233    fn len(&self) -> usize {
+234        self.len()
+235    }
+236}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/constant_time.rs.html b/pr/395/docs/src/iroh_quinn_proto/constant_time.rs.html new file mode 100644 index 000000000..1e5f93742 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/constant_time.rs.html @@ -0,0 +1,22 @@ +constant_time.rs - source

iroh_quinn_proto/
constant_time.rs

1// This function is non-inline to prevent the optimizer from looking inside it.
+2#[inline(never)]
+3fn constant_time_ne(a: &[u8], b: &[u8]) -> u8 {
+4    assert!(a.len() == b.len());
+5
+6    // These useless slices make the optimizer elide the bounds checks.
+7    // See the comment in clone_from_slice() added on Rust commit 6a7bc47.
+8    let len = a.len();
+9    let a = &a[..len];
+10    let b = &b[..len];
+11
+12    let mut tmp = 0;
+13    for i in 0..len {
+14        tmp |= a[i] ^ b[i];
+15    }
+16    tmp // The compare with 0 must happen outside this function.
+17}
+18
+19/// Compares byte strings in constant time.
+20pub(crate) fn eq(a: &[u8], b: &[u8]) -> bool {
+21    a.len() == b.len() && constant_time_ne(a, b) == 0
+22}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/crypto.rs.html b/pr/395/docs/src/iroh_quinn_proto/crypto.rs.html new file mode 100644 index 000000000..ab9b332e2 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/crypto.rs.html @@ -0,0 +1,221 @@ +crypto.rs - source

iroh_quinn_proto/
crypto.rs

1//! Traits and implementations for the QUIC cryptography protocol
+2//!
+3//! The protocol logic in Quinn is contained in types that abstract over the actual
+4//! cryptographic protocol used. This module contains the traits used for this
+5//! abstraction layer as well as a single implementation of these traits that uses
+6//! *ring* and rustls to implement the TLS protocol support.
+7//!
+8//! Note that usage of any protocol (version) other than TLS 1.3 does not conform to any
+9//! published versions of the specification, and will not be supported in QUIC v1.
+10
+11use std::{any::Any, str, sync::Arc};
+12
+13use bytes::BytesMut;
+14
+15use crate::{
+16    ConnectError, PathId, Side, TransportError, shared::ConnectionId,
+17    transport_parameters::TransportParameters,
+18};
+19
+20/// Cryptography interface based on *ring*
+21#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+22pub(crate) mod ring_like;
+23/// TLS interface based on rustls
+24#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+25pub mod rustls;
+26
+27/// A cryptographic session (commonly TLS)
+28pub trait Session: Send + Sync + 'static {
+29    /// Create the initial set of keys given the client's initial destination ConnectionId
+30    fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys;
+31
+32    /// Get data negotiated during the handshake, if available
+33    ///
+34    /// Returns `None` until the connection emits `HandshakeDataReady`.
+35    fn handshake_data(&self) -> Option<Box<dyn Any>>;
+36
+37    /// Get the peer's identity, if available
+38    fn peer_identity(&self) -> Option<Box<dyn Any>>;
+39
+40    /// Get the 0-RTT keys if available (clients only)
+41    ///
+42    /// On the client side, this method can be used to see if 0-RTT key material is available
+43    /// to start sending data before the protocol handshake has completed.
+44    ///
+45    /// Returns `None` if the key material is not available. This might happen if you have
+46    /// not connected to this server before.
+47    fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>;
+48
+49    /// If the 0-RTT-encrypted data has been accepted by the peer
+50    fn early_data_accepted(&self) -> Option<bool>;
+51
+52    /// Returns `true` until the connection is fully established.
+53    fn is_handshaking(&self) -> bool;
+54
+55    /// Read bytes of handshake data
+56    ///
+57    /// This should be called with the contents of `CRYPTO` frames. If it returns `Ok`, the
+58    /// caller should call `write_handshake()` to check if the crypto protocol has anything
+59    /// to send to the peer. This method will only return `true` the first time that
+60    /// handshake data is available. Future calls will always return false.
+61    ///
+62    /// On success, returns `true` iff `self.handshake_data()` has been populated.
+63    fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError>;
+64
+65    /// The peer's QUIC transport parameters
+66    ///
+67    /// These are only available after the first flight from the peer has been received.
+68    fn transport_parameters(&self) -> Result<Option<TransportParameters>, TransportError>;
+69
+70    /// Writes handshake bytes into the given buffer and optionally returns the negotiated keys
+71    ///
+72    /// When the handshake proceeds to the next phase, this method will return a new set of
+73    /// keys to encrypt data with.
+74    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>;
+75
+76    /// Compute keys for the next key update
+77    fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>;
+78
+79    /// Verify the integrity of a retry packet
+80    fn is_valid_retry(&self, orig_dst_cid: ConnectionId, header: &[u8], payload: &[u8]) -> bool;
+81
+82    /// Fill `output` with `output.len()` bytes of keying material derived
+83    /// from the [Session]'s secrets, using `label` and `context` for domain
+84    /// separation.
+85    ///
+86    /// This function will fail, returning [ExportKeyingMaterialError],
+87    /// if the requested output length is too large.
+88    fn export_keying_material(
+89        &self,
+90        output: &mut [u8],
+91        label: &[u8],
+92        context: &[u8],
+93    ) -> Result<(), ExportKeyingMaterialError>;
+94}
+95
+96/// A pair of keys for bidirectional communication
+97pub struct KeyPair<T> {
+98    /// Key for encrypting data
+99    pub local: T,
+100    /// Key for decrypting data
+101    pub remote: T,
+102}
+103
+104/// A complete set of keys for a certain packet space
+105pub struct Keys {
+106    /// Header protection keys
+107    pub header: KeyPair<Box<dyn HeaderKey>>,
+108    /// Packet protection keys
+109    pub packet: KeyPair<Box<dyn PacketKey>>,
+110}
+111
+112/// Client-side configuration for the crypto protocol
+113pub trait ClientConfig: Send + Sync {
+114    /// Start a client session with this configuration
+115    fn start_session(
+116        self: Arc<Self>,
+117        version: u32,
+118        server_name: &str,
+119        params: &TransportParameters,
+120    ) -> Result<Box<dyn Session>, ConnectError>;
+121}
+122
+123/// Server-side configuration for the crypto protocol
+124pub trait ServerConfig: Send + Sync {
+125    /// Create the initial set of keys given the client's initial destination ConnectionId
+126    fn initial_keys(&self, version: u32, dst_cid: ConnectionId)
+127    -> Result<Keys, UnsupportedVersion>;
+128
+129    /// Generate the integrity tag for a retry packet
+130    ///
+131    /// Never called if `initial_keys` rejected `version`.
+132    fn retry_tag(&self, version: u32, orig_dst_cid: ConnectionId, packet: &[u8]) -> [u8; 16];
+133
+134    /// Start a server session with this configuration
+135    ///
+136    /// Never called if `initial_keys` rejected `version`.
+137    fn start_session(
+138        self: Arc<Self>,
+139        version: u32,
+140        params: &TransportParameters,
+141    ) -> Box<dyn Session>;
+142}
+143
+144/// Keys used to protect packet payloads
+145pub trait PacketKey: Send + Sync {
+146    /// Encrypt the packet payload with the given packet number
+147    fn encrypt(&self, path_id: PathId, packet: u64, buf: &mut [u8], header_len: usize);
+148    /// Decrypt the packet payload with the given packet number
+149    fn decrypt(
+150        &self,
+151        path_id: PathId,
+152        packet: u64,
+153        header: &[u8],
+154        payload: &mut BytesMut,
+155    ) -> Result<(), CryptoError>;
+156    /// The length of the AEAD tag appended to packets on encryption
+157    fn tag_len(&self) -> usize;
+158    /// Maximum number of packets that may be sent using a single key
+159    fn confidentiality_limit(&self) -> u64;
+160    /// Maximum number of incoming packets that may fail decryption before the connection must be
+161    /// abandoned
+162    fn integrity_limit(&self) -> u64;
+163}
+164
+165/// Keys used to protect packet headers
+166pub trait HeaderKey: Send + Sync {
+167    /// Decrypt the given packet's header
+168    fn decrypt(&self, pn_offset: usize, packet: &mut [u8]);
+169    /// Encrypt the given packet's header
+170    fn encrypt(&self, pn_offset: usize, packet: &mut [u8]);
+171    /// The sample size used for this key's algorithm
+172    fn sample_size(&self) -> usize;
+173}
+174
+175/// A key for signing with HMAC-based algorithms
+176pub trait HmacKey: Send + Sync {
+177    /// Method for signing a message
+178    fn sign(&self, data: &[u8], signature_out: &mut [u8]);
+179    /// Length of `sign`'s output
+180    fn signature_len(&self) -> usize;
+181    /// Method for verifying a message
+182    fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>;
+183}
+184
+185/// Error returned by [Session::export_keying_material].
+186///
+187/// This error occurs if the requested output length is too large.
+188#[derive(Debug, PartialEq, Eq)]
+189pub struct ExportKeyingMaterialError;
+190
+191/// A pseudo random key for HKDF
+192pub trait HandshakeTokenKey: Send + Sync {
+193    /// Derive AEAD using hkdf
+194    fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>;
+195}
+196
+197/// A key for sealing data with AEAD-based algorithms
+198pub trait AeadKey {
+199    /// Method for sealing message `data`
+200    fn seal(&self, data: &mut Vec<u8>, additional_data: &[u8]) -> Result<(), CryptoError>;
+201    /// Method for opening a sealed message `data`
+202    fn open<'a>(
+203        &self,
+204        data: &'a mut [u8],
+205        additional_data: &[u8],
+206    ) -> Result<&'a mut [u8], CryptoError>;
+207}
+208
+209/// Generic crypto errors
+210#[derive(Debug)]
+211pub struct CryptoError;
+212
+213/// Error indicating that the specified QUIC version is not supported
+214#[derive(Debug)]
+215pub struct UnsupportedVersion;
+216
+217impl From<UnsupportedVersion> for ConnectError {
+218    fn from(_: UnsupportedVersion) -> Self {
+219        Self::UnsupportedVersion
+220    }
+221}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/crypto/ring_like.rs.html b/pr/395/docs/src/iroh_quinn_proto/crypto/ring_like.rs.html new file mode 100644 index 000000000..e4c1ba9cb --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/crypto/ring_like.rs.html @@ -0,0 +1,57 @@ +ring_like.rs - source

iroh_quinn_proto/crypto/
ring_like.rs

1#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+2use aws_lc_rs::{aead, error, hkdf, hmac};
+3#[cfg(feature = "ring")]
+4use ring::{aead, error, hkdf, hmac};
+5
+6use crate::crypto::{self, CryptoError};
+7
+8impl crypto::HmacKey for hmac::Key {
+9    fn sign(&self, data: &[u8], out: &mut [u8]) {
+10        out.copy_from_slice(hmac::sign(self, data).as_ref());
+11    }
+12
+13    fn signature_len(&self) -> usize {
+14        32
+15    }
+16
+17    fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError> {
+18        Ok(hmac::verify(self, data, signature)?)
+19    }
+20}
+21
+22impl crypto::HandshakeTokenKey for hkdf::Prk {
+23    fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn crypto::AeadKey> {
+24        let mut key_buffer = [0u8; 32];
+25        let info = [random_bytes];
+26        let okm = self.expand(&info, hkdf::HKDF_SHA256).unwrap();
+27
+28        okm.fill(&mut key_buffer).unwrap();
+29
+30        let key = aead::UnboundKey::new(&aead::AES_256_GCM, &key_buffer).unwrap();
+31        Box::new(aead::LessSafeKey::new(key))
+32    }
+33}
+34
+35impl crypto::AeadKey for aead::LessSafeKey {
+36    fn seal(&self, data: &mut Vec<u8>, additional_data: &[u8]) -> Result<(), CryptoError> {
+37        let aad = aead::Aad::from(additional_data);
+38        let zero_nonce = aead::Nonce::assume_unique_for_key([0u8; 12]);
+39        Ok(self.seal_in_place_append_tag(zero_nonce, aad, data)?)
+40    }
+41
+42    fn open<'a>(
+43        &self,
+44        data: &'a mut [u8],
+45        additional_data: &[u8],
+46    ) -> Result<&'a mut [u8], CryptoError> {
+47        let aad = aead::Aad::from(additional_data);
+48        let zero_nonce = aead::Nonce::assume_unique_for_key([0u8; 12]);
+49        Ok(self.open_in_place(zero_nonce, aad, data)?)
+50    }
+51}
+52
+53impl From<error::Unspecified> for CryptoError {
+54    fn from(_: error::Unspecified) -> Self {
+55        Self
+56    }
+57}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/crypto/rustls.rs.html b/pr/395/docs/src/iroh_quinn_proto/crypto/rustls.rs.html new file mode 100644 index 000000000..9672d2071 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/crypto/rustls.rs.html @@ -0,0 +1,660 @@ +rustls.rs - source

iroh_quinn_proto/crypto/
rustls.rs

1use std::{any::Any, io, str, sync::Arc};
+2
+3#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+4use aws_lc_rs::aead;
+5use bytes::BytesMut;
+6#[cfg(feature = "ring")]
+7use ring::aead;
+8pub use rustls::Error;
+9#[cfg(feature = "__rustls-post-quantum-test")]
+10use rustls::NamedGroup;
+11use rustls::{
+12    self, CipherSuite,
+13    client::danger::ServerCertVerifier,
+14    pki_types::{CertificateDer, PrivateKeyDer, ServerName},
+15    quic::{Connection, HeaderProtectionKey, KeyChange, PacketKey, Secrets, Suite, Version},
+16};
+17#[cfg(feature = "platform-verifier")]
+18use rustls_platform_verifier::BuilderVerifierExt;
+19
+20use crate::{
+21    ConnectError, ConnectionId, PathId, Side, TransportError, TransportErrorCode,
+22    crypto::{
+23        self, CryptoError, ExportKeyingMaterialError, HeaderKey, KeyPair, Keys, UnsupportedVersion,
+24    },
+25    transport_parameters::TransportParameters,
+26};
+27
+28impl From<Side> for rustls::Side {
+29    fn from(s: Side) -> Self {
+30        match s {
+31            Side::Client => Self::Client,
+32            Side::Server => Self::Server,
+33        }
+34    }
+35}
+36
+37/// A rustls TLS session
+38pub struct TlsSession {
+39    version: Version,
+40    got_handshake_data: bool,
+41    next_secrets: Option<Secrets>,
+42    inner: Connection,
+43    suite: Suite,
+44}
+45
+46impl TlsSession {
+47    fn side(&self) -> Side {
+48        match self.inner {
+49            Connection::Client(_) => Side::Client,
+50            Connection::Server(_) => Side::Server,
+51        }
+52    }
+53}
+54
+55impl crypto::Session for TlsSession {
+56    fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys {
+57        initial_keys(self.version, dst_cid, side, &self.suite)
+58    }
+59
+60    fn handshake_data(&self) -> Option<Box<dyn Any>> {
+61        if !self.got_handshake_data {
+62            return None;
+63        }
+64        Some(Box::new(HandshakeData {
+65            protocol: self.inner.alpn_protocol().map(|x| x.into()),
+66            server_name: match self.inner {
+67                Connection::Client(_) => None,
+68                Connection::Server(ref session) => session.server_name().map(|x| x.into()),
+69            },
+70            #[cfg(feature = "__rustls-post-quantum-test")]
+71            negotiated_key_exchange_group: self
+72                .inner
+73                .negotiated_key_exchange_group()
+74                .expect("key exchange group is negotiated")
+75                .name(),
+76        }))
+77    }
+78
+79    /// For the rustls `TlsSession`, the `Any` type is `Vec<rustls::pki_types::CertificateDer>`
+80    fn peer_identity(&self) -> Option<Box<dyn Any>> {
+81        self.inner.peer_certificates().map(|v| -> Box<dyn Any> {
+82            Box::new(
+83                v.iter()
+84                    .map(|v| v.clone().into_owned())
+85                    .collect::<Vec<CertificateDer<'static>>>(),
+86            )
+87        })
+88    }
+89
+90    fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn crypto::PacketKey>)> {
+91        let keys = self.inner.zero_rtt_keys()?;
+92        Some((Box::new(keys.header), Box::new(keys.packet)))
+93    }
+94
+95    fn early_data_accepted(&self) -> Option<bool> {
+96        match self.inner {
+97            Connection::Client(ref session) => Some(session.is_early_data_accepted()),
+98            _ => None,
+99        }
+100    }
+101
+102    fn is_handshaking(&self) -> bool {
+103        self.inner.is_handshaking()
+104    }
+105
+106    fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError> {
+107        self.inner.read_hs(buf).map_err(|e| {
+108            if let Some(alert) = self.inner.alert() {
+109                TransportError {
+110                    code: TransportErrorCode::crypto(alert.into()),
+111                    frame: crate::frame::MaybeFrame::None,
+112                    reason: e.to_string(),
+113                    crypto: Some(Arc::new(e)),
+114                }
+115            } else {
+116                TransportError::PROTOCOL_VIOLATION(format!("TLS error: {e}"))
+117            }
+118        })?;
+119        if !self.got_handshake_data {
+120            // Hack around the lack of an explicit signal from rustls to reflect ClientHello being
+121            // ready on incoming connections, or ALPN negotiation completing on outgoing
+122            // connections.
+123            let have_server_name = match self.inner {
+124                Connection::Client(_) => false,
+125                Connection::Server(ref session) => session.server_name().is_some(),
+126            };
+127            if self.inner.alpn_protocol().is_some() || have_server_name || !self.is_handshaking() {
+128                self.got_handshake_data = true;
+129                return Ok(true);
+130            }
+131        }
+132        Ok(false)
+133    }
+134
+135    fn transport_parameters(&self) -> Result<Option<TransportParameters>, TransportError> {
+136        match self.inner.quic_transport_parameters() {
+137            None => Ok(None),
+138            Some(buf) => match TransportParameters::read(self.side(), &mut io::Cursor::new(buf)) {
+139                Ok(params) => Ok(Some(params)),
+140                Err(e) => Err(e.into()),
+141            },
+142        }
+143    }
+144
+145    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys> {
+146        let keys = match self.inner.write_hs(buf)? {
+147            KeyChange::Handshake { keys } => keys,
+148            KeyChange::OneRtt { keys, next } => {
+149                self.next_secrets = Some(next);
+150                keys
+151            }
+152        };
+153
+154        Some(Keys {
+155            header: KeyPair {
+156                local: Box::new(keys.local.header),
+157                remote: Box::new(keys.remote.header),
+158            },
+159            packet: KeyPair {
+160                local: Box::new(keys.local.packet),
+161                remote: Box::new(keys.remote.packet),
+162            },
+163        })
+164    }
+165
+166    fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn crypto::PacketKey>>> {
+167        let secrets = self.next_secrets.as_mut()?;
+168        let keys = secrets.next_packet_keys();
+169        Some(KeyPair {
+170            local: Box::new(keys.local),
+171            remote: Box::new(keys.remote),
+172        })
+173    }
+174
+175    fn is_valid_retry(&self, orig_dst_cid: ConnectionId, header: &[u8], payload: &[u8]) -> bool {
+176        let tag_start = match payload.len().checked_sub(16) {
+177            Some(x) => x,
+178            None => return false,
+179        };
+180
+181        let mut pseudo_packet =
+182            Vec::with_capacity(header.len() + payload.len() + orig_dst_cid.len() + 1);
+183        pseudo_packet.push(orig_dst_cid.len() as u8);
+184        pseudo_packet.extend_from_slice(&orig_dst_cid);
+185        pseudo_packet.extend_from_slice(header);
+186        let tag_start = tag_start + pseudo_packet.len();
+187        pseudo_packet.extend_from_slice(payload);
+188
+189        let (nonce, key) = match self.version {
+190            Version::V1 => (RETRY_INTEGRITY_NONCE_V1, RETRY_INTEGRITY_KEY_V1),
+191            Version::V1Draft => (RETRY_INTEGRITY_NONCE_DRAFT, RETRY_INTEGRITY_KEY_DRAFT),
+192            _ => unreachable!(),
+193        };
+194
+195        let nonce = aead::Nonce::assume_unique_for_key(nonce);
+196        let key = aead::LessSafeKey::new(aead::UnboundKey::new(&aead::AES_128_GCM, &key).unwrap());
+197
+198        let (aad, tag) = pseudo_packet.split_at_mut(tag_start);
+199        key.open_in_place(nonce, aead::Aad::from(aad), tag).is_ok()
+200    }
+201
+202    fn export_keying_material(
+203        &self,
+204        output: &mut [u8],
+205        label: &[u8],
+206        context: &[u8],
+207    ) -> Result<(), ExportKeyingMaterialError> {
+208        self.inner
+209            .export_keying_material(output, label, Some(context))
+210            .map_err(|_| ExportKeyingMaterialError)?;
+211        Ok(())
+212    }
+213}
+214
+215const RETRY_INTEGRITY_KEY_DRAFT: [u8; 16] = [
+216    0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a, 0x09, 0xd0, 0x57, 0x28, 0x15, 0x5a, 0x6c, 0xb9, 0x6b, 0xe1,
+217];
+218const RETRY_INTEGRITY_NONCE_DRAFT: [u8; 12] = [
+219    0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c,
+220];
+221
+222const RETRY_INTEGRITY_KEY_V1: [u8; 16] = [
+223    0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e,
+224];
+225const RETRY_INTEGRITY_NONCE_V1: [u8; 12] = [
+226    0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb,
+227];
+228
+229impl crypto::HeaderKey for Box<dyn HeaderProtectionKey> {
+230    fn decrypt(&self, pn_offset: usize, packet: &mut [u8]) {
+231        let (header, sample) = packet.split_at_mut(pn_offset + 4);
+232        let (first, rest) = header.split_at_mut(1);
+233        let pn_end = Ord::min(pn_offset + 3, rest.len());
+234        self.decrypt_in_place(
+235            &sample[..self.sample_size()],
+236            &mut first[0],
+237            &mut rest[pn_offset - 1..pn_end],
+238        )
+239        .unwrap();
+240    }
+241
+242    fn encrypt(&self, pn_offset: usize, packet: &mut [u8]) {
+243        let (header, sample) = packet.split_at_mut(pn_offset + 4);
+244        let (first, rest) = header.split_at_mut(1);
+245        let pn_end = Ord::min(pn_offset + 3, rest.len());
+246        self.encrypt_in_place(
+247            &sample[..self.sample_size()],
+248            &mut first[0],
+249            &mut rest[pn_offset - 1..pn_end],
+250        )
+251        .unwrap();
+252    }
+253
+254    fn sample_size(&self) -> usize {
+255        self.sample_len()
+256    }
+257}
+258
+259/// Authentication data for (rustls) TLS session
+260pub struct HandshakeData {
+261    /// The negotiated application protocol, if ALPN is in use
+262    ///
+263    /// Guaranteed to be set if a nonempty list of protocols was specified for this connection.
+264    pub protocol: Option<Vec<u8>>,
+265    /// The server name specified by the client, if any
+266    ///
+267    /// Always `None` for outgoing connections
+268    pub server_name: Option<String>,
+269    /// The key exchange group negotiated with the peer
+270    #[cfg(feature = "__rustls-post-quantum-test")]
+271    pub negotiated_key_exchange_group: NamedGroup,
+272}
+273
+274/// A QUIC-compatible TLS client configuration
+275///
+276/// Quinn implicitly constructs a `QuicClientConfig` with reasonable defaults within
+277/// [`ClientConfig::with_root_certificates()`][root_certs] and [`ClientConfig::try_with_platform_verifier()`][platform].
+278/// Alternatively, `QuicClientConfig`'s [`TryFrom`] implementation can be used to wrap around a
+279/// custom [`rustls::ClientConfig`], in which case care should be taken around certain points:
+280///
+281/// - If `enable_early_data` is not set to true, then sending 0-RTT data will not be possible on
+282///   outgoing connections.
+283/// - The [`rustls::ClientConfig`] must have TLS 1.3 support enabled for conversion to succeed.
+284///
+285/// The object in the `resumption` field of the inner [`rustls::ClientConfig`] determines whether
+286/// calling `into_0rtt` on outgoing connections returns `Ok` or `Err`. It typically allows
+287/// `into_0rtt` to proceed if it recognizes the server name, and defaults to an in-memory cache of
+288/// 256 server names.
+289///
+290/// [root_certs]: crate::config::ClientConfig::with_root_certificates()
+291/// [platform]: crate::config::ClientConfig::try_with_platform_verifier()
+292pub struct QuicClientConfig {
+293    pub(crate) inner: Arc<rustls::ClientConfig>,
+294    initial: Suite,
+295}
+296
+297impl QuicClientConfig {
+298    #[cfg(feature = "platform-verifier")]
+299    pub(crate) fn with_platform_verifier() -> Result<Self, Error> {
+300        // Keep in sync with `inner()` below
+301        let mut inner = rustls::ClientConfig::builder_with_provider(configured_provider())
+302            .with_protocol_versions(&[&rustls::version::TLS13])
+303            .unwrap() // The default providers support TLS 1.3
+304            .with_platform_verifier()?
+305            .with_no_client_auth();
+306
+307        inner.enable_early_data = true;
+308        Ok(Self {
+309            // We're confident that the *ring* default provider contains TLS13_AES_128_GCM_SHA256
+310            initial: initial_suite_from_provider(inner.crypto_provider())
+311                .expect("no initial cipher suite found"),
+312            inner: Arc::new(inner),
+313        })
+314    }
+315
+316    /// Initialize a sane QUIC-compatible TLS client configuration
+317    ///
+318    /// QUIC requires that TLS 1.3 be enabled. Advanced users can use any [`rustls::ClientConfig`] that
+319    /// satisfies this requirement.
+320    pub(crate) fn new(verifier: Arc<dyn ServerCertVerifier>) -> Self {
+321        let inner = Self::inner(verifier);
+322        Self {
+323            // We're confident that the *ring* default provider contains TLS13_AES_128_GCM_SHA256
+324            initial: initial_suite_from_provider(inner.crypto_provider())
+325                .expect("no initial cipher suite found"),
+326            inner: Arc::new(inner),
+327        }
+328    }
+329
+330    /// Initialize a QUIC-compatible TLS client configuration with a separate initial cipher suite
+331    ///
+332    /// This is useful if you want to avoid the initial cipher suite for traffic encryption.
+333    pub fn with_initial(
+334        inner: Arc<rustls::ClientConfig>,
+335        initial: Suite,
+336    ) -> Result<Self, NoInitialCipherSuite> {
+337        match initial.suite.common.suite {
+338            CipherSuite::TLS13_AES_128_GCM_SHA256 => Ok(Self { inner, initial }),
+339            _ => Err(NoInitialCipherSuite { specific: true }),
+340        }
+341    }
+342
+343    pub(crate) fn inner(verifier: Arc<dyn ServerCertVerifier>) -> rustls::ClientConfig {
+344        // Keep in sync with `with_platform_verifier()` above
+345        let mut config = rustls::ClientConfig::builder_with_provider(configured_provider())
+346            .with_protocol_versions(&[&rustls::version::TLS13])
+347            .unwrap() // The default providers support TLS 1.3
+348            .dangerous()
+349            .with_custom_certificate_verifier(verifier)
+350            .with_no_client_auth();
+351
+352        config.enable_early_data = true;
+353        config
+354    }
+355}
+356
+357impl crypto::ClientConfig for QuicClientConfig {
+358    fn start_session(
+359        self: Arc<Self>,
+360        version: u32,
+361        server_name: &str,
+362        params: &TransportParameters,
+363    ) -> Result<Box<dyn crypto::Session>, ConnectError> {
+364        let version = interpret_version(version)?;
+365        Ok(Box::new(TlsSession {
+366            version,
+367            got_handshake_data: false,
+368            next_secrets: None,
+369            inner: rustls::quic::Connection::Client(
+370                rustls::quic::ClientConnection::new(
+371                    self.inner.clone(),
+372                    version,
+373                    ServerName::try_from(server_name)
+374                        .map_err(|_| ConnectError::InvalidServerName(server_name.into()))?
+375                        .to_owned(),
+376                    to_vec(params),
+377                )
+378                .unwrap(),
+379            ),
+380            suite: self.initial,
+381        }))
+382    }
+383}
+384
+385impl TryFrom<rustls::ClientConfig> for QuicClientConfig {
+386    type Error = NoInitialCipherSuite;
+387
+388    fn try_from(inner: rustls::ClientConfig) -> Result<Self, Self::Error> {
+389        Arc::new(inner).try_into()
+390    }
+391}
+392
+393impl TryFrom<Arc<rustls::ClientConfig>> for QuicClientConfig {
+394    type Error = NoInitialCipherSuite;
+395
+396    fn try_from(inner: Arc<rustls::ClientConfig>) -> Result<Self, Self::Error> {
+397        Ok(Self {
+398            initial: initial_suite_from_provider(inner.crypto_provider())
+399                .ok_or(NoInitialCipherSuite { specific: false })?,
+400            inner,
+401        })
+402    }
+403}
+404
+405/// The initial cipher suite (AES-128-GCM-SHA256) is not available
+406///
+407/// When the cipher suite is supplied `with_initial()`, it must be
+408/// [`CipherSuite::TLS13_AES_128_GCM_SHA256`]. When the cipher suite is derived from a config's
+409/// [`CryptoProvider`][provider], that provider must reference a cipher suite with the same ID.
+410///
+411/// [provider]: rustls::crypto::CryptoProvider
+412#[derive(Clone, Debug)]
+413pub struct NoInitialCipherSuite {
+414    /// Whether the initial cipher suite was supplied by the caller
+415    specific: bool,
+416}
+417
+418impl std::fmt::Display for NoInitialCipherSuite {
+419    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+420        f.write_str(match self.specific {
+421            true => "invalid cipher suite specified",
+422            false => "no initial cipher suite found",
+423        })
+424    }
+425}
+426
+427impl std::error::Error for NoInitialCipherSuite {}
+428
+429/// A QUIC-compatible TLS server configuration
+430///
+431/// Quinn implicitly constructs a `QuicServerConfig` with reasonable defaults within
+432/// [`ServerConfig::with_single_cert()`][single]. Alternatively, `QuicServerConfig`'s [`TryFrom`]
+433/// implementation or `with_initial` method can be used to wrap around a custom
+434/// [`rustls::ServerConfig`], in which case care should be taken around certain points:
+435///
+436/// - If `max_early_data_size` is not set to `u32::MAX`, the server will not be able to accept
+437///   incoming 0-RTT data. QUIC prohibits `max_early_data_size` values other than 0 or `u32::MAX`.
+438/// - The `rustls::ServerConfig` must have TLS 1.3 support enabled for conversion to succeed.
+439///
+440/// [single]: crate::config::ServerConfig::with_single_cert()
+441pub struct QuicServerConfig {
+442    inner: Arc<rustls::ServerConfig>,
+443    initial: Suite,
+444}
+445
+446impl QuicServerConfig {
+447    pub(crate) fn new(
+448        cert_chain: Vec<CertificateDer<'static>>,
+449        key: PrivateKeyDer<'static>,
+450    ) -> Result<Self, rustls::Error> {
+451        let inner = Self::inner(cert_chain, key)?;
+452        Ok(Self {
+453            // We're confident that the *ring* default provider contains TLS13_AES_128_GCM_SHA256
+454            initial: initial_suite_from_provider(inner.crypto_provider())
+455                .expect("no initial cipher suite found"),
+456            inner: Arc::new(inner),
+457        })
+458    }
+459
+460    /// Initialize a QUIC-compatible TLS client configuration with a separate initial cipher suite
+461    ///
+462    /// This is useful if you want to avoid the initial cipher suite for traffic encryption.
+463    pub fn with_initial(
+464        inner: Arc<rustls::ServerConfig>,
+465        initial: Suite,
+466    ) -> Result<Self, NoInitialCipherSuite> {
+467        match initial.suite.common.suite {
+468            CipherSuite::TLS13_AES_128_GCM_SHA256 => Ok(Self { inner, initial }),
+469            _ => Err(NoInitialCipherSuite { specific: true }),
+470        }
+471    }
+472
+473    /// Initialize a sane QUIC-compatible TLS server configuration
+474    ///
+475    /// QUIC requires that TLS 1.3 be enabled, and that the maximum early data size is either 0 or
+476    /// `u32::MAX`. Advanced users can use any [`rustls::ServerConfig`] that satisfies these
+477    /// requirements.
+478    pub(crate) fn inner(
+479        cert_chain: Vec<CertificateDer<'static>>,
+480        key: PrivateKeyDer<'static>,
+481    ) -> Result<rustls::ServerConfig, rustls::Error> {
+482        let mut inner = rustls::ServerConfig::builder_with_provider(configured_provider())
+483            .with_protocol_versions(&[&rustls::version::TLS13])
+484            .unwrap() // The *ring* default provider supports TLS 1.3
+485            .with_no_client_auth()
+486            .with_single_cert(cert_chain, key)?;
+487
+488        inner.max_early_data_size = u32::MAX;
+489        Ok(inner)
+490    }
+491}
+492
+493impl TryFrom<rustls::ServerConfig> for QuicServerConfig {
+494    type Error = NoInitialCipherSuite;
+495
+496    fn try_from(inner: rustls::ServerConfig) -> Result<Self, Self::Error> {
+497        Arc::new(inner).try_into()
+498    }
+499}
+500
+501impl TryFrom<Arc<rustls::ServerConfig>> for QuicServerConfig {
+502    type Error = NoInitialCipherSuite;
+503
+504    fn try_from(inner: Arc<rustls::ServerConfig>) -> Result<Self, Self::Error> {
+505        Ok(Self {
+506            initial: initial_suite_from_provider(inner.crypto_provider())
+507                .ok_or(NoInitialCipherSuite { specific: false })?,
+508            inner,
+509        })
+510    }
+511}
+512
+513impl crypto::ServerConfig for QuicServerConfig {
+514    fn start_session(
+515        self: Arc<Self>,
+516        version: u32,
+517        params: &TransportParameters,
+518    ) -> Box<dyn crypto::Session> {
+519        // Safe: `start_session()` is never called if `initial_keys()` rejected `version`
+520        let version = interpret_version(version).unwrap();
+521        Box::new(TlsSession {
+522            version,
+523            got_handshake_data: false,
+524            next_secrets: None,
+525            inner: rustls::quic::Connection::Server(
+526                rustls::quic::ServerConnection::new(self.inner.clone(), version, to_vec(params))
+527                    .unwrap(),
+528            ),
+529            suite: self.initial,
+530        })
+531    }
+532
+533    fn initial_keys(
+534        &self,
+535        version: u32,
+536        dst_cid: ConnectionId,
+537    ) -> Result<Keys, UnsupportedVersion> {
+538        let version = interpret_version(version)?;
+539        Ok(initial_keys(version, dst_cid, Side::Server, &self.initial))
+540    }
+541
+542    fn retry_tag(&self, version: u32, orig_dst_cid: ConnectionId, packet: &[u8]) -> [u8; 16] {
+543        // Safe: `start_session()` is never called if `initial_keys()` rejected `version`
+544        let version = interpret_version(version).unwrap();
+545        let (nonce, key) = match version {
+546            Version::V1 => (RETRY_INTEGRITY_NONCE_V1, RETRY_INTEGRITY_KEY_V1),
+547            Version::V1Draft => (RETRY_INTEGRITY_NONCE_DRAFT, RETRY_INTEGRITY_KEY_DRAFT),
+548            _ => unreachable!(),
+549        };
+550
+551        let mut pseudo_packet = Vec::with_capacity(packet.len() + orig_dst_cid.len() + 1);
+552        pseudo_packet.push(orig_dst_cid.len() as u8);
+553        pseudo_packet.extend_from_slice(&orig_dst_cid);
+554        pseudo_packet.extend_from_slice(packet);
+555
+556        let nonce = aead::Nonce::assume_unique_for_key(nonce);
+557        let key = aead::LessSafeKey::new(aead::UnboundKey::new(&aead::AES_128_GCM, &key).unwrap());
+558
+559        let tag = key
+560            .seal_in_place_separate_tag(nonce, aead::Aad::from(pseudo_packet), &mut [])
+561            .unwrap();
+562        let mut result = [0; 16];
+563        result.copy_from_slice(tag.as_ref());
+564        result
+565    }
+566}
+567
+568pub(crate) fn initial_suite_from_provider(
+569    provider: &Arc<rustls::crypto::CryptoProvider>,
+570) -> Option<Suite> {
+571    provider
+572        .cipher_suites
+573        .iter()
+574        .find_map(|cs| match (cs.suite(), cs.tls13()) {
+575            (rustls::CipherSuite::TLS13_AES_128_GCM_SHA256, Some(suite)) => {
+576                Some(suite.quic_suite())
+577            }
+578            _ => None,
+579        })
+580        .flatten()
+581}
+582
+583pub(crate) fn configured_provider() -> Arc<rustls::crypto::CryptoProvider> {
+584    #[cfg(all(feature = "rustls-aws-lc-rs", not(feature = "rustls-ring")))]
+585    let provider = rustls::crypto::aws_lc_rs::default_provider();
+586    #[cfg(feature = "rustls-ring")]
+587    let provider = rustls::crypto::ring::default_provider();
+588    Arc::new(provider)
+589}
+590
+591fn to_vec(params: &TransportParameters) -> Vec<u8> {
+592    let mut bytes = Vec::new();
+593    params.write(&mut bytes);
+594    bytes
+595}
+596
+597pub(crate) fn initial_keys(
+598    version: Version,
+599    dst_cid: ConnectionId,
+600    side: Side,
+601    suite: &Suite,
+602) -> Keys {
+603    let keys = suite.keys(&dst_cid, side.into(), version);
+604    Keys {
+605        header: KeyPair {
+606            local: Box::new(keys.local.header),
+607            remote: Box::new(keys.remote.header),
+608        },
+609        packet: KeyPair {
+610            local: Box::new(keys.local.packet),
+611            remote: Box::new(keys.remote.packet),
+612        },
+613    }
+614}
+615
+616impl crypto::PacketKey for Box<dyn PacketKey> {
+617    fn encrypt(&self, path_id: PathId, packet: u64, buf: &mut [u8], header_len: usize) {
+618        let (header, payload_tag) = buf.split_at_mut(header_len);
+619        let (payload, tag_storage) = payload_tag.split_at_mut(payload_tag.len() - self.tag_len());
+620        let tag = self
+621            .encrypt_in_place_for_path(path_id.as_u32(), packet, &*header, payload)
+622            .unwrap();
+623        tag_storage.copy_from_slice(tag.as_ref());
+624    }
+625
+626    fn decrypt(
+627        &self,
+628        path_id: PathId,
+629        packet: u64,
+630        header: &[u8],
+631        payload: &mut BytesMut,
+632    ) -> Result<(), CryptoError> {
+633        let plain = self
+634            .decrypt_in_place_for_path(path_id.as_u32(), packet, header, payload.as_mut())
+635            .map_err(|_| CryptoError)?;
+636        let plain_len = plain.len();
+637        payload.truncate(plain_len);
+638        Ok(())
+639    }
+640
+641    fn tag_len(&self) -> usize {
+642        (**self).tag_len()
+643    }
+644
+645    fn confidentiality_limit(&self) -> u64 {
+646        (**self).confidentiality_limit()
+647    }
+648
+649    fn integrity_limit(&self) -> u64 {
+650        (**self).integrity_limit()
+651    }
+652}
+653
+654fn interpret_version(version: u32) -> Result<Version, UnsupportedVersion> {
+655    match version {
+656        0xff00_001d..=0xff00_0020 => Ok(Version::V1Draft),
+657        0x0000_0001 | 0xff00_0021..=0xff00_0022 => Ok(Version::V1),
+658        _ => Err(UnsupportedVersion),
+659    }
+660}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/endpoint.rs.html b/pr/395/docs/src/iroh_quinn_proto/endpoint.rs.html new file mode 100644 index 000000000..c1079d09f --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/endpoint.rs.html @@ -0,0 +1,1373 @@ +endpoint.rs - source

iroh_quinn_proto/
endpoint.rs

1use std::{
+2    collections::{HashMap, hash_map},
+3    convert::TryFrom,
+4    fmt, mem,
+5    net::{IpAddr, SocketAddr},
+6    ops::{Index, IndexMut},
+7    sync::Arc,
+8};
+9
+10use bytes::{BufMut, Bytes, BytesMut};
+11use rand::{Rng, RngCore, SeedableRng, rngs::StdRng};
+12use rustc_hash::FxHashMap;
+13use slab::Slab;
+14use thiserror::Error;
+15use tracing::{debug, error, trace, warn};
+16
+17use crate::{
+18    Duration, FourTuple, INITIAL_MTU, Instant, MAX_CID_SIZE, MIN_INITIAL_SIZE, PathId,
+19    RESET_TOKEN_SIZE, ResetToken, Side, Transmit, TransportConfig, TransportError,
+20    cid_generator::ConnectionIdGenerator,
+21    coding::{BufMutExt, Encodable},
+22    config::{ClientConfig, EndpointConfig, ServerConfig},
+23    connection::{Connection, ConnectionError, SideArgs},
+24    crypto::{self, Keys, UnsupportedVersion},
+25    frame,
+26    packet::{
+27        FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, PacketDecodeError,
+28        PacketNumber, PartialDecode, ProtectedInitialHeader,
+29    },
+30    shared::{
+31        ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint,
+32        EndpointEvent, EndpointEventInner, IssuedCid,
+33    },
+34    token::{IncomingToken, InvalidRetryTokenError, Token, TokenPayload},
+35    transport_parameters::{PreferredAddress, TransportParameters},
+36};
+37
+38/// The main entry point to the library
+39///
+40/// This object performs no I/O whatsoever. Instead, it consumes incoming packets and
+41/// connection-generated events via `handle` and `handle_event`.
+42pub struct Endpoint {
+43    rng: StdRng,
+44    index: ConnectionIndex,
+45    connections: Slab<ConnectionMeta>,
+46    local_cid_generator: Box<dyn ConnectionIdGenerator>,
+47    config: Arc<EndpointConfig>,
+48    server_config: Option<Arc<ServerConfig>>,
+49    /// Whether the underlying UDP socket promises not to fragment packets
+50    allow_mtud: bool,
+51    /// Time at which a stateless reset was most recently sent
+52    last_stateless_reset: Option<Instant>,
+53    /// Buffered Initial and 0-RTT messages for pending incoming connections
+54    incoming_buffers: Slab<IncomingBuffer>,
+55    all_incoming_buffers_total_bytes: u64,
+56}
+57
+58impl Endpoint {
+59    /// Create a new endpoint
+60    ///
+61    /// `allow_mtud` enables path MTU detection when requested by `Connection` configuration for
+62    /// better performance. This requires that outgoing packets are never fragmented, which can be
+63    /// achieved via e.g. the `IPV6_DONTFRAG` socket option.
+64    pub fn new(
+65        config: Arc<EndpointConfig>,
+66        server_config: Option<Arc<ServerConfig>>,
+67        allow_mtud: bool,
+68    ) -> Self {
+69        Self {
+70            rng: config
+71                .rng_seed
+72                .map_or_else(StdRng::from_os_rng, StdRng::from_seed),
+73            index: ConnectionIndex::default(),
+74            connections: Slab::new(),
+75            local_cid_generator: (config.connection_id_generator_factory.as_ref())(),
+76            config,
+77            server_config,
+78            allow_mtud,
+79            last_stateless_reset: None,
+80            incoming_buffers: Slab::new(),
+81            all_incoming_buffers_total_bytes: 0,
+82        }
+83    }
+84
+85    /// Replace the server configuration, affecting new incoming connections only
+86    pub fn set_server_config(&mut self, server_config: Option<Arc<ServerConfig>>) {
+87        self.server_config = server_config;
+88    }
+89
+90    /// Process `EndpointEvent`s emitted from related `Connection`s
+91    ///
+92    /// In turn, processing this event may return a `ConnectionEvent` for the same `Connection`.
+93    pub fn handle_event(
+94        &mut self,
+95        ch: ConnectionHandle,
+96        event: EndpointEvent,
+97    ) -> Option<ConnectionEvent> {
+98        use EndpointEventInner::*;
+99        match event.0 {
+100            NeedIdentifiers(path_id, now, n) => {
+101                return Some(self.send_new_identifiers(path_id, now, ch, n));
+102            }
+103            ResetToken(path_id, remote, token) => {
+104                if let Some(old) = self.connections[ch]
+105                    .reset_token
+106                    .insert(path_id, (remote, token))
+107                {
+108                    self.index.connection_reset_tokens.remove(old.0, old.1);
+109                }
+110                if self.index.connection_reset_tokens.insert(remote, token, ch) {
+111                    warn!("duplicate reset token");
+112                }
+113            }
+114            RetireResetToken(path_id) => {
+115                if let Some(old) = self.connections[ch].reset_token.remove(&path_id) {
+116                    self.index.connection_reset_tokens.remove(old.0, old.1);
+117                }
+118            }
+119            RetireConnectionId(now, path_id, seq, allow_more_cids) => {
+120                if let Some(cid) = self.connections[ch]
+121                    .local_cids
+122                    .get_mut(&path_id)
+123                    .and_then(|pcid| pcid.cids.remove(&seq))
+124                {
+125                    trace!(%path_id, "local CID retired {}: {}", seq, cid);
+126                    self.index.retire(cid);
+127                    if allow_more_cids {
+128                        return Some(self.send_new_identifiers(path_id, now, ch, 1));
+129                    }
+130                }
+131            }
+132            Drained => {
+133                if let Some(conn) = self.connections.try_remove(ch.0) {
+134                    self.index.remove(&conn);
+135                } else {
+136                    // This indicates a bug in downstream code, which could cause spurious
+137                    // connection loss instead of this error if the CID was (re)allocated prior to
+138                    // the illegal call.
+139                    error!(id = ch.0, "unknown connection drained");
+140                }
+141            }
+142        }
+143        None
+144    }
+145
+146    /// Process an incoming UDP datagram
+147    pub fn handle(
+148        &mut self,
+149        now: Instant,
+150        network_path: FourTuple,
+151        ecn: Option<EcnCodepoint>,
+152        data: BytesMut,
+153        buf: &mut Vec<u8>,
+154    ) -> Option<DatagramEvent> {
+155        // Partially decode packet or short-circuit if unable
+156        let datagram_len = data.len();
+157        let mut event = match PartialDecode::new(
+158            data,
+159            &FixedLengthConnectionIdParser::new(self.local_cid_generator.cid_len()),
+160            &self.config.supported_versions,
+161            self.config.grease_quic_bit,
+162        ) {
+163            Ok((first_decode, remaining)) => DatagramConnectionEvent {
+164                now,
+165                network_path,
+166                path_id: PathId::ZERO, // Corrected later for existing paths
+167                ecn,
+168                first_decode,
+169                remaining,
+170            },
+171            Err(PacketDecodeError::UnsupportedVersion {
+172                src_cid,
+173                dst_cid,
+174                version,
+175            }) => {
+176                if self.server_config.is_none() {
+177                    debug!("dropping packet with unsupported version");
+178                    return None;
+179                }
+180                trace!("sending version negotiation");
+181                // Negotiate versions
+182                Header::VersionNegotiate {
+183                    random: self.rng.random::<u8>() | 0x40,
+184                    src_cid: dst_cid,
+185                    dst_cid: src_cid,
+186                }
+187                .encode(buf);
+188                // Grease with a reserved version
+189                buf.write::<u32>(match version {
+190                    0x0a1a_2a3a => 0x0a1a_2a4a,
+191                    _ => 0x0a1a_2a3a,
+192                });
+193                for &version in &self.config.supported_versions {
+194                    buf.write(version);
+195                }
+196                return Some(DatagramEvent::Response(Transmit {
+197                    destination: network_path.remote,
+198                    ecn: None,
+199                    size: buf.len(),
+200                    segment_size: None,
+201                    src_ip: network_path.local_ip,
+202                }));
+203            }
+204            Err(e) => {
+205                trace!("malformed header: {}", e);
+206                return None;
+207            }
+208        };
+209
+210        let dst_cid = event.first_decode.dst_cid();
+211
+212        if let Some(route_to) = self.index.get(&network_path, &event.first_decode) {
+213            event.path_id = match route_to {
+214                RouteDatagramTo::Incoming(_) => PathId::ZERO,
+215                RouteDatagramTo::Connection(_, path_id) => path_id,
+216            };
+217            match route_to {
+218                RouteDatagramTo::Incoming(incoming_idx) => {
+219                    let incoming_buffer = &mut self.incoming_buffers[incoming_idx];
+220                    let config = &self.server_config.as_ref().unwrap();
+221
+222                    if incoming_buffer
+223                        .total_bytes
+224                        .checked_add(datagram_len as u64)
+225                        .is_some_and(|n| n <= config.incoming_buffer_size)
+226                        && self
+227                            .all_incoming_buffers_total_bytes
+228                            .checked_add(datagram_len as u64)
+229                            .is_some_and(|n| n <= config.incoming_buffer_size_total)
+230                    {
+231                        incoming_buffer.datagrams.push(event);
+232                        incoming_buffer.total_bytes += datagram_len as u64;
+233                        self.all_incoming_buffers_total_bytes += datagram_len as u64;
+234                    }
+235
+236                    None
+237                }
+238                RouteDatagramTo::Connection(ch, _path_id) => Some(DatagramEvent::ConnectionEvent(
+239                    ch,
+240                    ConnectionEvent(ConnectionEventInner::Datagram(event)),
+241                )),
+242            }
+243        } else if event.first_decode.initial_header().is_some() {
+244            // Potentially create a new connection
+245
+246            self.handle_first_packet(datagram_len, event, network_path, buf)
+247        } else if event.first_decode.has_long_header() {
+248            debug!(
+249                "ignoring non-initial packet for unknown connection {}",
+250                dst_cid
+251            );
+252            None
+253        } else if !event.first_decode.is_initial()
+254            && self.local_cid_generator.validate(dst_cid).is_err()
+255        {
+256            debug!("dropping packet with invalid CID");
+257            None
+258        } else if dst_cid.is_empty() {
+259            trace!("dropping unrecognized short packet without ID");
+260            None
+261        } else {
+262            // If we got this far, we're receiving a seemingly valid packet for an unknown
+263            // connection. Send a stateless reset if possible.
+264            self.stateless_reset(now, datagram_len, network_path, dst_cid, buf)
+265                .map(DatagramEvent::Response)
+266        }
+267    }
+268
+269    /// Builds a stateless reset packet to respond with
+270    fn stateless_reset(
+271        &mut self,
+272        now: Instant,
+273        inciting_dgram_len: usize,
+274        network_path: FourTuple,
+275        dst_cid: ConnectionId,
+276        buf: &mut Vec<u8>,
+277    ) -> Option<Transmit> {
+278        if self
+279            .last_stateless_reset
+280            .is_some_and(|last| last + self.config.min_reset_interval > now)
+281        {
+282            debug!("ignoring unexpected packet within minimum stateless reset interval");
+283            return None;
+284        }
+285
+286        /// Minimum amount of padding for the stateless reset to look like a short-header packet
+287        const MIN_PADDING_LEN: usize = 5;
+288
+289        // Prevent amplification attacks and reset loops by ensuring we pad to at most 1 byte
+290        // smaller than the inciting packet.
+291        let max_padding_len = match inciting_dgram_len.checked_sub(RESET_TOKEN_SIZE) {
+292            Some(headroom) if headroom > MIN_PADDING_LEN => headroom - 1,
+293            _ => {
+294                debug!(
+295                    "ignoring unexpected {} byte packet: not larger than minimum stateless reset size",
+296                    inciting_dgram_len
+297                );
+298                return None;
+299            }
+300        };
+301
+302        debug!(%dst_cid, %network_path.remote, "sending stateless reset");
+303        self.last_stateless_reset = Some(now);
+304        // Resets with at least this much padding can't possibly be distinguished from real packets
+305        const IDEAL_MIN_PADDING_LEN: usize = MIN_PADDING_LEN + MAX_CID_SIZE;
+306        let padding_len = if max_padding_len <= IDEAL_MIN_PADDING_LEN {
+307            max_padding_len
+308        } else {
+309            self.rng
+310                .random_range(IDEAL_MIN_PADDING_LEN..max_padding_len)
+311        };
+312        buf.reserve(padding_len + RESET_TOKEN_SIZE);
+313        buf.resize(padding_len, 0);
+314        self.rng.fill_bytes(&mut buf[0..padding_len]);
+315        buf[0] = 0b0100_0000 | (buf[0] >> 2);
+316        buf.extend_from_slice(&ResetToken::new(&*self.config.reset_key, dst_cid));
+317
+318        debug_assert!(buf.len() < inciting_dgram_len);
+319
+320        Some(Transmit {
+321            destination: network_path.remote,
+322            ecn: None,
+323            size: buf.len(),
+324            segment_size: None,
+325            src_ip: network_path.local_ip,
+326        })
+327    }
+328
+329    /// Initiate a connection
+330    pub fn connect(
+331        &mut self,
+332        now: Instant,
+333        config: ClientConfig,
+334        remote: SocketAddr,
+335        server_name: &str,
+336    ) -> Result<(ConnectionHandle, Connection), ConnectError> {
+337        if self.cids_exhausted() {
+338            return Err(ConnectError::CidsExhausted);
+339        }
+340        if remote.port() == 0 || remote.ip().is_unspecified() {
+341            return Err(ConnectError::InvalidRemoteAddress(remote));
+342        }
+343        if !self.config.supported_versions.contains(&config.version) {
+344            return Err(ConnectError::UnsupportedVersion);
+345        }
+346
+347        let remote_id = (config.initial_dst_cid_provider)();
+348        trace!(initial_dcid = %remote_id);
+349
+350        let ch = ConnectionHandle(self.connections.vacant_key());
+351        let local_cid = self.new_cid(ch, PathId::ZERO);
+352        let params = TransportParameters::new(
+353            &config.transport,
+354            &self.config,
+355            self.local_cid_generator.as_ref(),
+356            local_cid,
+357            None,
+358            &mut self.rng,
+359        );
+360        let tls = config
+361            .crypto
+362            .start_session(config.version, server_name, &params)?;
+363
+364        let conn = self.add_connection(
+365            ch,
+366            config.version,
+367            remote_id,
+368            local_cid,
+369            remote_id,
+370            FourTuple {
+371                remote,
+372                local_ip: None,
+373            },
+374            now,
+375            tls,
+376            config.transport,
+377            SideArgs::Client {
+378                token_store: config.token_store,
+379                server_name: server_name.into(),
+380            },
+381            &params,
+382        );
+383        Ok((ch, conn))
+384    }
+385
+386    /// Generates new CIDs and creates message to send to the connection state
+387    fn send_new_identifiers(
+388        &mut self,
+389        path_id: PathId,
+390        now: Instant,
+391        ch: ConnectionHandle,
+392        num: u64,
+393    ) -> ConnectionEvent {
+394        let mut ids = vec![];
+395        for _ in 0..num {
+396            let id = self.new_cid(ch, path_id);
+397            let cid_meta = self.connections[ch].local_cids.entry(path_id).or_default();
+398            let sequence = cid_meta.issued;
+399            cid_meta.issued += 1;
+400            cid_meta.cids.insert(sequence, id);
+401            ids.push(IssuedCid {
+402                path_id,
+403                sequence,
+404                id,
+405                reset_token: ResetToken::new(&*self.config.reset_key, id),
+406            });
+407        }
+408        ConnectionEvent(ConnectionEventInner::NewIdentifiers(
+409            ids,
+410            now,
+411            self.local_cid_generator.cid_len(),
+412            self.local_cid_generator.cid_lifetime(),
+413        ))
+414    }
+415
+416    /// Generate a connection ID for `ch`
+417    fn new_cid(&mut self, ch: ConnectionHandle, path_id: PathId) -> ConnectionId {
+418        loop {
+419            let cid = self.local_cid_generator.generate_cid();
+420            if cid.is_empty() {
+421                // Zero-length CID; nothing to track
+422                debug_assert_eq!(self.local_cid_generator.cid_len(), 0);
+423                return cid;
+424            }
+425            if let hash_map::Entry::Vacant(e) = self.index.connection_ids.entry(cid) {
+426                e.insert((ch, path_id));
+427                break cid;
+428            }
+429        }
+430    }
+431
+432    fn handle_first_packet(
+433        &mut self,
+434        datagram_len: usize,
+435        event: DatagramConnectionEvent,
+436        network_path: FourTuple,
+437        buf: &mut Vec<u8>,
+438    ) -> Option<DatagramEvent> {
+439        let dst_cid = event.first_decode.dst_cid();
+440        let header = event.first_decode.initial_header().unwrap();
+441
+442        let Some(server_config) = &self.server_config else {
+443            debug!("packet for unrecognized connection {}", dst_cid);
+444            return self
+445                .stateless_reset(event.now, datagram_len, network_path, dst_cid, buf)
+446                .map(DatagramEvent::Response);
+447        };
+448
+449        if datagram_len < MIN_INITIAL_SIZE as usize {
+450            debug!("ignoring short initial for connection {}", dst_cid);
+451            return None;
+452        }
+453
+454        let crypto = match server_config.crypto.initial_keys(header.version, dst_cid) {
+455            Ok(keys) => keys,
+456            Err(UnsupportedVersion) => {
+457                // This probably indicates that the user set supported_versions incorrectly in
+458                // `EndpointConfig`.
+459                debug!(
+460                    "ignoring initial packet version {:#x} unsupported by cryptographic layer",
+461                    header.version
+462                );
+463                return None;
+464            }
+465        };
+466
+467        if let Err(reason) = self.early_validate_first_packet(header) {
+468            return Some(DatagramEvent::Response(self.initial_close(
+469                header.version,
+470                network_path,
+471                &crypto,
+472                header.src_cid,
+473                reason,
+474                buf,
+475            )));
+476        }
+477
+478        let packet = match event.first_decode.finish(Some(&*crypto.header.remote)) {
+479            Ok(packet) => packet,
+480            Err(e) => {
+481                trace!("unable to decode initial packet: {}", e);
+482                return None;
+483            }
+484        };
+485
+486        if !packet.reserved_bits_valid() {
+487            debug!("dropping connection attempt with invalid reserved bits");
+488            return None;
+489        }
+490
+491        let Header::Initial(header) = packet.header else {
+492            panic!("non-initial packet in handle_first_packet()");
+493        };
+494
+495        let server_config = self.server_config.as_ref().unwrap().clone();
+496
+497        let token = match IncomingToken::from_header(&header, &server_config, network_path.remote) {
+498            Ok(token) => token,
+499            Err(InvalidRetryTokenError) => {
+500                debug!("rejecting invalid retry token");
+501                return Some(DatagramEvent::Response(self.initial_close(
+502                    header.version,
+503                    network_path,
+504                    &crypto,
+505                    header.src_cid,
+506                    TransportError::INVALID_TOKEN(""),
+507                    buf,
+508                )));
+509            }
+510        };
+511
+512        let incoming_idx = self.incoming_buffers.insert(IncomingBuffer::default());
+513        self.index
+514            .insert_initial_incoming(header.dst_cid, incoming_idx);
+515
+516        Some(DatagramEvent::NewConnection(Incoming {
+517            received_at: event.now,
+518            network_path,
+519            ecn: event.ecn,
+520            packet: InitialPacket {
+521                header,
+522                header_data: packet.header_data,
+523                payload: packet.payload,
+524            },
+525            rest: event.remaining,
+526            crypto,
+527            token,
+528            incoming_idx,
+529            improper_drop_warner: IncomingImproperDropWarner,
+530        }))
+531    }
+532
+533    /// Attempt to accept this incoming connection (an error may still occur)
+534    // box err to avoid clippy::result_large_err
+535    pub fn accept(
+536        &mut self,
+537        mut incoming: Incoming,
+538        now: Instant,
+539        buf: &mut Vec<u8>,
+540        server_config: Option<Arc<ServerConfig>>,
+541    ) -> Result<(ConnectionHandle, Connection), Box<AcceptError>> {
+542        let remote_address_validated = incoming.remote_address_validated();
+543        incoming.improper_drop_warner.dismiss();
+544        let incoming_buffer = self.incoming_buffers.remove(incoming.incoming_idx);
+545        self.all_incoming_buffers_total_bytes -= incoming_buffer.total_bytes;
+546
+547        let packet_number = incoming.packet.header.number.expand(0);
+548        let InitialHeader {
+549            src_cid,
+550            dst_cid,
+551            version,
+552            ..
+553        } = incoming.packet.header;
+554        let server_config =
+555            server_config.unwrap_or_else(|| self.server_config.as_ref().unwrap().clone());
+556
+557        if server_config
+558            .transport
+559            .max_idle_timeout
+560            .is_some_and(|timeout| {
+561                incoming.received_at + Duration::from_millis(timeout.into()) <= now
+562            })
+563        {
+564            debug!("abandoning accept of stale initial");
+565            self.index.remove_initial(dst_cid);
+566            return Err(Box::new(AcceptError {
+567                cause: ConnectionError::TimedOut,
+568                response: None,
+569            }));
+570        }
+571
+572        if self.cids_exhausted() {
+573            debug!("refusing connection");
+574            self.index.remove_initial(dst_cid);
+575            return Err(Box::new(AcceptError {
+576                cause: ConnectionError::CidsExhausted,
+577                response: Some(self.initial_close(
+578                    version,
+579                    incoming.network_path,
+580                    &incoming.crypto,
+581                    src_cid,
+582                    TransportError::CONNECTION_REFUSED(""),
+583                    buf,
+584                )),
+585            }));
+586        }
+587
+588        if incoming
+589            .crypto
+590            .packet
+591            .remote
+592            .decrypt(
+593                PathId::ZERO,
+594                packet_number,
+595                &incoming.packet.header_data,
+596                &mut incoming.packet.payload,
+597            )
+598            .is_err()
+599        {
+600            debug!(packet_number, "failed to authenticate initial packet");
+601            self.index.remove_initial(dst_cid);
+602            return Err(Box::new(AcceptError {
+603                cause: TransportError::PROTOCOL_VIOLATION("authentication failed").into(),
+604                response: None,
+605            }));
+606        };
+607
+608        let ch = ConnectionHandle(self.connections.vacant_key());
+609        let local_cid = self.new_cid(ch, PathId::ZERO);
+610        let mut params = TransportParameters::new(
+611            &server_config.transport,
+612            &self.config,
+613            self.local_cid_generator.as_ref(),
+614            local_cid,
+615            Some(&server_config),
+616            &mut self.rng,
+617        );
+618        params.stateless_reset_token = Some(ResetToken::new(&*self.config.reset_key, local_cid));
+619        params.original_dst_cid = Some(incoming.token.orig_dst_cid);
+620        params.retry_src_cid = incoming.token.retry_src_cid;
+621        let mut pref_addr_cid = None;
+622        if server_config.has_preferred_address() {
+623            let cid = self.new_cid(ch, PathId::ZERO);
+624            pref_addr_cid = Some(cid);
+625            params.preferred_address = Some(PreferredAddress {
+626                address_v4: server_config.preferred_address_v4,
+627                address_v6: server_config.preferred_address_v6,
+628                connection_id: cid,
+629                stateless_reset_token: ResetToken::new(&*self.config.reset_key, cid),
+630            });
+631        }
+632
+633        let tls = server_config.crypto.clone().start_session(version, &params);
+634        let transport_config = server_config.transport.clone();
+635        let mut conn = self.add_connection(
+636            ch,
+637            version,
+638            dst_cid,
+639            local_cid,
+640            src_cid,
+641            incoming.network_path,
+642            incoming.received_at,
+643            tls,
+644            transport_config,
+645            SideArgs::Server {
+646                server_config,
+647                pref_addr_cid,
+648                path_validated: remote_address_validated,
+649            },
+650            &params,
+651        );
+652        self.index.insert_initial(dst_cid, ch);
+653
+654        match conn.handle_first_packet(
+655            incoming.received_at,
+656            incoming.network_path,
+657            incoming.ecn,
+658            packet_number,
+659            incoming.packet,
+660            incoming.rest,
+661        ) {
+662            Ok(()) => {
+663                trace!(id = ch.0, icid = %dst_cid, "new connection");
+664
+665                for event in incoming_buffer.datagrams {
+666                    conn.handle_event(ConnectionEvent(ConnectionEventInner::Datagram(event)))
+667                }
+668
+669                Ok((ch, conn))
+670            }
+671            Err(e) => {
+672                debug!("handshake failed: {}", e);
+673                self.handle_event(ch, EndpointEvent(EndpointEventInner::Drained));
+674                let response = match e {
+675                    ConnectionError::TransportError(ref e) => Some(self.initial_close(
+676                        version,
+677                        incoming.network_path,
+678                        &incoming.crypto,
+679                        src_cid,
+680                        e.clone(),
+681                        buf,
+682                    )),
+683                    _ => None,
+684                };
+685                Err(Box::new(AcceptError { cause: e, response }))
+686            }
+687        }
+688    }
+689
+690    /// Check if we should refuse a connection attempt regardless of the packet's contents
+691    fn early_validate_first_packet(
+692        &mut self,
+693        header: &ProtectedInitialHeader,
+694    ) -> Result<(), TransportError> {
+695        let config = &self.server_config.as_ref().unwrap();
+696        if self.cids_exhausted() || self.incoming_buffers.len() >= config.max_incoming {
+697            return Err(TransportError::CONNECTION_REFUSED(""));
+698        }
+699
+700        // RFC9000 §7.2 dictates that initial (client-chosen) destination CIDs must be at least 8
+701        // bytes. If this is a Retry packet, then the length must instead match our usual CID
+702        // length. If we ever issue non-Retry address validation tokens via `NEW_TOKEN`, then we'll
+703        // also need to validate CID length for those after decoding the token.
+704        if header.dst_cid.len() < 8
+705            && (header.token_pos.is_empty()
+706                || header.dst_cid.len() != self.local_cid_generator.cid_len())
+707        {
+708            debug!(
+709                "rejecting connection due to invalid DCID length {}",
+710                header.dst_cid.len()
+711            );
+712            return Err(TransportError::PROTOCOL_VIOLATION(
+713                "invalid destination CID length",
+714            ));
+715        }
+716
+717        Ok(())
+718    }
+719
+720    /// Reject this incoming connection attempt
+721    pub fn refuse(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Transmit {
+722        self.clean_up_incoming(&incoming);
+723        incoming.improper_drop_warner.dismiss();
+724
+725        self.initial_close(
+726            incoming.packet.header.version,
+727            incoming.network_path,
+728            &incoming.crypto,
+729            incoming.packet.header.src_cid,
+730            TransportError::CONNECTION_REFUSED(""),
+731            buf,
+732        )
+733    }
+734
+735    /// Respond with a retry packet, requiring the client to retry with address validation
+736    ///
+737    /// Errors if `incoming.may_retry()` is false.
+738    pub fn retry(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Result<Transmit, RetryError> {
+739        if !incoming.may_retry() {
+740            return Err(RetryError(Box::new(incoming)));
+741        }
+742
+743        self.clean_up_incoming(&incoming);
+744        incoming.improper_drop_warner.dismiss();
+745
+746        let server_config = self.server_config.as_ref().unwrap();
+747
+748        // First Initial
+749        // The peer will use this as the DCID of its following Initials. Initial DCIDs are
+750        // looked up separately from Handshake/Data DCIDs, so there is no risk of collision
+751        // with established connections. In the unlikely event that a collision occurs
+752        // between two connections in the initial phase, both will fail fast and may be
+753        // retried by the application layer.
+754        let local_cid = self.local_cid_generator.generate_cid();
+755
+756        let payload = TokenPayload::Retry {
+757            address: incoming.network_path.remote,
+758            orig_dst_cid: incoming.packet.header.dst_cid,
+759            issued: server_config.time_source.now(),
+760        };
+761        let token = Token::new(payload, &mut self.rng).encode(&*server_config.token_key);
+762
+763        let header = Header::Retry {
+764            src_cid: local_cid,
+765            dst_cid: incoming.packet.header.src_cid,
+766            version: incoming.packet.header.version,
+767        };
+768
+769        let encode = header.encode(buf);
+770        buf.put_slice(&token);
+771        buf.extend_from_slice(&server_config.crypto.retry_tag(
+772            incoming.packet.header.version,
+773            incoming.packet.header.dst_cid,
+774            buf,
+775        ));
+776        encode.finish(buf, &*incoming.crypto.header.local, None);
+777
+778        Ok(Transmit {
+779            destination: incoming.network_path.remote,
+780            ecn: None,
+781            size: buf.len(),
+782            segment_size: None,
+783            src_ip: incoming.network_path.local_ip,
+784        })
+785    }
+786
+787    /// Ignore this incoming connection attempt, not sending any packet in response
+788    ///
+789    /// Doing this actively, rather than merely dropping the [`Incoming`], is necessary to prevent
+790    /// memory leaks due to state within [`Endpoint`] tracking the incoming connection.
+791    pub fn ignore(&mut self, incoming: Incoming) {
+792        self.clean_up_incoming(&incoming);
+793        incoming.improper_drop_warner.dismiss();
+794    }
+795
+796    /// Clean up endpoint data structures associated with an `Incoming`.
+797    fn clean_up_incoming(&mut self, incoming: &Incoming) {
+798        self.index.remove_initial(incoming.packet.header.dst_cid);
+799        let incoming_buffer = self.incoming_buffers.remove(incoming.incoming_idx);
+800        self.all_incoming_buffers_total_bytes -= incoming_buffer.total_bytes;
+801    }
+802
+803    fn add_connection(
+804        &mut self,
+805        ch: ConnectionHandle,
+806        version: u32,
+807        init_cid: ConnectionId,
+808        local_cid: ConnectionId,
+809        remote_cid: ConnectionId,
+810        network_path: FourTuple,
+811        now: Instant,
+812        tls: Box<dyn crypto::Session>,
+813        transport_config: Arc<TransportConfig>,
+814        side_args: SideArgs,
+815        // Only used for qlog.
+816        params: &TransportParameters,
+817    ) -> Connection {
+818        let mut rng_seed = [0; 32];
+819        self.rng.fill_bytes(&mut rng_seed);
+820        let side = side_args.side();
+821        let pref_addr_cid = side_args.pref_addr_cid();
+822
+823        let qlog =
+824            transport_config.create_qlog_sink(side_args.side(), network_path.remote, init_cid, now);
+825
+826        qlog.emit_connection_started(
+827            now,
+828            local_cid,
+829            remote_cid,
+830            network_path.remote,
+831            network_path.local_ip,
+832            params,
+833        );
+834
+835        let conn = Connection::new(
+836            self.config.clone(),
+837            transport_config,
+838            init_cid,
+839            local_cid,
+840            remote_cid,
+841            network_path,
+842            tls,
+843            self.local_cid_generator.as_ref(),
+844            now,
+845            version,
+846            self.allow_mtud,
+847            rng_seed,
+848            side_args,
+849            qlog,
+850        );
+851
+852        let mut path_cids = PathLocalCids::default();
+853        path_cids.cids.insert(path_cids.issued, local_cid);
+854        path_cids.issued += 1;
+855
+856        if let Some(cid) = pref_addr_cid {
+857            debug_assert_eq!(path_cids.issued, 1, "preferred address cid seq must be 1");
+858            path_cids.cids.insert(path_cids.issued, cid);
+859            path_cids.issued += 1;
+860        }
+861
+862        let id = self.connections.insert(ConnectionMeta {
+863            init_cid,
+864            local_cids: FxHashMap::from_iter([(PathId::ZERO, path_cids)]),
+865            network_path,
+866            side,
+867            reset_token: Default::default(),
+868        });
+869        debug_assert_eq!(id, ch.0, "connection handle allocation out of sync");
+870
+871        self.index.insert_conn(network_path, local_cid, ch, side);
+872
+873        conn
+874    }
+875
+876    fn initial_close(
+877        &mut self,
+878        version: u32,
+879        network_path: FourTuple,
+880        crypto: &Keys,
+881        remote_id: ConnectionId,
+882        reason: TransportError,
+883        buf: &mut Vec<u8>,
+884    ) -> Transmit {
+885        // We don't need to worry about CID collisions in initial closes because the peer
+886        // shouldn't respond, and if it does, and the CID collides, we'll just drop the
+887        // unexpected response.
+888        let local_id = self.local_cid_generator.generate_cid();
+889        let number = PacketNumber::U8(0);
+890        let header = Header::Initial(InitialHeader {
+891            dst_cid: remote_id,
+892            src_cid: local_id,
+893            number,
+894            token: Bytes::new(),
+895            version,
+896        });
+897
+898        let partial_encode = header.encode(buf);
+899        let max_len =
+900            INITIAL_MTU as usize - partial_encode.header_len - crypto.packet.local.tag_len();
+901        frame::Close::from(reason).encoder(max_len).encode(buf);
+902        buf.resize(buf.len() + crypto.packet.local.tag_len(), 0);
+903        partial_encode.finish(
+904            buf,
+905            &*crypto.header.local,
+906            Some((0, Default::default(), &*crypto.packet.local)),
+907        );
+908        Transmit {
+909            destination: network_path.remote,
+910            ecn: None,
+911            size: buf.len(),
+912            segment_size: None,
+913            src_ip: network_path.local_ip,
+914        }
+915    }
+916
+917    /// Access the configuration used by this endpoint
+918    pub fn config(&self) -> &EndpointConfig {
+919        &self.config
+920    }
+921
+922    /// Number of connections that are currently open
+923    pub fn open_connections(&self) -> usize {
+924        self.connections.len()
+925    }
+926
+927    /// Counter for the number of bytes currently used
+928    /// in the buffers for Initial and 0-RTT messages for pending incoming connections
+929    pub fn incoming_buffer_bytes(&self) -> u64 {
+930        self.all_incoming_buffers_total_bytes
+931    }
+932
+933    #[cfg(test)]
+934    pub(crate) fn known_connections(&self) -> usize {
+935        let x = self.connections.len();
+936        debug_assert_eq!(x, self.index.connection_ids_initial.len());
+937        // Not all connections have known reset tokens
+938        debug_assert!(x >= self.index.connection_reset_tokens.0.len());
+939        // Not all connections have unique remotes, and 0-length CIDs might not be in use.
+940        debug_assert!(x >= self.index.incoming_connection_remotes.len());
+941        debug_assert!(x >= self.index.outgoing_connection_remotes.len());
+942        x
+943    }
+944
+945    #[cfg(test)]
+946    pub(crate) fn known_cids(&self) -> usize {
+947        self.index.connection_ids.len()
+948    }
+949
+950    /// Whether we've used up 3/4 of the available CID space
+951    ///
+952    /// We leave some space unused so that `new_cid` can be relied upon to finish quickly. We don't
+953    /// bother to check when CID longer than 4 bytes are used because 2^40 connections is a lot.
+954    fn cids_exhausted(&self) -> bool {
+955        self.local_cid_generator.cid_len() <= 4
+956            && self.local_cid_generator.cid_len() != 0
+957            && (2usize.pow(self.local_cid_generator.cid_len() as u32 * 8)
+958                - self.index.connection_ids.len())
+959                < 2usize.pow(self.local_cid_generator.cid_len() as u32 * 8 - 2)
+960    }
+961}
+962
+963impl fmt::Debug for Endpoint {
+964    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+965        fmt.debug_struct("Endpoint")
+966            .field("rng", &self.rng)
+967            .field("index", &self.index)
+968            .field("connections", &self.connections)
+969            .field("config", &self.config)
+970            .field("server_config", &self.server_config)
+971            // incoming_buffers too large
+972            .field("incoming_buffers.len", &self.incoming_buffers.len())
+973            .field(
+974                "all_incoming_buffers_total_bytes",
+975                &self.all_incoming_buffers_total_bytes,
+976            )
+977            .finish()
+978    }
+979}
+980
+981/// Buffered Initial and 0-RTT messages for a pending incoming connection
+982#[derive(Default)]
+983struct IncomingBuffer {
+984    datagrams: Vec<DatagramConnectionEvent>,
+985    total_bytes: u64,
+986}
+987
+988/// Part of protocol state incoming datagrams can be routed to
+989#[derive(Copy, Clone, Debug)]
+990enum RouteDatagramTo {
+991    Incoming(usize),
+992    Connection(ConnectionHandle, PathId),
+993}
+994
+995/// Maps packets to existing connections
+996#[derive(Default, Debug)]
+997struct ConnectionIndex {
+998    /// Identifies connections based on the initial DCID the peer utilized
+999    ///
+1000    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1001    ///
+1002    /// Used by the server, not the client.
+1003    connection_ids_initial: HashMap<ConnectionId, RouteDatagramTo>,
+1004    /// Identifies connections based on locally created CIDs
+1005    ///
+1006    /// Uses a cheaper hash function since keys are locally created
+1007    connection_ids: FxHashMap<ConnectionId, (ConnectionHandle, PathId)>,
+1008    /// Identifies incoming connections with zero-length CIDs
+1009    ///
+1010    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1011    incoming_connection_remotes: HashMap<FourTuple, ConnectionHandle>,
+1012    /// Identifies outgoing connections with zero-length CIDs
+1013    ///
+1014    /// We don't yet support explicit source addresses for client connections, and zero-length CIDs
+1015    /// require a unique 4-tuple, so at most one client connection with zero-length local CIDs
+1016    /// may be established per remote. We must omit the local address from the key because we don't
+1017    /// necessarily know what address we're sending from, and hence receiving at.
+1018    ///
+1019    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1020    // TODO(matheus23): It's possible this could be changed now that we track the full 4-tuple on the client side, too.
+1021    outgoing_connection_remotes: HashMap<SocketAddr, ConnectionHandle>,
+1022    /// Reset tokens provided by the peer for the CID each connection is currently sending to
+1023    ///
+1024    /// Incoming stateless resets do not have correct CIDs, so we need this to identify the correct
+1025    /// recipient, if any.
+1026    connection_reset_tokens: ResetTokenTable,
+1027}
+1028
+1029impl ConnectionIndex {
+1030    /// Associate an incoming connection with its initial destination CID
+1031    fn insert_initial_incoming(&mut self, dst_cid: ConnectionId, incoming_key: usize) {
+1032        if dst_cid.is_empty() {
+1033            return;
+1034        }
+1035        self.connection_ids_initial
+1036            .insert(dst_cid, RouteDatagramTo::Incoming(incoming_key));
+1037    }
+1038
+1039    /// Remove an association with an initial destination CID
+1040    fn remove_initial(&mut self, dst_cid: ConnectionId) {
+1041        if dst_cid.is_empty() {
+1042            return;
+1043        }
+1044        let removed = self.connection_ids_initial.remove(&dst_cid);
+1045        debug_assert!(removed.is_some());
+1046    }
+1047
+1048    /// Associate a connection with its initial destination CID
+1049    fn insert_initial(&mut self, dst_cid: ConnectionId, connection: ConnectionHandle) {
+1050        if dst_cid.is_empty() {
+1051            return;
+1052        }
+1053        self.connection_ids_initial.insert(
+1054            dst_cid,
+1055            RouteDatagramTo::Connection(connection, PathId::ZERO),
+1056        );
+1057    }
+1058
+1059    /// Associate a connection with its first locally-chosen destination CID if used, or otherwise
+1060    /// its current 4-tuple
+1061    fn insert_conn(
+1062        &mut self,
+1063        network_path: FourTuple,
+1064        dst_cid: ConnectionId,
+1065        connection: ConnectionHandle,
+1066        side: Side,
+1067    ) {
+1068        match dst_cid.len() {
+1069            0 => match side {
+1070                Side::Server => {
+1071                    self.incoming_connection_remotes
+1072                        .insert(network_path, connection);
+1073                }
+1074                Side::Client => {
+1075                    self.outgoing_connection_remotes
+1076                        .insert(network_path.remote, connection);
+1077                }
+1078            },
+1079            _ => {
+1080                self.connection_ids
+1081                    .insert(dst_cid, (connection, PathId::ZERO));
+1082            }
+1083        }
+1084    }
+1085
+1086    /// Discard a connection ID
+1087    fn retire(&mut self, dst_cid: ConnectionId) {
+1088        self.connection_ids.remove(&dst_cid);
+1089    }
+1090
+1091    /// Remove all references to a connection
+1092    fn remove(&mut self, conn: &ConnectionMeta) {
+1093        if conn.side.is_server() {
+1094            self.remove_initial(conn.init_cid);
+1095        }
+1096        for cid in conn
+1097            .local_cids
+1098            .values()
+1099            .flat_map(|pcids| pcids.cids.values())
+1100        {
+1101            self.connection_ids.remove(cid);
+1102        }
+1103        self.incoming_connection_remotes.remove(&conn.network_path);
+1104        self.outgoing_connection_remotes
+1105            .remove(&conn.network_path.remote);
+1106        for (remote, token) in conn.reset_token.values() {
+1107            self.connection_reset_tokens.remove(*remote, *token);
+1108        }
+1109    }
+1110
+1111    /// Find the existing connection that `datagram` should be routed to, if any
+1112    fn get(&self, network_path: &FourTuple, datagram: &PartialDecode) -> Option<RouteDatagramTo> {
+1113        if !datagram.dst_cid().is_empty()
+1114            && let Some(&(ch, path_id)) = self.connection_ids.get(&datagram.dst_cid())
+1115        {
+1116            return Some(RouteDatagramTo::Connection(ch, path_id));
+1117        }
+1118        if (datagram.is_initial() || datagram.is_0rtt())
+1119            && let Some(&ch) = self.connection_ids_initial.get(&datagram.dst_cid())
+1120        {
+1121            return Some(ch);
+1122        }
+1123        if datagram.dst_cid().is_empty() {
+1124            if let Some(&ch) = self.incoming_connection_remotes.get(network_path) {
+1125                // Never multipath because QUIC-MULTIPATH 1.1 mandates the use of non-zero
+1126                // length CIDs.  So this is always PathId::ZERO.
+1127                return Some(RouteDatagramTo::Connection(ch, PathId::ZERO));
+1128            }
+1129            if let Some(&ch) = self.outgoing_connection_remotes.get(&network_path.remote) {
+1130                // Like above, QUIC-MULTIPATH 1.1 mandates the use of non-zero length CIDs.
+1131                return Some(RouteDatagramTo::Connection(ch, PathId::ZERO));
+1132            }
+1133        }
+1134        let data = datagram.data();
+1135        if data.len() < RESET_TOKEN_SIZE {
+1136            return None;
+1137        }
+1138        // For stateless resets the PathId is meaningless since it closes the entire
+1139        // connection regardless of path.  So use PathId::ZERO.
+1140        self.connection_reset_tokens
+1141            .get(network_path.remote, &data[data.len() - RESET_TOKEN_SIZE..])
+1142            .cloned()
+1143            .map(|ch| RouteDatagramTo::Connection(ch, PathId::ZERO))
+1144    }
+1145}
+1146
+1147#[derive(Debug)]
+1148pub(crate) struct ConnectionMeta {
+1149    init_cid: ConnectionId,
+1150    /// Locally issues CIDs for each path
+1151    local_cids: FxHashMap<PathId, PathLocalCids>,
+1152    /// Remote/local addresses the connection began with
+1153    ///
+1154    /// Only needed to support connections with zero-length CIDs, which cannot migrate, so we don't
+1155    /// bother keeping it up to date.
+1156    network_path: FourTuple,
+1157    side: Side,
+1158    /// Reset tokens provided by the peer for CIDs we're currently sending to
+1159    ///
+1160    /// Since each reset token is for a CID, it is also for a fixed remote address which is
+1161    /// also stored. This allows us to look up which reset tokens we might expect from a
+1162    /// given remote address, see [`ResetTokenTable`].
+1163    ///
+1164    /// Each path has its own active CID. We use the [`PathId`] as a unique index, allowing
+1165    /// us to retire the reset token when a path is abandoned.
+1166    // TODO(matheus23): Should be migrated to make reset tokens per 4-tuple instead of per remote addr
+1167    reset_token: FxHashMap<PathId, (SocketAddr, ResetToken)>,
+1168}
+1169
+1170/// Local connection IDs for a single path
+1171#[derive(Debug, Default)]
+1172struct PathLocalCids {
+1173    /// Number of connection IDs that have been issued in (PATH_)NEW_CONNECTION_ID frames
+1174    ///
+1175    /// Another way of saying this is that this is the next sequence number to be issued.
+1176    issued: u64,
+1177    /// Issues CIDs indexed by their sequence number.
+1178    cids: FxHashMap<u64, ConnectionId>,
+1179}
+1180
+1181/// Internal identifier for a `Connection` currently associated with an endpoint
+1182#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
+1183pub struct ConnectionHandle(pub usize);
+1184
+1185impl From<ConnectionHandle> for usize {
+1186    fn from(x: ConnectionHandle) -> Self {
+1187        x.0
+1188    }
+1189}
+1190
+1191impl Index<ConnectionHandle> for Slab<ConnectionMeta> {
+1192    type Output = ConnectionMeta;
+1193    fn index(&self, ch: ConnectionHandle) -> &ConnectionMeta {
+1194        &self[ch.0]
+1195    }
+1196}
+1197
+1198impl IndexMut<ConnectionHandle> for Slab<ConnectionMeta> {
+1199    fn index_mut(&mut self, ch: ConnectionHandle) -> &mut ConnectionMeta {
+1200        &mut self[ch.0]
+1201    }
+1202}
+1203
+1204/// Event resulting from processing a single datagram
+1205pub enum DatagramEvent {
+1206    /// The datagram is redirected to its `Connection`
+1207    ConnectionEvent(ConnectionHandle, ConnectionEvent),
+1208    /// The datagram may result in starting a new `Connection`
+1209    NewConnection(Incoming),
+1210    /// Response generated directly by the endpoint
+1211    Response(Transmit),
+1212}
+1213
+1214/// An incoming connection for which the server has not yet begun its part of the handshake.
+1215#[derive(derive_more::Debug)]
+1216pub struct Incoming {
+1217    #[debug(skip)]
+1218    received_at: Instant,
+1219    network_path: FourTuple,
+1220    ecn: Option<EcnCodepoint>,
+1221    #[debug(skip)]
+1222    packet: InitialPacket,
+1223    #[debug(skip)]
+1224    rest: Option<BytesMut>,
+1225    #[debug(skip)]
+1226    crypto: Keys,
+1227    token: IncomingToken,
+1228    incoming_idx: usize,
+1229    #[debug(skip)]
+1230    improper_drop_warner: IncomingImproperDropWarner,
+1231}
+1232
+1233impl Incoming {
+1234    /// The local IP address which was used when the peer established the connection
+1235    pub fn local_ip(&self) -> Option<IpAddr> {
+1236        self.network_path.local_ip
+1237    }
+1238
+1239    /// The peer's UDP address
+1240    pub fn remote_address(&self) -> SocketAddr {
+1241        self.network_path.remote
+1242    }
+1243
+1244    /// Whether the socket address that is initiating this connection has been validated
+1245    ///
+1246    /// This means that the sender of the initial packet has proved that they can receive traffic
+1247    /// sent to `self.remote_address()`.
+1248    ///
+1249    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+1250    /// The inverse is not guaranteed.
+1251    pub fn remote_address_validated(&self) -> bool {
+1252        self.token.validated
+1253    }
+1254
+1255    /// Whether it is legal to respond with a retry packet
+1256    ///
+1257    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+1258    /// The inverse is not guaranteed.
+1259    pub fn may_retry(&self) -> bool {
+1260        self.token.retry_src_cid.is_none()
+1261    }
+1262
+1263    /// The original destination connection ID sent by the client
+1264    pub fn orig_dst_cid(&self) -> ConnectionId {
+1265        self.token.orig_dst_cid
+1266    }
+1267}
+1268
+1269struct IncomingImproperDropWarner;
+1270
+1271impl IncomingImproperDropWarner {
+1272    fn dismiss(self) {
+1273        mem::forget(self);
+1274    }
+1275}
+1276
+1277impl Drop for IncomingImproperDropWarner {
+1278    fn drop(&mut self) {
+1279        warn!(
+1280            "quinn_proto::Incoming dropped without passing to Endpoint::accept/refuse/retry/ignore \
+1281               (may cause memory leak and eventual inability to accept new connections)"
+1282        );
+1283    }
+1284}
+1285
+1286/// Errors in the parameters being used to create a new connection
+1287///
+1288/// These arise before any I/O has been performed.
+1289#[derive(Debug, Error, Clone, PartialEq, Eq)]
+1290pub enum ConnectError {
+1291    /// The endpoint can no longer create new connections
+1292    ///
+1293    /// Indicates that a necessary component of the endpoint has been dropped or otherwise disabled.
+1294    #[error("endpoint stopping")]
+1295    EndpointStopping,
+1296    /// The connection could not be created because not enough of the CID space is available
+1297    ///
+1298    /// Try using longer connection IDs
+1299    #[error("CIDs exhausted")]
+1300    CidsExhausted,
+1301    /// The given server name was malformed
+1302    #[error("invalid server name: {0}")]
+1303    InvalidServerName(String),
+1304    /// The remote [`SocketAddr`] supplied was malformed
+1305    ///
+1306    /// Examples include attempting to connect to port 0, or using an inappropriate address family.
+1307    #[error("invalid remote address: {0}")]
+1308    InvalidRemoteAddress(SocketAddr),
+1309    /// No default client configuration was set up
+1310    ///
+1311    /// Use `Endpoint::connect_with` to specify a client configuration.
+1312    #[error("no default client config")]
+1313    NoDefaultClientConfig,
+1314    /// The local endpoint does not support the QUIC version specified in the client configuration
+1315    #[error("unsupported QUIC version")]
+1316    UnsupportedVersion,
+1317}
+1318
+1319/// Error type for attempting to accept an [`Incoming`]
+1320#[derive(Debug)]
+1321pub struct AcceptError {
+1322    /// Underlying error describing reason for failure
+1323    pub cause: ConnectionError,
+1324    /// Optional response to transmit back
+1325    pub response: Option<Transmit>,
+1326}
+1327
+1328/// Error for attempting to retry an [`Incoming`] which already bears a token from a previous retry
+1329#[derive(Debug, Error)]
+1330#[error("retry() with validated Incoming")]
+1331pub struct RetryError(Box<Incoming>);
+1332
+1333impl RetryError {
+1334    /// Get the [`Incoming`]
+1335    pub fn into_incoming(self) -> Incoming {
+1336        *self.0
+1337    }
+1338}
+1339
+1340/// Reset Tokens which are associated with peer socket addresses
+1341///
+1342/// The standard `HashMap` is used since both `SocketAddr` and `ResetToken` are
+1343/// peer generated and might be usable for hash collision attacks.
+1344#[derive(Default, Debug)]
+1345struct ResetTokenTable(HashMap<SocketAddr, HashMap<ResetToken, ConnectionHandle>>);
+1346
+1347impl ResetTokenTable {
+1348    fn insert(&mut self, remote: SocketAddr, token: ResetToken, ch: ConnectionHandle) -> bool {
+1349        self.0
+1350            .entry(remote)
+1351            .or_default()
+1352            .insert(token, ch)
+1353            .is_some()
+1354    }
+1355
+1356    fn remove(&mut self, remote: SocketAddr, token: ResetToken) {
+1357        use std::collections::hash_map::Entry;
+1358        match self.0.entry(remote) {
+1359            Entry::Vacant(_) => {}
+1360            Entry::Occupied(mut e) => {
+1361                e.get_mut().remove(&token);
+1362                if e.get().is_empty() {
+1363                    e.remove_entry();
+1364                }
+1365            }
+1366        }
+1367    }
+1368
+1369    fn get(&self, remote: SocketAddr, token: &[u8]) -> Option<&ConnectionHandle> {
+1370        let token = ResetToken::from(<[u8; RESET_TOKEN_SIZE]>::try_from(token).ok()?);
+1371        self.0.get(&remote)?.get(&token)
+1372    }
+1373}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/frame.rs.html b/pr/395/docs/src/iroh_quinn_proto/frame.rs.html new file mode 100644 index 000000000..1232b96ed --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/frame.rs.html @@ -0,0 +1,2837 @@ +frame.rs - source

iroh_quinn_proto/
frame.rs

1use std::{
+2    fmt::{self, Display},
+3    mem,
+4    net::{IpAddr, SocketAddr},
+5    ops::{Range, RangeInclusive},
+6};
+7
+8use bytes::{Buf, BufMut, Bytes};
+9use tinyvec::TinyVec;
+10
+11use crate::{
+12    Dir, MAX_CID_SIZE, RESET_TOKEN_SIZE, ResetToken, StreamId, TransportError, TransportErrorCode,
+13    VarInt,
+14    coding::{self, BufExt, BufMutExt, Decodable, Encodable, UnexpectedEnd},
+15    connection::PathId,
+16    range_set::ArrayRangeSet,
+17    shared::{ConnectionId, EcnCodepoint},
+18};
+19
+20#[cfg(feature = "qlog")]
+21use super::connection::qlog::ToQlog;
+22
+23#[cfg(test)]
+24use crate::varint::varint_u64;
+25#[cfg(test)]
+26use proptest::{collection, prelude::any, strategy::Strategy};
+27#[cfg(test)]
+28use test_strategy::Arbitrary;
+29
+30#[derive(
+31    Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display, enum_assoc::Assoc,
+32)]
+33#[cfg_attr(test, derive(Arbitrary))]
+34#[display(rename_all = "SCREAMING_SNAKE_CASE")]
+35#[allow(missing_docs)]
+36#[func(
+37    pub(crate) const fn to_u64(self) -> u64,
+38    const fn from_u64(rev: u64) -> Option<Self>,
+39)]
+40pub enum FrameType {
+41    #[assoc(to_u64 = 0x00)]
+42    Padding,
+43    #[assoc(to_u64 = 0x01)]
+44    Ping,
+45    #[assoc(to_u64 = 0x02)]
+46    Ack,
+47    #[assoc(to_u64 = 0x03)]
+48    AckEcn,
+49    #[assoc(to_u64 = 0x04)]
+50    ResetStream,
+51    #[assoc(to_u64 = 0x05)]
+52    StopSending,
+53    #[assoc(to_u64 = 0x06)]
+54    Crypto,
+55    #[assoc(to_u64 = 0x07)]
+56    NewToken,
+57    // STREAM
+58    #[assoc(to_u64 = _0.to_u64())]
+59    Stream(StreamInfo),
+60    #[assoc(to_u64 = 0x10)]
+61    MaxData,
+62    #[assoc(to_u64 = 0x11)]
+63    MaxStreamData,
+64    #[assoc(to_u64 = 0x12)]
+65    MaxStreamsBidi,
+66    #[assoc(to_u64 = 0x13)]
+67    MaxStreamsUni,
+68    #[assoc(to_u64 = 0x14)]
+69    DataBlocked,
+70    #[assoc(to_u64 = 0x15)]
+71    StreamDataBlocked,
+72    #[assoc(to_u64 = 0x16)]
+73    StreamsBlockedBidi,
+74    #[assoc(to_u64 = 0x17)]
+75    StreamsBlockedUni,
+76    #[assoc(to_u64 = 0x18)]
+77    NewConnectionId,
+78    #[assoc(to_u64 = 0x19)]
+79    RetireConnectionId,
+80    #[assoc(to_u64 = 0x1a)]
+81    PathChallenge,
+82    #[assoc(to_u64 = 0x1b)]
+83    PathResponse,
+84    #[assoc(to_u64 = 0x1c)]
+85    ConnectionClose,
+86    #[assoc(to_u64 = 0x1d)]
+87    ApplicationClose,
+88    #[assoc(to_u64 = 0x1e)]
+89    HandshakeDone,
+90    // ACK Frequency
+91    #[assoc(to_u64 = 0xaf)]
+92    AckFrequency,
+93    #[assoc(to_u64 = 0x1f)]
+94    ImmediateAck,
+95    // DATAGRAM
+96    #[assoc(to_u64 = _0.to_u64())]
+97    Datagram(DatagramInfo),
+98    // ADDRESS DISCOVERY REPORT
+99    #[assoc(to_u64 = 0x9f81a6)]
+100    ObservedIpv4Addr,
+101    #[assoc(to_u64 = 0x9f81a7)]
+102    ObservedIpv6Addr,
+103    // Multipath
+104    #[assoc(to_u64 = 0x3e)]
+105    PathAck,
+106    #[assoc(to_u64 = 0x3f)]
+107    PathAckEcn,
+108    #[assoc(to_u64 = 0x3e75)]
+109    PathAbandon,
+110    #[assoc(to_u64 = 0x3e76)]
+111    PathStatusBackup,
+112    #[assoc(to_u64 = 0x3e77)]
+113    PathStatusAvailable,
+114    #[assoc(to_u64 = 0x3e78)]
+115    PathNewConnectionId,
+116    #[assoc(to_u64 = 0x3e79)]
+117    PathRetireConnectionId,
+118    #[assoc(to_u64 = 0x3e7a)]
+119    MaxPathId,
+120    #[assoc(to_u64 = 0x3e7b)]
+121    PathsBlocked,
+122    #[assoc(to_u64 = 0x3e7c)]
+123    PathCidsBlocked,
+124    // IROH'S NAT TRAVERSAL
+125    #[assoc(to_u64 = 0x3d7f90)]
+126    AddIpv4Address,
+127    #[assoc(to_u64 = 0x3d7f91)]
+128    AddIpv6Address,
+129    #[assoc(to_u64 = 0x3d7f92)]
+130    ReachOutAtIpv4,
+131    #[assoc(to_u64 = 0x3d7f93)]
+132    ReachOutAtIpv6,
+133    #[assoc(to_u64 = 0x3d7f94)]
+134    RemoveAddress,
+135}
+136
+137/// Encounter a frame ID that was not valid.
+138#[derive(Debug, PartialEq, Eq, thiserror::Error)]
+139#[error("Invalid frame identifier {_0:02x}")]
+140pub struct InvalidFrameId(u64);
+141
+142impl TryFrom<u64> for FrameType {
+143    type Error = InvalidFrameId;
+144    fn try_from(value: u64) -> Result<Self, Self::Error> {
+145        match Self::from_u64(value) {
+146            Some(t) => Ok(t),
+147            None => {
+148                if DatagramInfo::VALUES.contains(&value) {
+149                    return Ok(Self::Datagram(DatagramInfo(value as u8)));
+150                }
+151                if StreamInfo::VALUES.contains(&value) {
+152                    return Ok(Self::Stream(StreamInfo(value as u8)));
+153                }
+154                Err(InvalidFrameId(value))
+155            }
+156        }
+157    }
+158}
+159
+160impl FrameType {
+161    /// The encoded size of this [`FrameType`].
+162    const fn size(&self) -> usize {
+163        VarInt(self.to_u64()).size()
+164    }
+165}
+166
+167impl Decodable for FrameType {
+168    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+169        Self::try_from(buf.get_var()?).map_err(|_| coding::UnexpectedEnd)
+170    }
+171}
+172
+173impl Encodable for FrameType {
+174    fn encode<B: BufMut>(&self, buf: &mut B) {
+175        buf.write_var(self.to_u64());
+176    }
+177}
+178
+179/// Wrapper type for the encodable frames.
+180///
+181/// This includes some "encoder" types instead of the actual read frame, when writing directly to
+182/// a buffer is more efficient than building the Frame itself.
+183#[derive(derive_more::From, enum_assoc::Assoc, derive_more::Display)]
+184#[func(fn encode_inner<B: BufMut>(&self, buf: &mut B) {_0.encode(buf)})]
+185#[func(pub(crate) const fn get_type(&self) -> FrameType {_0.get_type()})]
+186#[cfg_attr(feature = "qlog", func(pub(crate) fn to_qlog(&self) -> qlog::events::quic::QuicFrame {_0.to_qlog()}))]
+187pub(super) enum EncodableFrame<'a> {
+188    PathAck(PathAckEncoder<'a>),
+189    Ack(AckEncoder<'a>),
+190    Close(CloseEncoder<'a>),
+191    PathResponse(PathResponse),
+192    HandshakeDone(HandshakeDone),
+193    ReachOut(ReachOut),
+194    ObservedAddr(ObservedAddr),
+195    Ping(Ping),
+196    ImmediateAck(ImmediateAck),
+197    AckFrequency(AckFrequency),
+198    PathChallenge(PathChallenge),
+199    Crypto(Crypto),
+200    PathAbandon(PathAbandon),
+201    PathStatusAvailable(PathStatusAvailable),
+202    PathStatusBackup(PathStatusBackup),
+203    MaxPathId(MaxPathId),
+204    PathsBlocked(PathsBlocked),
+205    PathCidsBlocked(PathCidsBlocked),
+206    ResetStream(ResetStream),
+207    StopSending(StopSending),
+208    NewConnectionId(NewConnectionId),
+209    RetireConnectionId(RetireConnectionId),
+210    Datagram(Datagram),
+211    NewToken(NewToken),
+212    AddAddress(AddAddress),
+213    RemoveAddress(RemoveAddress),
+214    StreamMeta(StreamMetaEncoder),
+215    MaxData(MaxData),
+216    MaxStreamData(MaxStreamData),
+217    MaxStreams(MaxStreams),
+218}
+219
+220impl<'a> Encodable for EncodableFrame<'a> {
+221    fn encode<B: BufMut>(&self, buf: &mut B) {
+222        self.encode_inner(buf)
+223    }
+224}
+225
+226pub(crate) trait FrameStruct {
+227    /// Smallest number of bytes this type of frame is guaranteed to fit within.
+228    const SIZE_BOUND: usize;
+229}
+230
+231/// The type used to refer to [`FrameType`]s in closing and transport errors.
+232#[derive(Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display)]
+233pub enum MaybeFrame {
+234    /// Not attributed to any particular [`FrameType`].
+235    None,
+236    /// Attributed to some frame type this implementation does not recognize.
+237    #[display("UNKNOWN({:02x})", _0)]
+238    #[debug("Unknown({:02x})", _0)]
+239    Unknown(u64),
+240    /// Attributed to a specific [`FrameType`], never [`FrameType::Padding`].
+241    Known(FrameType),
+242}
+243
+244impl MaybeFrame {
+245    /// Encoded size of this [`MaybeFrame`].
+246    const fn size(&self) -> usize {
+247        match self {
+248            Self::None => VarInt(0).size(),
+249            Self::Unknown(other) => VarInt(*other).size(),
+250            Self::Known(frame_type) => frame_type.size(),
+251        }
+252    }
+253}
+254
+255impl Decodable for MaybeFrame {
+256    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+257        match FrameType::try_from(buf.get_var()?) {
+258            Ok(FrameType::Padding) => Ok(Self::None),
+259            Ok(other_frame) => Ok(Self::Known(other_frame)),
+260            Err(InvalidFrameId(other)) => Ok(Self::Unknown(other)),
+261        }
+262    }
+263}
+264
+265impl Encodable for MaybeFrame {
+266    fn encode<B: BufMut>(&self, buf: &mut B) {
+267        match self {
+268            Self::None => buf.write_var(0u64),
+269            Self::Unknown(frame_id) => buf.write_var(*frame_id),
+270            Self::Known(frame_type) => buf.write(*frame_type),
+271        }
+272    }
+273}
+274
+275#[cfg(test)]
+276impl proptest::arbitrary::Arbitrary for MaybeFrame {
+277    type Parameters = ();
+278    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+279
+280    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
+281        use proptest::prelude::*;
+282        prop_oneof![
+283            Just(Self::None),
+284            any::<VarInt>().prop_map(|v| Self::Unknown(v.0)),
+285            // do not generate padding frames here, since they are not allowed in MaybeFrame::Known
+286            any::<FrameType>()
+287                .prop_filter("not Padding", |ft| *ft != FrameType::Padding)
+288                .prop_map(MaybeFrame::Known),
+289        ]
+290        .boxed()
+291    }
+292}
+293
+294#[derive(derive_more::Display)]
+295#[cfg_attr(test, derive(Arbitrary, Debug, Clone, PartialEq, Eq))]
+296#[display("HANDSHAKE_DONE")]
+297pub(crate) struct HandshakeDone;
+298
+299impl HandshakeDone {
+300    const fn get_type(&self) -> FrameType {
+301        FrameType::HandshakeDone
+302    }
+303}
+304
+305impl Encodable for HandshakeDone {
+306    fn encode<B: BufMut>(&self, buf: &mut B) {
+307        FrameType::HandshakeDone.encode(buf);
+308    }
+309}
+310
+311#[derive(derive_more::Display)]
+312#[cfg_attr(test, derive(Arbitrary, Debug, Clone, PartialEq, Eq))]
+313#[display("PING")]
+314pub(crate) struct Ping;
+315
+316impl Ping {
+317    const fn get_type(&self) -> FrameType {
+318        FrameType::Ping
+319    }
+320}
+321
+322impl Encodable for Ping {
+323    fn encode<B: BufMut>(&self, buf: &mut B) {
+324        FrameType::Ping.encode(buf);
+325    }
+326}
+327
+328#[derive(derive_more::Display)]
+329#[cfg_attr(test, derive(Arbitrary, Debug, Clone, PartialEq, Eq))]
+330#[display("IMMEDIATE_ACK")]
+331pub(crate) struct ImmediateAck;
+332
+333impl ImmediateAck {
+334    const fn get_type(&self) -> FrameType {
+335        FrameType::ImmediateAck
+336    }
+337}
+338
+339impl Encodable for ImmediateAck {
+340    fn encode<B: BufMut>(&self, buf: &mut B) {
+341        FrameType::ImmediateAck.encode(buf);
+342    }
+343}
+344
+345#[allow(missing_docs)]
+346#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)]
+347#[display("STREAM")]
+348#[cfg_attr(test, derive(Arbitrary))]
+349pub struct StreamInfo(#[cfg_attr(test, strategy(0x08u8..=0x0f))] u8);
+350
+351impl StreamInfo {
+352    const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x08, 0x0f);
+353    fn fin(self) -> bool {
+354        self.0 & 0x01 != 0
+355    }
+356    fn len(self) -> bool {
+357        self.0 & 0x02 != 0
+358    }
+359    fn off(self) -> bool {
+360        self.0 & 0x04 != 0
+361    }
+362
+363    const fn to_u64(self) -> u64 {
+364        self.0 as u64
+365    }
+366}
+367
+368#[allow(missing_docs)]
+369#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)]
+370#[display("DATAGRAM")]
+371#[cfg_attr(test, derive(Arbitrary))]
+372pub struct DatagramInfo(#[cfg_attr(test, strategy(0x30u8..=0x31))] u8);
+373
+374impl DatagramInfo {
+375    const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x30, 0x31);
+376
+377    fn len(self) -> bool {
+378        self.0 & 0x01 != 0
+379    }
+380
+381    const fn to_u64(self) -> u64 {
+382        self.0 as u64
+383    }
+384}
+385
+386#[derive(Debug)]
+387#[cfg_attr(test, derive(Arbitrary))]
+388pub(crate) enum Frame {
+389    Padding,
+390    Ping,
+391    Ack(Ack),
+392    PathAck(PathAck),
+393    ResetStream(ResetStream),
+394    StopSending(StopSending),
+395    Crypto(Crypto),
+396    NewToken(NewToken),
+397    Stream(Stream),
+398    MaxData(MaxData),
+399    MaxStreamData(MaxStreamData),
+400    MaxStreams(MaxStreams),
+401    DataBlocked(DataBlocked),
+402    StreamDataBlocked(StreamDataBlocked),
+403    StreamsBlocked(StreamsBlocked),
+404    NewConnectionId(NewConnectionId),
+405    RetireConnectionId(RetireConnectionId),
+406    PathChallenge(PathChallenge),
+407    PathResponse(PathResponse),
+408    Close(Close),
+409    Datagram(Datagram),
+410    AckFrequency(AckFrequency),
+411    ImmediateAck,
+412    HandshakeDone,
+413    ObservedAddr(ObservedAddr),
+414    PathAbandon(PathAbandon),
+415    PathStatusAvailable(PathStatusAvailable),
+416    PathStatusBackup(PathStatusBackup),
+417    MaxPathId(MaxPathId),
+418    PathsBlocked(PathsBlocked),
+419    PathCidsBlocked(PathCidsBlocked),
+420    AddAddress(AddAddress),
+421    ReachOut(ReachOut),
+422    RemoveAddress(RemoveAddress),
+423}
+424
+425impl fmt::Display for Frame {
+426    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+427        // Eventually all our frames will support fmt::Display and be able to be used to log
+428        // consistently. For now we fall back to fmt::Debug.
+429        match self {
+430            Self::Padding => write!(f, "PADDING"),
+431            Self::Ping => write!(f, "PING"),
+432            Self::PathChallenge(frame) => write!(f, "{frame}"),
+433            Self::PathResponse(frame) => write!(f, "{frame}"),
+434            Self::ImmediateAck => write!(f, "IMMEDIATE_ACK"),
+435            Self::HandshakeDone => write!(f, "HANDSHAKE_DONE"),
+436            _ => write!(f, "{self:?}"),
+437        }
+438    }
+439}
+440
+441impl Frame {
+442    pub(crate) fn ty(&self) -> FrameType {
+443        use Frame::*;
+444        match &self {
+445            Padding => FrameType::Padding,
+446            ResetStream(_) => FrameType::ResetStream,
+447            Close(self::Close::Connection(_)) => FrameType::ConnectionClose,
+448            Close(self::Close::Application(_)) => FrameType::ConnectionClose,
+449            MaxData(_) => FrameType::MaxData,
+450            MaxStreamData(_) => FrameType::MaxStreamData,
+451            MaxStreams(max_streams) => max_streams.get_type(),
+452            Ping => FrameType::Ping,
+453            DataBlocked(_) => FrameType::DataBlocked,
+454            StreamDataBlocked(sdb) => sdb.get_type(),
+455            StreamsBlocked(sb) => sb.get_type(),
+456            StopSending { .. } => FrameType::StopSending,
+457            RetireConnectionId(retire_frame) => retire_frame.get_type(),
+458            Ack(ack) => ack.get_type(),
+459            PathAck(path_ack) => path_ack.get_type(),
+460            Stream(x) => {
+461                let mut ty = *StreamInfo::VALUES.start() as u8;
+462                if x.fin {
+463                    ty |= 0x01;
+464                }
+465                if x.offset != 0 {
+466                    ty |= 0x04;
+467                }
+468                // TODO(@divma): move all this to getframetype for Stream
+469                FrameType::Stream(StreamInfo(ty))
+470            }
+471            PathChallenge(_) => FrameType::PathChallenge,
+472            PathResponse(_) => FrameType::PathResponse,
+473            NewConnectionId(cid) => cid.get_type(),
+474            Crypto(_) => FrameType::Crypto,
+475            NewToken(_) => FrameType::NewToken,
+476            Datagram(_) => FrameType::Datagram(DatagramInfo(*DatagramInfo::VALUES.start() as u8)),
+477            AckFrequency(_) => FrameType::AckFrequency,
+478            ImmediateAck => FrameType::ImmediateAck,
+479            HandshakeDone => FrameType::HandshakeDone,
+480            ObservedAddr(observed) => observed.get_type(),
+481            PathAbandon(_) => FrameType::PathAbandon,
+482            PathStatusAvailable(_) => FrameType::PathStatusAvailable,
+483            PathStatusBackup(_) => FrameType::PathStatusBackup,
+484            MaxPathId(_) => FrameType::MaxPathId,
+485            PathsBlocked(_) => FrameType::PathsBlocked,
+486            PathCidsBlocked(_) => FrameType::PathCidsBlocked,
+487            AddAddress(frame) => frame.get_type(),
+488            ReachOut(frame) => frame.get_type(),
+489            RemoveAddress(_) => self::RemoveAddress::TYPE,
+490        }
+491    }
+492
+493    pub(crate) fn is_ack_eliciting(&self) -> bool {
+494        !matches!(
+495            *self,
+496            Self::Ack(_) | Self::PathAck(_) | Self::Padding | Self::Close(_)
+497        )
+498    }
+499
+500    /// Returns `true` if this frame MUST be sent in 1-RTT space
+501    pub(crate) fn is_1rtt(&self) -> bool {
+502        // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-4-1:
+503        // > All frames defined in this document MUST only be sent in 1-RTT packets.
+504        // > If an endpoint receives a multipath-specific frame in a different packet type, it MUST close the
+505        // > connection with an error of type PROTOCOL_VIOLATION.
+506
+507        self.is_multipath_frame() || self.is_qad_frame()
+508    }
+509
+510    fn is_qad_frame(&self) -> bool {
+511        matches!(*self, Self::ObservedAddr(_))
+512    }
+513
+514    fn is_multipath_frame(&self) -> bool {
+515        matches!(
+516            *self,
+517            Self::PathAck(_)
+518                | Self::PathAbandon(_)
+519                | Self::PathStatusBackup(_)
+520                | Self::PathStatusAvailable(_)
+521                | Self::MaxPathId(_)
+522                | Self::PathsBlocked(_)
+523                | Self::PathCidsBlocked(_)
+524                | Self::NewConnectionId(NewConnectionId {
+525                    path_id: Some(_),
+526                    ..
+527                })
+528                | Self::RetireConnectionId(RetireConnectionId {
+529                    path_id: Some(_),
+530                    ..
+531                })
+532        )
+533    }
+534}
+535
+536#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display)]
+537#[cfg_attr(test, derive(Arbitrary))]
+538#[display("PATH_CHALLENGE({_0:08x})")]
+539pub(crate) struct PathChallenge(pub(crate) u64);
+540
+541impl PathChallenge {
+542    pub(crate) const SIZE_BOUND: usize = 9;
+543
+544    const fn get_type(&self) -> FrameType {
+545        FrameType::PathChallenge
+546    }
+547}
+548
+549impl Decodable for PathChallenge {
+550    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+551        Ok(Self(buf.get()?))
+552    }
+553}
+554
+555impl Encodable for PathChallenge {
+556    fn encode<B: BufMut>(&self, buf: &mut B) {
+557        buf.write(FrameType::PathChallenge);
+558        buf.write(self.0);
+559    }
+560}
+561
+562#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Display)]
+563#[cfg_attr(test, derive(Arbitrary))]
+564#[display("PATH_RESPONSE({_0:08x})")]
+565pub(crate) struct PathResponse(pub(crate) u64);
+566
+567impl PathResponse {
+568    pub(crate) const SIZE_BOUND: usize = 9;
+569
+570    const fn get_type(&self) -> FrameType {
+571        FrameType::PathResponse
+572    }
+573}
+574
+575impl Decodable for PathResponse {
+576    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+577        Ok(Self(buf.get()?))
+578    }
+579}
+580
+581impl Encodable for PathResponse {
+582    fn encode<B: BufMut>(&self, buf: &mut B) {
+583        buf.write(FrameType::PathResponse);
+584        buf.write(self.0);
+585    }
+586}
+587
+588#[derive(Debug, Clone, Copy, derive_more::Display)]
+589#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+590#[display("DATA_BLOCKED offset: {_0}")]
+591pub(crate) struct DataBlocked(#[cfg_attr(test, strategy(varint_u64()))] pub(crate) u64);
+592
+593impl Encodable for DataBlocked {
+594    fn encode<B: BufMut>(&self, buf: &mut B) {
+595        buf.write(FrameType::DataBlocked);
+596        buf.write_var(self.0);
+597    }
+598}
+599
+600#[derive(Debug, Clone, Copy, derive_more::Display)]
+601#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+602#[display("STREAM_DATA_BLOCKED id: {id} offset: {offset}")]
+603pub(crate) struct StreamDataBlocked {
+604    pub(crate) id: StreamId,
+605    #[cfg_attr(test, strategy(varint_u64()))]
+606    pub(crate) offset: u64,
+607}
+608
+609impl StreamDataBlocked {
+610    const fn get_type(&self) -> FrameType {
+611        FrameType::StreamDataBlocked
+612    }
+613}
+614
+615impl Encodable for StreamDataBlocked {
+616    fn encode<B: BufMut>(&self, buf: &mut B) {
+617        buf.write(FrameType::StreamDataBlocked);
+618        buf.write(self.id);
+619        buf.write_var(self.offset);
+620    }
+621}
+622
+623#[derive(Debug, Clone, Copy, derive_more::Display)]
+624#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+625#[display("STREAMS_BLOCKED dir: {:?} limit: {limit}", dir)]
+626pub(crate) struct StreamsBlocked {
+627    pub(crate) dir: Dir,
+628    #[cfg_attr(test, strategy(varint_u64()))]
+629    pub(crate) limit: u64,
+630}
+631
+632impl StreamsBlocked {
+633    const fn get_type(&self) -> FrameType {
+634        match self.dir {
+635            Dir::Bi => FrameType::StreamsBlockedBidi,
+636            Dir::Uni => FrameType::StreamsBlockedUni,
+637        }
+638    }
+639}
+640
+641impl Encodable for StreamsBlocked {
+642    fn encode<B: BufMut>(&self, buf: &mut B) {
+643        buf.write(self.get_type());
+644        buf.write_var(self.limit);
+645    }
+646}
+647
+648#[derive(Debug, Clone, Copy, derive_more::Display)]
+649#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+650#[display("MAX_DATA({_0})")]
+651pub(crate) struct MaxData(pub(crate) VarInt);
+652
+653impl MaxData {
+654    const fn get_type(&self) -> FrameType {
+655        FrameType::MaxData
+656    }
+657}
+658
+659impl Decodable for MaxData {
+660    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+661        Ok(Self(buf.get()?))
+662    }
+663}
+664
+665impl Encodable for MaxData {
+666    fn encode<B: BufMut>(&self, buf: &mut B) {
+667        buf.write(FrameType::MaxData);
+668        buf.write(self.0);
+669    }
+670}
+671
+672#[derive(Debug, Clone, Copy, derive_more::Display)]
+673#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+674#[display("MAX_STREAM_DATA id: {id} max: {offset}")]
+675pub(crate) struct MaxStreamData {
+676    pub(crate) id: StreamId,
+677    #[cfg_attr(test, strategy(varint_u64()))]
+678    pub(crate) offset: u64,
+679}
+680
+681impl MaxStreamData {
+682    const fn get_type(&self) -> FrameType {
+683        FrameType::MaxStreamData
+684    }
+685}
+686
+687impl Decodable for MaxStreamData {
+688    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+689        Ok(Self {
+690            id: buf.get()?,
+691            offset: buf.get_var()?,
+692        })
+693    }
+694}
+695
+696impl Encodable for MaxStreamData {
+697    fn encode<B: BufMut>(&self, buf: &mut B) {
+698        buf.write(FrameType::MaxStreamData);
+699        buf.write(self.id);
+700        buf.write_var(self.offset);
+701    }
+702}
+703
+704#[derive(Debug, Clone, Copy, derive_more::Display)]
+705#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+706#[display("{} count: {count}", self.get_type())]
+707pub(crate) struct MaxStreams {
+708    pub(crate) dir: Dir,
+709    #[cfg_attr(test, strategy(varint_u64()))]
+710    pub(crate) count: u64,
+711}
+712
+713impl MaxStreams {
+714    const fn get_type(&self) -> FrameType {
+715        match self.dir {
+716            Dir::Bi => FrameType::MaxStreamsBidi,
+717            Dir::Uni => FrameType::MaxStreamsUni,
+718        }
+719    }
+720}
+721
+722impl Encodable for MaxStreams {
+723    fn encode<B: BufMut>(&self, buf: &mut B) {
+724        buf.write(self.get_type());
+725        buf.write_var(self.count);
+726    }
+727}
+728
+729#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+730#[cfg_attr(test, derive(Arbitrary))]
+731#[display("{} {} seq: {sequence}", self.get_type(), DisplayOption::new("path_id", path_id.as_ref()))]
+732pub(crate) struct RetireConnectionId {
+733    pub(crate) path_id: Option<PathId>,
+734    #[cfg_attr(test, strategy(varint_u64()))]
+735    pub(crate) sequence: u64,
+736}
+737
+738impl RetireConnectionId {
+739    /// Maximum size of this frame when the frame type is [`FrameType::RetireConnectionId`]
+740    pub(crate) const SIZE_BOUND: usize = {
+741        let type_len = FrameType::RetireConnectionId.size();
+742        let seq_max_len = 8usize;
+743        type_len + seq_max_len
+744    };
+745
+746    /// Maximum size of this frame when the frame type is [`FrameType::PathRetireConnectionId`]
+747    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
+748        let type_len = FrameType::PathRetireConnectionId.size();
+749        let path_id_len = VarInt::from_u32(u32::MAX).size();
+750        let seq_max_len = 8usize;
+751        type_len + path_id_len + seq_max_len
+752    };
+753
+754    /// Decode [`Self`] from the buffer, provided that the frame type has been verified (either
+755    /// [`FrameType::PathRetireConnectionId`], or [`FrameType::RetireConnectionId`])
+756    pub(crate) fn decode<R: Buf>(bytes: &mut R, read_path: bool) -> coding::Result<Self> {
+757        Ok(Self {
+758            path_id: if read_path { Some(bytes.get()?) } else { None },
+759            sequence: bytes.get_var()?,
+760        })
+761    }
+762
+763    /// Get the [`FrameType`] for this [`RetireConnectionId`]
+764    const fn get_type(&self) -> FrameType {
+765        if self.path_id.is_some() {
+766            FrameType::PathRetireConnectionId
+767        } else {
+768            FrameType::RetireConnectionId
+769        }
+770    }
+771
+772    /// Returns the maximum encoded size on the wire
+773    ///
+774    /// `path_retire_cid` determines whether this frame is a multipath frame. This is a rough upper
+775    /// estimate, does not squeeze every last byte out.
+776    pub(crate) const fn size_bound(path_retire_cid: bool) -> usize {
+777        match path_retire_cid {
+778            true => Self::SIZE_BOUND_MULTIPATH,
+779            false => Self::SIZE_BOUND,
+780        }
+781    }
+782}
+783
+784impl Encodable for RetireConnectionId {
+785    fn encode<W: BufMut>(&self, buf: &mut W) {
+786        buf.write(self.get_type());
+787        if let Some(id) = self.path_id {
+788            buf.write(id);
+789        }
+790        buf.write_var(self.sequence);
+791    }
+792}
+793
+794#[cfg_attr(test, derive(Arbitrary))]
+795#[derive(Clone, Debug, derive_more::Display)]
+796pub(crate) enum Close {
+797    Connection(ConnectionClose),
+798    Application(ApplicationClose),
+799}
+800
+801impl Close {
+802    pub(crate) fn encoder(&self, max_len: usize) -> CloseEncoder<'_> {
+803        CloseEncoder {
+804            close: self,
+805            max_len,
+806        }
+807    }
+808
+809    pub(crate) fn is_transport_layer(&self) -> bool {
+810        matches!(*self, Self::Connection(_))
+811    }
+812}
+813
+814#[derive(derive_more::Display)]
+815#[display("{close}")]
+816pub(crate) struct CloseEncoder<'a> {
+817    pub(crate) close: &'a Close,
+818    max_len: usize,
+819}
+820
+821impl<'a> CloseEncoder<'a> {
+822    const fn get_type(&self) -> FrameType {
+823        match self.close {
+824            Close::Connection(_) => FrameType::ConnectionClose,
+825            Close::Application(_) => FrameType::ApplicationClose,
+826        }
+827    }
+828}
+829
+830impl<'a> Encodable for CloseEncoder<'a> {
+831    fn encode<W: BufMut>(&self, out: &mut W) {
+832        match self.close {
+833            Close::Connection(x) => x.encode(out, self.max_len),
+834            Close::Application(x) => x.encode(out, self.max_len),
+835        }
+836    }
+837}
+838
+839impl From<TransportError> for Close {
+840    fn from(x: TransportError) -> Self {
+841        Self::Connection(x.into())
+842    }
+843}
+844impl From<ConnectionClose> for Close {
+845    fn from(x: ConnectionClose) -> Self {
+846        Self::Connection(x)
+847    }
+848}
+849impl From<ApplicationClose> for Close {
+850    fn from(x: ApplicationClose) -> Self {
+851        Self::Application(x)
+852    }
+853}
+854
+855/// Reason given by the transport for closing the connection
+856#[derive(Debug, Clone, PartialEq, Eq)]
+857#[cfg_attr(test, derive(Arbitrary))]
+858pub struct ConnectionClose {
+859    /// Class of error as encoded in the specification
+860    pub error_code: TransportErrorCode,
+861    /// Type of frame that caused the close
+862    pub frame_type: MaybeFrame,
+863    /// Human-readable reason for the close
+864    #[cfg_attr(test, strategy(proptest::collection::vec(any::<u8>(), 0..64).prop_map(Bytes::from)))]
+865    pub reason: Bytes,
+866}
+867
+868impl fmt::Display for ConnectionClose {
+869    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+870        self.error_code.fmt(f)?;
+871        if !self.reason.as_ref().is_empty() {
+872            f.write_str(": ")?;
+873            f.write_str(&String::from_utf8_lossy(&self.reason))?;
+874        }
+875        Ok(())
+876    }
+877}
+878
+879impl From<TransportError> for ConnectionClose {
+880    fn from(x: TransportError) -> Self {
+881        Self {
+882            error_code: x.code,
+883            frame_type: x.frame,
+884            reason: x.reason.into(),
+885        }
+886    }
+887}
+888
+889impl FrameStruct for ConnectionClose {
+890    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+891}
+892
+893impl ConnectionClose {
+894    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
+895        out.write(FrameType::ConnectionClose); // 1 byte
+896        out.write(self.error_code); // <= 8 bytes
+897        out.write(self.frame_type); // <= 8 bytes
+898        let max_len = max_len
+899            - 3
+900            - self.frame_type.size()
+901            - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
+902        let actual_len = self.reason.len().min(max_len);
+903        out.write_var(actual_len as u64); // <= 8 bytes
+904        out.put_slice(&self.reason[0..actual_len]); // whatever's left
+905    }
+906}
+907
+908/// Reason given by an application for closing the connection
+909#[derive(Debug, Clone, PartialEq, Eq)]
+910#[cfg_attr(test, derive(Arbitrary))]
+911pub struct ApplicationClose {
+912    /// Application-specific reason code
+913    pub error_code: VarInt,
+914    /// Human-readable reason for the close
+915    #[cfg_attr(test, strategy(proptest::collection::vec(any::<u8>(), 0..64).prop_map(Bytes::from)))]
+916    pub reason: Bytes,
+917}
+918
+919impl fmt::Display for ApplicationClose {
+920    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+921        if !self.reason.as_ref().is_empty() {
+922            f.write_str(&String::from_utf8_lossy(&self.reason))?;
+923            f.write_str(" (code ")?;
+924            self.error_code.fmt(f)?;
+925            f.write_str(")")?;
+926        } else {
+927            self.error_code.fmt(f)?;
+928        }
+929        Ok(())
+930    }
+931}
+932
+933impl FrameStruct for ApplicationClose {
+934    const SIZE_BOUND: usize = 1 + 8 + 8;
+935}
+936
+937impl ApplicationClose {
+938    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
+939        out.write(FrameType::ApplicationClose); // 1 byte
+940        out.write(self.error_code); // <= 8 bytes
+941        let max_len = max_len - 3 - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
+942        let actual_len = self.reason.len().min(max_len);
+943        out.write_var(actual_len as u64); // <= 8 bytes
+944        out.put_slice(&self.reason[0..actual_len]); // whatever's left
+945    }
+946}
+947
+948#[derive(Debug, Clone, Eq, PartialEq)]
+949pub(crate) struct PathAck {
+950    pub path_id: PathId,
+951    pub largest: u64,
+952    pub delay: u64,
+953    pub ranges: ArrayRangeSet,
+954    pub ecn: Option<EcnCounts>,
+955}
+956
+957#[cfg(test)]
+958impl proptest::arbitrary::Arbitrary for PathAck {
+959    type Parameters = ();
+960    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+961
+962    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
+963        use proptest::prelude::*;
+964        (
+965            any::<PathId>(),
+966            varint_u64(),
+967            any::<ArrayRangeSet>()
+968                .prop_filter("ranges must be non empty", |ranges| !ranges.is_empty()),
+969            any::<Option<EcnCounts>>(),
+970        )
+971            .prop_map(|(path_id, delay, ranges, ecn)| Self {
+972                path_id,
+973                largest: ranges.max().expect("ranges must be non empty"),
+974                delay,
+975                ranges,
+976                ecn,
+977            })
+978            .boxed()
+979    }
+980}
+981
+982impl PathAck {
+983    pub(crate) fn into_ack(self) -> (Ack, PathId) {
+984        let ack = Ack {
+985            largest: self.largest,
+986            delay: self.delay,
+987            ranges: self.ranges,
+988            ecn: self.ecn,
+989        };
+990
+991        (ack, self.path_id)
+992    }
+993
+994    fn get_type(&self) -> FrameType {
+995        if self.ecn.is_some() {
+996            FrameType::PathAckEcn
+997        } else {
+998            FrameType::PathAck
+999        }
+1000    }
+1001
+1002    pub(crate) fn encoder<'a>(
+1003        path_id: PathId,
+1004        delay: u64,
+1005        ranges: &'a ArrayRangeSet,
+1006        ecn: Option<&'a EcnCounts>,
+1007    ) -> PathAckEncoder<'a> {
+1008        PathAckEncoder {
+1009            path_id,
+1010            delay,
+1011            ranges,
+1012            ecn,
+1013        }
+1014    }
+1015}
+1016
+1017#[derive(derive_more::Display)]
+1018#[display("{} path_id: {path_id} ranges: {ranges:?} delay: {delay}µs", self.get_type())]
+1019pub(crate) struct PathAckEncoder<'a> {
+1020    pub(super) path_id: PathId,
+1021    pub(super) delay: u64,
+1022    pub(super) ranges: &'a ArrayRangeSet,
+1023    pub(super) ecn: Option<&'a EcnCounts>,
+1024}
+1025
+1026impl<'a> PathAckEncoder<'a> {
+1027    const fn get_type(&self) -> FrameType {
+1028        match self.ecn.is_some() {
+1029            true => FrameType::PathAckEcn,
+1030            false => FrameType::PathAck,
+1031        }
+1032    }
+1033}
+1034
+1035impl<'a> Encodable for PathAckEncoder<'a> {
+1036    /// Encode [`Self`] into the given buffer
+1037    ///
+1038    /// The [`FrameType`] will be either [`FrameType::PathAckEcn`] or [`FrameType::PathAck`]
+1039    /// depending on whether [`EcnCounts`] are provided.
+1040    ///
+1041    /// PANICS: if `ranges` is empty.
+1042    fn encode<W: BufMut>(&self, buf: &mut W) {
+1043        let PathAckEncoder {
+1044            path_id,
+1045            delay,
+1046            ranges,
+1047            ecn,
+1048        } = self;
+1049        let mut rest = ranges.iter().rev();
+1050        let first = rest
+1051            .next()
+1052            .expect("Caller has verified ranges is non empty");
+1053        let largest = first.end - 1;
+1054        let first_size = first.end - first.start;
+1055        let kind = match ecn.is_some() {
+1056            true => FrameType::PathAckEcn,
+1057            false => FrameType::PathAck,
+1058        };
+1059        buf.write(kind);
+1060        buf.write(*path_id);
+1061        buf.write_var(largest);
+1062        buf.write_var(*delay);
+1063        buf.write_var(ranges.len() as u64 - 1);
+1064        buf.write_var(first_size - 1);
+1065        let mut prev = first.start;
+1066        for block in rest {
+1067            let size = block.end - block.start;
+1068            buf.write_var(prev - block.end - 1);
+1069            buf.write_var(size - 1);
+1070            prev = block.start;
+1071        }
+1072        if let Some(x) = ecn {
+1073            x.encode(buf)
+1074        }
+1075    }
+1076}
+1077
+1078#[derive(Debug, Clone, Eq, PartialEq)]
+1079pub(crate) struct Ack {
+1080    pub largest: u64,
+1081    pub delay: u64,
+1082    pub ranges: ArrayRangeSet,
+1083    pub ecn: Option<EcnCounts>,
+1084}
+1085
+1086#[cfg(test)]
+1087impl proptest::arbitrary::Arbitrary for Ack {
+1088    type Parameters = ();
+1089    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+1090
+1091    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
+1092        use proptest::prelude::*;
+1093        (
+1094            varint_u64(),
+1095            any::<ArrayRangeSet>()
+1096                .prop_filter("ranges must be non empty", |ranges| !ranges.is_empty()),
+1097            any::<Option<EcnCounts>>(),
+1098        )
+1099            .prop_map(|(delay, ranges, ecn)| Self {
+1100                largest: ranges.max().expect("ranges must be non empty"),
+1101                delay,
+1102                ranges,
+1103                ecn,
+1104            })
+1105            .boxed()
+1106    }
+1107}
+1108
+1109impl Ack {
+1110    pub(crate) fn encoder<'a>(
+1111        delay: u64,
+1112        ranges: &'a ArrayRangeSet,
+1113        ecn: Option<&'a EcnCounts>,
+1114    ) -> AckEncoder<'a> {
+1115        AckEncoder { delay, ranges, ecn }
+1116    }
+1117
+1118    pub(crate) fn iter(&self) -> impl DoubleEndedIterator<Item = Range<u64>> + '_ {
+1119        self.ranges.iter()
+1120    }
+1121
+1122    pub(crate) const fn get_type(&self) -> FrameType {
+1123        if self.ecn.is_some() {
+1124            FrameType::AckEcn
+1125        } else {
+1126            FrameType::Ack
+1127        }
+1128    }
+1129}
+1130
+1131#[derive(derive_more::Display)]
+1132#[display("{} ranges: {ranges:?} delay: {delay}µs", self.get_type())]
+1133pub(crate) struct AckEncoder<'a> {
+1134    pub(crate) delay: u64,
+1135    pub(crate) ranges: &'a ArrayRangeSet,
+1136    pub(crate) ecn: Option<&'a EcnCounts>,
+1137}
+1138
+1139impl<'a> AckEncoder<'a> {
+1140    const fn get_type(&self) -> FrameType {
+1141        match self.ecn.is_some() {
+1142            true => FrameType::AckEcn,
+1143            false => FrameType::Ack,
+1144        }
+1145    }
+1146}
+1147
+1148impl<'a> Encodable for AckEncoder<'a> {
+1149    fn encode<W: BufMut>(&self, buf: &mut W) {
+1150        let AckEncoder { delay, ranges, ecn } = self;
+1151        let mut rest = ranges.iter().rev();
+1152        let first = rest.next().unwrap();
+1153        let largest = first.end - 1;
+1154        let first_size = first.end - first.start;
+1155        let kind = match ecn.is_some() {
+1156            true => FrameType::AckEcn,
+1157            false => FrameType::Ack,
+1158        };
+1159        buf.write(kind);
+1160        buf.write_var(largest);
+1161        buf.write_var(*delay);
+1162        buf.write_var(ranges.len() as u64 - 1);
+1163        buf.write_var(first_size - 1);
+1164        let mut prev = first.start;
+1165        for block in rest {
+1166            let size = block.end - block.start;
+1167            buf.write_var(prev - block.end - 1);
+1168            buf.write_var(size - 1);
+1169            prev = block.start;
+1170        }
+1171        if let Some(x) = ecn {
+1172            x.encode(buf)
+1173        }
+1174    }
+1175}
+1176
+1177#[cfg_attr(test, derive(Arbitrary))]
+1178#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+1179pub(crate) struct EcnCounts {
+1180    #[cfg_attr(test, strategy(varint_u64()))]
+1181    pub ect0: u64,
+1182    #[cfg_attr(test, strategy(varint_u64()))]
+1183    pub ect1: u64,
+1184    #[cfg_attr(test, strategy(varint_u64()))]
+1185    pub ce: u64,
+1186}
+1187
+1188impl std::ops::AddAssign<EcnCodepoint> for EcnCounts {
+1189    fn add_assign(&mut self, rhs: EcnCodepoint) {
+1190        match rhs {
+1191            EcnCodepoint::Ect0 => {
+1192                self.ect0 += 1;
+1193            }
+1194            EcnCodepoint::Ect1 => {
+1195                self.ect1 += 1;
+1196            }
+1197            EcnCodepoint::Ce => {
+1198                self.ce += 1;
+1199            }
+1200        }
+1201    }
+1202}
+1203
+1204impl EcnCounts {
+1205    pub(crate) const ZERO: Self = Self {
+1206        ect0: 0,
+1207        ect1: 0,
+1208        ce: 0,
+1209    };
+1210}
+1211
+1212impl Encodable for EcnCounts {
+1213    fn encode<W: BufMut>(&self, out: &mut W) {
+1214        out.write_var(self.ect0);
+1215        out.write_var(self.ect1);
+1216        out.write_var(self.ce);
+1217    }
+1218}
+1219
+1220#[derive(Debug, Clone)]
+1221#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+1222pub(crate) struct Stream {
+1223    pub(crate) id: StreamId,
+1224    #[cfg_attr(test, strategy(varint_u64()))]
+1225    pub(crate) offset: u64,
+1226    pub(crate) fin: bool,
+1227    #[cfg_attr(test, strategy(Strategy::prop_map(collection::vec(any::<u8>(), 0..100), Bytes::from)))]
+1228    pub(crate) data: Bytes,
+1229}
+1230
+1231impl FrameStruct for Stream {
+1232    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+1233}
+1234
+1235/// Metadata from a stream frame
+1236#[derive(Debug, Clone, derive_more::Display)]
+1237#[display("STREAM id: {id} off: {} len: {} fin: {fin}", offsets.start, offsets.end - offsets.start)]
+1238pub(crate) struct StreamMeta {
+1239    pub(crate) id: StreamId,
+1240    pub(crate) offsets: Range<u64>,
+1241    pub(crate) fin: bool,
+1242}
+1243
+1244// This manual implementation exists because `Default` is not implemented for `StreamId`
+1245impl Default for StreamMeta {
+1246    fn default() -> Self {
+1247        Self {
+1248            id: StreamId(0),
+1249            offsets: 0..0,
+1250            fin: false,
+1251        }
+1252    }
+1253}
+1254
+1255impl StreamMeta {
+1256    pub(crate) fn encoder(self, encode_length: bool) -> StreamMetaEncoder {
+1257        StreamMetaEncoder {
+1258            meta: self,
+1259            encode_length,
+1260        }
+1261    }
+1262
+1263    const fn get_type(&self, encode_length: bool) -> StreamInfo {
+1264        let mut ty = *StreamInfo::VALUES.start();
+1265        if self.offsets.start != 0 {
+1266            ty |= 0x04;
+1267        }
+1268        if encode_length {
+1269            ty |= 0x02;
+1270        }
+1271        if self.fin {
+1272            ty |= 0x01;
+1273        }
+1274        StreamInfo(ty as u8)
+1275    }
+1276}
+1277
+1278#[derive(derive_more::Display)]
+1279#[display("{meta}")]
+1280pub(crate) struct StreamMetaEncoder {
+1281    pub(crate) meta: StreamMeta,
+1282    encode_length: bool,
+1283}
+1284
+1285impl StreamMetaEncoder {
+1286    const fn get_type(&self) -> FrameType {
+1287        FrameType::Stream(self.meta.get_type(self.encode_length))
+1288    }
+1289}
+1290
+1291impl Encodable for StreamMetaEncoder {
+1292    fn encode<W: BufMut>(&self, out: &mut W) {
+1293        let Self {
+1294            meta,
+1295            encode_length,
+1296        } = self;
+1297        out.write_var(meta.get_type(*encode_length).0 as u64); // 1 byte
+1298        out.write(meta.id); // <=8 bytes
+1299        if meta.offsets.start != 0 {
+1300            out.write_var(meta.offsets.start); // <=8 bytes
+1301        }
+1302        if *encode_length {
+1303            out.write_var(meta.offsets.end - meta.offsets.start); // <=8 bytes
+1304        }
+1305    }
+1306}
+1307
+1308/// A vector of [`StreamMeta`] with optimization for the single element case
+1309pub(crate) type StreamMetaVec = TinyVec<[StreamMeta; 1]>;
+1310
+1311#[derive(Debug, Clone, derive_more::Display)]
+1312#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+1313#[display("CRYPTO off: {offset} len = {}", data.len())]
+1314pub(crate) struct Crypto {
+1315    #[cfg_attr(test, strategy(varint_u64()))]
+1316    pub(crate) offset: u64,
+1317    #[cfg_attr(test, strategy(Strategy::prop_map(collection::vec(any::<u8>(), 0..1024), Bytes::from)))]
+1318    pub(crate) data: Bytes,
+1319}
+1320
+1321impl Crypto {
+1322    pub(crate) const SIZE_BOUND: usize = 17;
+1323
+1324    const fn get_type(&self) -> FrameType {
+1325        FrameType::Crypto
+1326    }
+1327}
+1328
+1329impl Encodable for Crypto {
+1330    fn encode<W: BufMut>(&self, out: &mut W) {
+1331        out.write(FrameType::Crypto);
+1332        out.write_var(self.offset);
+1333        out.write_var(self.data.len() as u64);
+1334        out.put_slice(&self.data);
+1335    }
+1336}
+1337
+1338#[derive(Debug, Clone, derive_more::Display)]
+1339#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+1340#[display("NEW_TOKEN")]
+1341pub(crate) struct NewToken {
+1342    #[cfg_attr(test, strategy(Strategy::prop_map(collection::vec(any::<u8>(), 0..1024), Bytes::from)))]
+1343    pub(crate) token: Bytes,
+1344}
+1345
+1346impl Encodable for NewToken {
+1347    fn encode<W: BufMut>(&self, out: &mut W) {
+1348        out.write(FrameType::NewToken);
+1349        out.write_var(self.token.len() as u64);
+1350        out.put_slice(&self.token);
+1351    }
+1352}
+1353
+1354impl NewToken {
+1355    pub(crate) fn size(&self) -> usize {
+1356        1 + VarInt::from_u64(self.token.len() as u64).unwrap().size() + self.token.len()
+1357    }
+1358
+1359    const fn get_type(&self) -> FrameType {
+1360        FrameType::NewToken
+1361    }
+1362}
+1363
+1364#[derive(Debug, Clone, derive_more::Display)]
+1365#[cfg_attr(test, derive(Arbitrary, PartialEq, Eq))]
+1366#[display("MAX_PATH_ID path_id: {_0}")]
+1367pub(crate) struct MaxPathId(pub(crate) PathId);
+1368
+1369impl MaxPathId {
+1370    pub(crate) const SIZE_BOUND: usize =
+1371        FrameType::MaxPathId.size() + VarInt(u32::MAX as u64).size();
+1372
+1373    const fn get_type(&self) -> FrameType {
+1374        FrameType::MaxPathId
+1375    }
+1376}
+1377
+1378impl Decodable for MaxPathId {
+1379    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+1380        Ok(Self(buf.get()?))
+1381    }
+1382}
+1383
+1384impl Encodable for MaxPathId {
+1385    fn encode<B: BufMut>(&self, buf: &mut B) {
+1386        buf.write(FrameType::MaxPathId);
+1387        buf.write(self.0);
+1388    }
+1389}
+1390
+1391#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
+1392#[cfg_attr(test, derive(Arbitrary))]
+1393#[display("PATHS_BLOCKED remote_max_path_id: {_0}")]
+1394pub(crate) struct PathsBlocked(pub(crate) PathId);
+1395
+1396impl PathsBlocked {
+1397    pub(crate) const SIZE_BOUND: usize =
+1398        FrameType::PathsBlocked.size() + VarInt(u32::MAX as u64).size();
+1399
+1400    const fn get_type(&self) -> FrameType {
+1401        FrameType::PathsBlocked
+1402    }
+1403}
+1404
+1405impl Encodable for PathsBlocked {
+1406    fn encode<B: BufMut>(&self, buf: &mut B) {
+1407        buf.write(FrameType::PathsBlocked);
+1408        buf.write(self.0);
+1409    }
+1410}
+1411
+1412impl Decodable for PathsBlocked {
+1413    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
+1414    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+1415        Ok(Self(buf.get()?))
+1416    }
+1417}
+1418
+1419#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
+1420#[cfg_attr(test, derive(Arbitrary))]
+1421#[display("PATH_CIDS_BLOCKED path_id: {path_id} next_seq: {next_seq}")]
+1422pub(crate) struct PathCidsBlocked {
+1423    pub(crate) path_id: PathId,
+1424    pub(crate) next_seq: VarInt,
+1425}
+1426
+1427impl PathCidsBlocked {
+1428    pub(crate) const SIZE_BOUND: usize =
+1429        FrameType::PathCidsBlocked.size() + VarInt(u32::MAX as u64).size() + VarInt::MAX.size();
+1430
+1431    const fn get_type(&self) -> FrameType {
+1432        FrameType::PathCidsBlocked
+1433    }
+1434}
+1435
+1436impl Decodable for PathCidsBlocked {
+1437    fn decode<R: Buf>(buf: &mut R) -> coding::Result<Self> {
+1438        Ok(Self {
+1439            path_id: buf.get()?,
+1440            next_seq: buf.get()?,
+1441        })
+1442    }
+1443}
+1444
+1445impl Encodable for PathCidsBlocked {
+1446    fn encode<W: BufMut>(&self, buf: &mut W) {
+1447        buf.write(FrameType::PathCidsBlocked);
+1448        buf.write(self.path_id);
+1449        buf.write(self.next_seq);
+1450    }
+1451}
+1452
+1453pub(crate) struct Iter {
+1454    bytes: Bytes,
+1455    last_ty: MaybeFrame,
+1456}
+1457
+1458impl Iter {
+1459    pub(crate) fn new(payload: Bytes) -> Result<Self, TransportError> {
+1460        if payload.is_empty() {
+1461            // "An endpoint MUST treat receipt of a packet containing no frames as a
+1462            // connection error of type PROTOCOL_VIOLATION."
+1463            // https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types
+1464            return Err(TransportError::PROTOCOL_VIOLATION(
+1465                "packet payload is empty",
+1466            ));
+1467        }
+1468
+1469        Ok(Self {
+1470            bytes: payload,
+1471            last_ty: MaybeFrame::None,
+1472        })
+1473    }
+1474
+1475    fn take_len(&mut self) -> Result<Bytes, UnexpectedEnd> {
+1476        let len = self.bytes.get_var()?;
+1477        if len > self.bytes.remaining() as u64 {
+1478            return Err(UnexpectedEnd);
+1479        }
+1480        Ok(self.bytes.split_to(len as usize))
+1481    }
+1482
+1483    #[track_caller]
+1484    fn try_next(&mut self) -> Result<Frame, IterErr> {
+1485        self.last_ty = self.bytes.get()?;
+1486
+1487        let ty = match self.last_ty {
+1488            MaybeFrame::None => FrameType::Padding,
+1489            MaybeFrame::Unknown(_other) => return Err(IterErr::InvalidFrameId),
+1490            MaybeFrame::Known(frame_type) => frame_type,
+1491        };
+1492        Ok(match ty {
+1493            FrameType::Padding => Frame::Padding,
+1494            FrameType::ResetStream => Frame::ResetStream(ResetStream {
+1495                id: self.bytes.get()?,
+1496                error_code: self.bytes.get()?,
+1497                final_offset: self.bytes.get()?,
+1498            }),
+1499            FrameType::ConnectionClose => Frame::Close(Close::Connection(ConnectionClose {
+1500                error_code: self.bytes.get()?,
+1501                frame_type: self.bytes.get()?,
+1502                reason: self.take_len()?,
+1503            })),
+1504            FrameType::ApplicationClose => Frame::Close(Close::Application(ApplicationClose {
+1505                error_code: self.bytes.get()?,
+1506                reason: self.take_len()?,
+1507            })),
+1508            FrameType::MaxData => Frame::MaxData(self.bytes.get()?),
+1509            FrameType::MaxStreamData => Frame::MaxStreamData(self.bytes.get()?),
+1510            FrameType::MaxStreamsBidi => Frame::MaxStreams(MaxStreams {
+1511                dir: Dir::Bi,
+1512                count: self.bytes.get_var()?,
+1513            }),
+1514            FrameType::MaxStreamsUni => Frame::MaxStreams(MaxStreams {
+1515                dir: Dir::Uni,
+1516                count: self.bytes.get_var()?,
+1517            }),
+1518            FrameType::Ping => Frame::Ping,
+1519            FrameType::DataBlocked => Frame::DataBlocked(DataBlocked(self.bytes.get_var()?)),
+1520            FrameType::StreamDataBlocked => Frame::StreamDataBlocked(StreamDataBlocked {
+1521                id: self.bytes.get()?,
+1522                offset: self.bytes.get_var()?,
+1523            }),
+1524            FrameType::StreamsBlockedBidi => Frame::StreamsBlocked(StreamsBlocked {
+1525                dir: Dir::Bi,
+1526                limit: self.bytes.get_var()?,
+1527            }),
+1528            FrameType::StreamsBlockedUni => Frame::StreamsBlocked(StreamsBlocked {
+1529                dir: Dir::Uni,
+1530                limit: self.bytes.get_var()?,
+1531            }),
+1532            FrameType::StopSending => Frame::StopSending(StopSending {
+1533                id: self.bytes.get()?,
+1534                error_code: self.bytes.get()?,
+1535            }),
+1536            FrameType::RetireConnectionId | FrameType::PathRetireConnectionId => {
+1537                Frame::RetireConnectionId(RetireConnectionId::decode(
+1538                    &mut self.bytes,
+1539                    ty == FrameType::PathRetireConnectionId,
+1540                )?)
+1541            }
+1542            FrameType::Ack => {
+1543                let largest = self.bytes.get_var()?;
+1544                let delay = self.bytes.get_var()?;
+1545                let ranges = read_ack_blocks(&mut self.bytes, largest)?;
+1546                Frame::Ack(Ack {
+1547                    delay,
+1548                    largest,
+1549                    ranges,
+1550                    ecn: None,
+1551                })
+1552            }
+1553            FrameType::AckEcn => {
+1554                let largest = self.bytes.get_var()?;
+1555                let delay = self.bytes.get_var()?;
+1556                let ranges = read_ack_blocks(&mut self.bytes, largest)?;
+1557                let ecn = Some(EcnCounts {
+1558                    ect0: self.bytes.get_var()?,
+1559                    ect1: self.bytes.get_var()?,
+1560                    ce: self.bytes.get_var()?,
+1561                });
+1562
+1563                Frame::Ack(Ack {
+1564                    delay,
+1565                    largest,
+1566                    ranges,
+1567                    ecn,
+1568                })
+1569            }
+1570            FrameType::PathAck => {
+1571                let path_id = self.bytes.get()?;
+1572                let largest = self.bytes.get_var()?;
+1573                let delay = self.bytes.get_var()?;
+1574                let ranges = read_ack_blocks(&mut self.bytes, largest)?;
+1575                Frame::PathAck(PathAck {
+1576                    path_id,
+1577                    delay,
+1578                    largest,
+1579                    ranges,
+1580                    ecn: None,
+1581                })
+1582            }
+1583            FrameType::PathAckEcn => {
+1584                let path_id = self.bytes.get()?;
+1585                let largest = self.bytes.get_var()?;
+1586                let delay = self.bytes.get_var()?;
+1587                let ranges = read_ack_blocks(&mut self.bytes, largest)?;
+1588                let ecn = Some(EcnCounts {
+1589                    ect0: self.bytes.get_var()?,
+1590                    ect1: self.bytes.get_var()?,
+1591                    ce: self.bytes.get_var()?,
+1592                });
+1593                Frame::PathAck(PathAck {
+1594                    path_id,
+1595                    delay,
+1596                    largest,
+1597                    ranges,
+1598                    ecn,
+1599                })
+1600            }
+1601            FrameType::PathChallenge => Frame::PathChallenge(self.bytes.get()?),
+1602            FrameType::PathResponse => Frame::PathResponse(self.bytes.get()?),
+1603            FrameType::NewConnectionId | FrameType::PathNewConnectionId => {
+1604                let read_path = ty == FrameType::PathNewConnectionId;
+1605                Frame::NewConnectionId(NewConnectionId::read(&mut self.bytes, read_path)?)
+1606            }
+1607            FrameType::Crypto => Frame::Crypto(Crypto {
+1608                offset: self.bytes.get_var()?,
+1609                data: self.take_len()?,
+1610            }),
+1611            FrameType::NewToken => Frame::NewToken(NewToken {
+1612                token: self.take_len()?,
+1613            }),
+1614            FrameType::HandshakeDone => Frame::HandshakeDone,
+1615            FrameType::AckFrequency => Frame::AckFrequency(AckFrequency {
+1616                sequence: self.bytes.get()?,
+1617                ack_eliciting_threshold: self.bytes.get()?,
+1618                request_max_ack_delay: self.bytes.get()?,
+1619                reordering_threshold: self.bytes.get()?,
+1620            }),
+1621            FrameType::ImmediateAck => Frame::ImmediateAck,
+1622            FrameType::ObservedIpv4Addr | FrameType::ObservedIpv6Addr => {
+1623                let is_ipv6 = ty == FrameType::ObservedIpv6Addr;
+1624                let observed = ObservedAddr::read(&mut self.bytes, is_ipv6)?;
+1625                Frame::ObservedAddr(observed)
+1626            }
+1627            FrameType::PathAbandon => Frame::PathAbandon(PathAbandon::decode(&mut self.bytes)?),
+1628            FrameType::PathStatusAvailable => {
+1629                Frame::PathStatusAvailable(PathStatusAvailable::decode(&mut self.bytes)?)
+1630            }
+1631            FrameType::PathStatusBackup => {
+1632                Frame::PathStatusBackup(PathStatusBackup::decode(&mut self.bytes)?)
+1633            }
+1634            FrameType::MaxPathId => Frame::MaxPathId(MaxPathId::decode(&mut self.bytes)?),
+1635            FrameType::PathsBlocked => Frame::PathsBlocked(PathsBlocked::decode(&mut self.bytes)?),
+1636            FrameType::PathCidsBlocked => {
+1637                Frame::PathCidsBlocked(PathCidsBlocked::decode(&mut self.bytes)?)
+1638            }
+1639            FrameType::AddIpv4Address | FrameType::AddIpv6Address => {
+1640                let is_ipv6 = ty == FrameType::AddIpv6Address;
+1641                let add_address = AddAddress::read(&mut self.bytes, is_ipv6)?;
+1642                Frame::AddAddress(add_address)
+1643            }
+1644            FrameType::ReachOutAtIpv4 | FrameType::ReachOutAtIpv6 => {
+1645                let is_ipv6 = ty == FrameType::ReachOutAtIpv6;
+1646                let reach_out = ReachOut::read(&mut self.bytes, is_ipv6)?;
+1647                Frame::ReachOut(reach_out)
+1648            }
+1649            FrameType::RemoveAddress => Frame::RemoveAddress(RemoveAddress::read(&mut self.bytes)?),
+1650            FrameType::Stream(s) => Frame::Stream(Stream {
+1651                id: self.bytes.get()?,
+1652                offset: if s.off() { self.bytes.get_var()? } else { 0 },
+1653                fin: s.fin(),
+1654                data: if s.len() {
+1655                    self.take_len()?
+1656                } else {
+1657                    self.take_remaining()
+1658                },
+1659            }),
+1660            FrameType::Datagram(d) => Frame::Datagram(Datagram {
+1661                data: if d.len() {
+1662                    self.take_len()?
+1663                } else {
+1664                    self.take_remaining()
+1665                },
+1666            }),
+1667        })
+1668    }
+1669
+1670    pub(crate) fn take_remaining(&mut self) -> Bytes {
+1671        mem::take(&mut self.bytes)
+1672    }
+1673}
+1674
+1675impl Iterator for Iter {
+1676    type Item = Result<Frame, InvalidFrame>;
+1677    fn next(&mut self) -> Option<Self::Item> {
+1678        if !self.bytes.has_remaining() {
+1679            return None;
+1680        }
+1681        match self.try_next() {
+1682            Ok(x) => Some(Ok(x)),
+1683            Err(e) => {
+1684                // Corrupt frame, skip it and everything that follows
+1685                self.bytes.clear();
+1686                Some(Err(InvalidFrame {
+1687                    ty: self.last_ty,
+1688                    reason: e.reason(),
+1689                }))
+1690            }
+1691        }
+1692    }
+1693}
+1694
+1695#[derive(Debug)]
+1696pub(crate) struct InvalidFrame {
+1697    pub(crate) ty: MaybeFrame,
+1698    pub(crate) reason: &'static str,
+1699}
+1700
+1701impl From<InvalidFrame> for TransportError {
+1702    fn from(err: InvalidFrame) -> Self {
+1703        let mut te = Self::FRAME_ENCODING_ERROR(err.reason);
+1704        te.frame = err.ty;
+1705        te
+1706    }
+1707}
+1708
+1709/// Decodes the ACK Ranges from the given buffer.
+1710///  This means, reading these three values
+1711///
+1712/// - ACK Range Count
+1713/// - First ACK Range
+1714/// - ACK Ranges
+1715///
+1716/// Ref <https://www.rfc-editor.org/rfc/rfc9000.html#name-ack-ranges>
+1717fn read_ack_blocks(buf: &mut Bytes, mut largest: u64) -> Result<ArrayRangeSet, IterErr> {
+1718    // Ack Range Count
+1719    let num_blocks = buf.get_var()?;
+1720
+1721    let mut out = ArrayRangeSet::new();
+1722    let mut block_to_block;
+1723    let mut range;
+1724
+1725    for num_block in 0..num_blocks + 1 {
+1726        range = buf.get_var()?;
+1727        range += 1;
+1728
+1729        let start = (largest + 1).checked_sub(range).ok_or(IterErr::Malformed)?;
+1730
+1731        if range > 0 {
+1732            out.insert(start..largest + 1);
+1733        }
+1734
+1735        // no gap on the last "block"
+1736        if num_block < num_blocks {
+1737            // skip the gap
+1738            block_to_block = buf.get_var()?;
+1739            block_to_block += 1;
+1740            block_to_block += range;
+1741
+1742            largest = largest
+1743                .checked_sub(block_to_block)
+1744                .ok_or(IterErr::Malformed)?;
+1745        }
+1746    }
+1747
+1748    Ok(out)
+1749}
+1750
+1751#[derive(Debug)]
+1752enum IterErr {
+1753    UnexpectedEnd,
+1754    InvalidFrameId,
+1755    Malformed,
+1756}
+1757
+1758impl IterErr {
+1759    fn reason(&self) -> &'static str {
+1760        use IterErr::*;
+1761        match *self {
+1762            UnexpectedEnd => "unexpected end",
+1763            InvalidFrameId => "invalid frame ID",
+1764            Malformed => "malformed",
+1765        }
+1766    }
+1767}
+1768
+1769impl From<UnexpectedEnd> for IterErr {
+1770    fn from(_: UnexpectedEnd) -> Self {
+1771        Self::UnexpectedEnd
+1772    }
+1773}
+1774
+1775#[allow(unreachable_pub)] // fuzzing only
+1776#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+1777#[cfg_attr(test, derive(Arbitrary))]
+1778#[derive(Debug, Copy, Clone, derive_more::Display)]
+1779#[display("RESET_STREAM id: {id}")]
+1780pub struct ResetStream {
+1781    pub(crate) id: StreamId,
+1782    pub(crate) error_code: VarInt,
+1783    pub(crate) final_offset: VarInt,
+1784}
+1785
+1786impl ResetStream {
+1787    const fn get_type(&self) -> FrameType {
+1788        FrameType::ResetStream
+1789    }
+1790}
+1791
+1792impl FrameStruct for ResetStream {
+1793    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+1794}
+1795
+1796impl Encodable for ResetStream {
+1797    fn encode<W: BufMut>(&self, out: &mut W) {
+1798        out.write(FrameType::ResetStream); // 1 byte
+1799        out.write(self.id); // <= 8 bytes
+1800        out.write(self.error_code); // <= 8 bytes
+1801        out.write(self.final_offset); // <= 8 bytes
+1802    }
+1803}
+1804
+1805#[cfg_attr(test, derive(Arbitrary))]
+1806#[derive(Debug, Copy, Clone, derive_more::Display)]
+1807#[display("STOP_SENDING id: {id}")]
+1808pub(crate) struct StopSending {
+1809    pub(crate) id: StreamId,
+1810    pub(crate) error_code: VarInt,
+1811}
+1812
+1813impl FrameStruct for StopSending {
+1814    const SIZE_BOUND: usize = 1 + 8 + 8;
+1815}
+1816
+1817impl StopSending {
+1818    const fn get_type(&self) -> FrameType {
+1819        FrameType::StopSending
+1820    }
+1821}
+1822
+1823impl Encodable for StopSending {
+1824    fn encode<W: BufMut>(&self, out: &mut W) {
+1825        out.write(FrameType::StopSending); // 1 byte
+1826        out.write(self.id); // <= 8 bytes
+1827        out.write(self.error_code) // <= 8 bytes
+1828    }
+1829}
+1830
+1831#[derive(Debug, Copy, Clone, PartialEq, Eq, derive_more::Display)]
+1832#[display("{} {} seq: {sequence} id: {id}", self.get_type(), DisplayOption::new("path_id", path_id.as_ref()))]
+1833pub(crate) struct NewConnectionId {
+1834    pub(crate) path_id: Option<PathId>,
+1835    pub(crate) sequence: u64,
+1836    pub(crate) retire_prior_to: u64,
+1837    pub(crate) id: ConnectionId,
+1838    pub(crate) reset_token: ResetToken,
+1839}
+1840
+1841#[cfg(test)]
+1842fn connection_id_and_reset_token() -> impl Strategy<Value = (crate::ConnectionId, ResetToken)> {
+1843    (any::<ConnectionId>(), any::<[u8; 64]>()).prop_map(|(id, reset_key)| {
+1844        #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+1845        use aws_lc_rs::hmac;
+1846        #[cfg(feature = "ring")]
+1847        use ring::hmac;
+1848        let key = hmac::Key::new(hmac::HMAC_SHA256, &reset_key);
+1849        (id, ResetToken::new(&key, id))
+1850    })
+1851}
+1852
+1853#[cfg(test)]
+1854impl proptest::arbitrary::Arbitrary for NewConnectionId {
+1855    type Parameters = ();
+1856    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+1857
+1858    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
+1859        use proptest::prelude::*;
+1860        (
+1861            any::<Option<PathId>>(),
+1862            varint_u64(),
+1863            varint_u64(),
+1864            connection_id_and_reset_token(),
+1865        )
+1866            .prop_map(|(path_id, a, b, (id, reset_token))| {
+1867                let sequence = std::cmp::max(a, b);
+1868                let retire_prior_to = std::cmp::min(a, b);
+1869                Self {
+1870                    path_id,
+1871                    sequence,
+1872                    retire_prior_to,
+1873                    id,
+1874                    reset_token,
+1875                }
+1876            })
+1877            .boxed()
+1878    }
+1879}
+1880
+1881impl NewConnectionId {
+1882    /// Maximum size of this frame when the frame type is [`FrameType::NewConnectionId`],
+1883    pub(crate) const SIZE_BOUND: usize = {
+1884        let type_len = FrameType::NewConnectionId.size();
+1885        let seq_max_len = 8usize;
+1886        let retire_prior_to_max_len = 8usize;
+1887        let cid_len_len = 1;
+1888        let cid_len = 160;
+1889        let reset_token_len = 16;
+1890        type_len + seq_max_len + retire_prior_to_max_len + cid_len_len + cid_len + reset_token_len
+1891    };
+1892
+1893    /// Maximum size of this frame when the frame type is [`FrameType::PathNewConnectionId`],
+1894    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
+1895        let type_len = FrameType::PathNewConnectionId.size();
+1896        let path_id_len = VarInt::from_u32(u32::MAX).size();
+1897        let seq_max_len = 8usize;
+1898        let retire_prior_to_max_len = 8usize;
+1899        let cid_len_len = 1;
+1900        let cid_len = 160;
+1901        let reset_token_len = 16;
+1902        type_len
+1903            + path_id_len
+1904            + seq_max_len
+1905            + retire_prior_to_max_len
+1906            + cid_len_len
+1907            + cid_len
+1908            + reset_token_len
+1909    };
+1910
+1911    const fn get_type(&self) -> FrameType {
+1912        if self.path_id.is_some() {
+1913            FrameType::PathNewConnectionId
+1914        } else {
+1915            FrameType::NewConnectionId
+1916        }
+1917    }
+1918
+1919    /// Returns the maximum encoded size on the wire.
+1920    ///
+1921    /// This is a rough upper estimate, does not squeeze every last byte out.
+1922    pub(crate) const fn size_bound(path_new_cid: bool, cid_len: usize) -> usize {
+1923        let upper_bound = match path_new_cid {
+1924            true => Self::SIZE_BOUND_MULTIPATH,
+1925            false => Self::SIZE_BOUND,
+1926        };
+1927        // instead of using the maximum cid len, use the provided one
+1928        upper_bound - 160 + cid_len
+1929    }
+1930
+1931    fn read<R: Buf>(bytes: &mut R, read_path: bool) -> Result<Self, IterErr> {
+1932        let path_id = if read_path { Some(bytes.get()?) } else { None };
+1933        let sequence = bytes.get_var()?;
+1934        let retire_prior_to = bytes.get_var()?;
+1935        if retire_prior_to > sequence {
+1936            return Err(IterErr::Malformed);
+1937        }
+1938        let length = bytes.get::<u8>()? as usize;
+1939        if length > MAX_CID_SIZE || length == 0 {
+1940            return Err(IterErr::Malformed);
+1941        }
+1942        if length > bytes.remaining() {
+1943            return Err(IterErr::UnexpectedEnd);
+1944        }
+1945        let mut stage = [0; MAX_CID_SIZE];
+1946        bytes.copy_to_slice(&mut stage[0..length]);
+1947        let id = ConnectionId::new(&stage[..length]);
+1948        if bytes.remaining() < 16 {
+1949            return Err(IterErr::UnexpectedEnd);
+1950        }
+1951        let mut reset_token = [0; RESET_TOKEN_SIZE];
+1952        bytes.copy_to_slice(&mut reset_token);
+1953        Ok(Self {
+1954            path_id,
+1955            sequence,
+1956            retire_prior_to,
+1957            id,
+1958            reset_token: reset_token.into(),
+1959        })
+1960    }
+1961
+1962    pub(crate) fn issued(&self) -> crate::shared::IssuedCid {
+1963        crate::shared::IssuedCid {
+1964            path_id: self.path_id.unwrap_or_default(),
+1965            sequence: self.sequence,
+1966            id: self.id,
+1967            reset_token: self.reset_token,
+1968        }
+1969    }
+1970}
+1971
+1972impl Encodable for NewConnectionId {
+1973    fn encode<W: BufMut>(&self, out: &mut W) {
+1974        out.write(self.get_type());
+1975        if let Some(id) = self.path_id {
+1976            out.write(id);
+1977        }
+1978        out.write_var(self.sequence);
+1979        out.write_var(self.retire_prior_to);
+1980        out.write(self.id.len() as u8);
+1981        out.put_slice(&self.id);
+1982        out.put_slice(&self.reset_token);
+1983    }
+1984}
+1985
+1986impl FrameStruct for NewConnectionId {
+1987    const SIZE_BOUND: usize = 1 + 8 + 8 + 1 + MAX_CID_SIZE + RESET_TOKEN_SIZE;
+1988}
+1989
+1990/// An unreliable datagram
+1991#[derive(Debug, Clone, derive_more::Display)]
+1992#[cfg_attr(test, derive(Arbitrary))]
+1993#[display("DATAGRAM len: {}", data.len())]
+1994pub struct Datagram {
+1995    /// Payload
+1996    #[cfg_attr(test, strategy(Strategy::prop_map(collection::vec(any::<u8>(), 0..100), Bytes::from)))]
+1997    pub data: Bytes,
+1998}
+1999
+2000impl FrameStruct for Datagram {
+2001    const SIZE_BOUND: usize = 1 + 8;
+2002}
+2003
+2004impl Datagram {
+2005    pub(crate) fn size(&self, length: bool) -> usize {
+2006        1 + if length {
+2007            VarInt::from_u64(self.data.len() as u64).unwrap().size()
+2008        } else {
+2009            0
+2010        } + self.data.len()
+2011    }
+2012
+2013    const fn get_type(&self) -> FrameType {
+2014        FrameType::Datagram(DatagramInfo(*DatagramInfo::VALUES.start() as u8))
+2015    }
+2016}
+2017
+2018impl Encodable for Datagram {
+2019    fn encode<B: BufMut>(&self, out: &mut B) {
+2020        // A datagram is encoded only after this is verified.
+2021        const ENCODE_LEN: bool = true;
+2022        out.write(FrameType::Datagram(DatagramInfo(
+2023            *DatagramInfo::VALUES.start() as u8 | u8::from(ENCODE_LEN),
+2024        ))); // 1 byte
+2025        // Safe to unwrap because we check length sanity before queueing datagrams
+2026        out.write(VarInt::from_u64(self.data.len() as u64).unwrap()); // <= 8 bytes
+2027        out.put_slice(&self.data);
+2028    }
+2029}
+2030
+2031#[derive(Debug, Copy, Clone, PartialEq, Eq, derive_more::Display)]
+2032#[cfg_attr(test, derive(Arbitrary))]
+2033#[display("ACK_FREQUENCY max_ack_delay: {}µs", request_max_ack_delay.0)]
+2034pub(crate) struct AckFrequency {
+2035    pub(crate) sequence: VarInt,
+2036    pub(crate) ack_eliciting_threshold: VarInt,
+2037    pub(crate) request_max_ack_delay: VarInt,
+2038    pub(crate) reordering_threshold: VarInt,
+2039}
+2040
+2041impl AckFrequency {
+2042    const fn get_type(&self) -> FrameType {
+2043        FrameType::AckFrequency
+2044    }
+2045}
+2046
+2047impl Encodable for AckFrequency {
+2048    fn encode<W: BufMut>(&self, buf: &mut W) {
+2049        buf.write(FrameType::AckFrequency);
+2050        buf.write(self.sequence);
+2051        buf.write(self.ack_eliciting_threshold);
+2052        buf.write(self.request_max_ack_delay);
+2053        buf.write(self.reordering_threshold);
+2054    }
+2055}
+2056
+2057/* Address Discovery https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/ */
+2058
+2059/// Conjunction of the information contained in the address discovery frames
+2060/// ([`FrameType::ObservedIpv4Addr`], [`FrameType::ObservedIpv6Addr`]).
+2061#[derive(Debug, PartialEq, Eq, Clone, derive_more::Display)]
+2062#[display("{} seq_no: {seq_no} addr: {}", self.get_type(), self.socket_addr())]
+2063#[cfg_attr(test, derive(Arbitrary))]
+2064pub(crate) struct ObservedAddr {
+2065    /// Monotonically increasing integer within the same connection.
+2066    pub(crate) seq_no: VarInt,
+2067    /// Reported observed address.
+2068    pub(crate) ip: IpAddr,
+2069    /// Reported observed port.
+2070    pub(crate) port: u16,
+2071}
+2072
+2073impl ObservedAddr {
+2074    pub(crate) fn new<N: Into<VarInt>>(remote: std::net::SocketAddr, seq_no: N) -> Self {
+2075        Self {
+2076            ip: remote.ip(),
+2077            port: remote.port(),
+2078            seq_no: seq_no.into(),
+2079        }
+2080    }
+2081
+2082    /// Get the [`FrameType`] for this frame.
+2083    const fn get_type(&self) -> FrameType {
+2084        if self.ip.is_ipv6() {
+2085            FrameType::ObservedIpv6Addr
+2086        } else {
+2087            FrameType::ObservedIpv4Addr
+2088        }
+2089    }
+2090
+2091    /// Compute the number of bytes needed to encode the frame.
+2092    pub(crate) fn size(&self) -> usize {
+2093        let type_size = self.get_type().size();
+2094        let req_id_bytes = self.seq_no.size();
+2095        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+2096        let port_bytes = 2;
+2097        type_size + req_id_bytes + ip_bytes + port_bytes
+2098    }
+2099
+2100    /// Reads the frame contents from the buffer.
+2101    ///
+2102    /// Should only be called when the frame type has been identified as
+2103    /// [`FrameType::ObservedIpv4Addr`] or [`FrameType::ObservedIpv6Addr`].
+2104    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+2105        let seq_no = bytes.get()?;
+2106        let ip = if is_ipv6 {
+2107            IpAddr::V6(bytes.get()?)
+2108        } else {
+2109            IpAddr::V4(bytes.get()?)
+2110        };
+2111        let port = bytes.get()?;
+2112        Ok(Self { seq_no, ip, port })
+2113    }
+2114
+2115    /// Gives the [`SocketAddr`] reported in the frame.
+2116    pub(crate) fn socket_addr(&self) -> SocketAddr {
+2117        (self.ip, self.port).into()
+2118    }
+2119}
+2120
+2121impl Encodable for ObservedAddr {
+2122    fn encode<W: BufMut>(&self, buf: &mut W) {
+2123        buf.write(self.get_type());
+2124        buf.write(self.seq_no);
+2125        match self.ip {
+2126            IpAddr::V4(ipv4_addr) => {
+2127                buf.write(ipv4_addr);
+2128            }
+2129            IpAddr::V6(ipv6_addr) => {
+2130                buf.write(ipv6_addr);
+2131            }
+2132        }
+2133        buf.write::<u16>(self.port);
+2134    }
+2135}
+2136
+2137/* Multipath <https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/> */
+2138
+2139#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+2140#[cfg_attr(test, derive(Arbitrary))]
+2141#[display("PATH_ABANDON path_id: {path_id}")]
+2142pub(crate) struct PathAbandon {
+2143    pub(crate) path_id: PathId,
+2144    pub(crate) error_code: TransportErrorCode,
+2145}
+2146
+2147impl PathAbandon {
+2148    pub(crate) const SIZE_BOUND: usize = FrameType::PathAbandon.size() + 8 + 8;
+2149
+2150    const fn get_type(&self) -> FrameType {
+2151        FrameType::PathAbandon
+2152    }
+2153}
+2154
+2155impl Encodable for PathAbandon {
+2156    fn encode<W: BufMut>(&self, buf: &mut W) {
+2157        buf.write(FrameType::PathAbandon);
+2158        buf.write(self.path_id);
+2159        buf.write(self.error_code);
+2160    }
+2161}
+2162
+2163impl Decodable for PathAbandon {
+2164    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2165        Ok(Self {
+2166            path_id: bytes.get()?,
+2167            error_code: bytes.get()?,
+2168        })
+2169    }
+2170}
+2171
+2172#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+2173#[cfg_attr(test, derive(Arbitrary))]
+2174#[display("PATH_STATUS_AVAILABLE path_id: {path_id} seq_no: {status_seq_no}")]
+2175pub(crate) struct PathStatusAvailable {
+2176    pub(crate) path_id: PathId,
+2177    pub(crate) status_seq_no: VarInt,
+2178}
+2179
+2180impl PathStatusAvailable {
+2181    const TYPE: FrameType = FrameType::PathStatusAvailable;
+2182    pub(crate) const SIZE_BOUND: usize = FrameType::PathStatusAvailable.size() + 8 + 8;
+2183
+2184    const fn get_type(&self) -> FrameType {
+2185        FrameType::PathStatusAvailable
+2186    }
+2187}
+2188
+2189impl Encodable for PathStatusAvailable {
+2190    fn encode<W: BufMut>(&self, buf: &mut W) {
+2191        buf.write(Self::TYPE);
+2192        buf.write(self.path_id);
+2193        buf.write(self.status_seq_no);
+2194    }
+2195}
+2196
+2197impl Decodable for PathStatusAvailable {
+2198    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2199        Ok(Self {
+2200            path_id: bytes.get()?,
+2201            status_seq_no: bytes.get()?,
+2202        })
+2203    }
+2204}
+2205
+2206#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+2207#[cfg_attr(test, derive(Arbitrary))]
+2208#[display("PATH_STATUS_BACKUP path_id: {path_id} seq_no: {status_seq_no}")]
+2209pub(crate) struct PathStatusBackup {
+2210    pub(crate) path_id: PathId,
+2211    pub(crate) status_seq_no: VarInt,
+2212}
+2213
+2214impl PathStatusBackup {
+2215    const TYPE: FrameType = FrameType::PathStatusBackup;
+2216
+2217    const fn get_type(&self) -> FrameType {
+2218        FrameType::PathStatusBackup
+2219    }
+2220}
+2221
+2222impl Encodable for PathStatusBackup {
+2223    fn encode<W: BufMut>(&self, buf: &mut W) {
+2224        buf.write(Self::TYPE);
+2225        buf.write(self.path_id);
+2226        buf.write(self.status_seq_no);
+2227    }
+2228}
+2229
+2230impl Decodable for PathStatusBackup {
+2231    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2232        Ok(Self {
+2233            path_id: bytes.get()?,
+2234            status_seq_no: bytes.get()?,
+2235        })
+2236    }
+2237}
+2238
+2239/* Nat traversal frames */
+2240
+2241/// Conjunction of the information contained in the add address frames
+2242/// ([`FrameType::AddIpv4Address`], [`FrameType::AddIpv6Address`]).
+2243#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord, derive_more::Display)]
+2244#[display("{} seq_no: {seq_no} addr: {}", self.get_type(), self.socket_addr())]
+2245#[cfg_attr(test, derive(Arbitrary))]
+2246pub(crate) struct AddAddress {
+2247    /// Monotonically increasing integer within the same connection
+2248    // TODO(@divma): both assumed, the draft has no mention of this but it's standard
+2249    pub(crate) seq_no: VarInt,
+2250    /// Address to include in the known set
+2251    pub(crate) ip: IpAddr,
+2252    /// Port to use with this address
+2253    pub(crate) port: u16,
+2254}
+2255
+2256// TODO(@divma): remove
+2257#[allow(dead_code)]
+2258impl AddAddress {
+2259    /// Smallest number of bytes this type of frame is guaranteed to fit within.
+2260    pub(crate) const SIZE_BOUND: usize = Self {
+2261        ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
+2262        port: u16::MAX,
+2263        seq_no: VarInt::MAX,
+2264    }
+2265    .size();
+2266
+2267    pub(crate) const fn new((ip, port): (IpAddr, u16), seq_no: VarInt) -> Self {
+2268        Self { ip, port, seq_no }
+2269    }
+2270
+2271    /// Get the [`FrameType`] for this frame.
+2272    const fn get_type(&self) -> FrameType {
+2273        if self.ip.is_ipv6() {
+2274            FrameType::AddIpv6Address
+2275        } else {
+2276            FrameType::AddIpv4Address
+2277        }
+2278    }
+2279
+2280    /// Compute the number of bytes needed to encode the frame
+2281    pub(crate) const fn size(&self) -> usize {
+2282        let type_size = self.get_type().size();
+2283        let seq_no_bytes = self.seq_no.size();
+2284        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+2285        let port_bytes = 2;
+2286        type_size + seq_no_bytes + ip_bytes + port_bytes
+2287    }
+2288
+2289    /// Read the frame contents from the buffer
+2290    ///
+2291    /// Should only be called when the frame type has been identified as
+2292    /// [`FrameType::AddIpv4Address`] or [`FrameType::AddIpv6Address`].
+2293    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+2294        let seq_no = bytes.get()?;
+2295        let ip = if is_ipv6 {
+2296            IpAddr::V6(bytes.get()?)
+2297        } else {
+2298            IpAddr::V4(bytes.get()?)
+2299        };
+2300        let port = bytes.get()?;
+2301        Ok(Self { seq_no, ip, port })
+2302    }
+2303
+2304    /// Give the [`SocketAddr`] encoded in the frame
+2305    pub(crate) fn socket_addr(&self) -> SocketAddr {
+2306        self.ip_port().into()
+2307    }
+2308
+2309    pub(crate) fn ip_port(&self) -> (IpAddr, u16) {
+2310        (self.ip, self.port)
+2311    }
+2312}
+2313
+2314impl Encodable for AddAddress {
+2315    fn encode<W: BufMut>(&self, buf: &mut W) {
+2316        buf.write(self.get_type());
+2317        buf.write(self.seq_no);
+2318        match self.ip {
+2319            IpAddr::V4(ipv4_addr) => {
+2320                buf.write(ipv4_addr);
+2321            }
+2322            IpAddr::V6(ipv6_addr) => {
+2323                buf.write(ipv6_addr);
+2324            }
+2325        }
+2326        buf.write::<u16>(self.port);
+2327    }
+2328}
+2329
+2330/// Conjunction of the information contained in the reach out frames
+2331/// ([`FrameType::ReachOutAtIpv4`], [`FrameType::ReachOutAtIpv6`])
+2332#[derive(Debug, PartialEq, Eq, Clone, derive_more::Display)]
+2333#[display("REACH_OUT round: {round} local_addr: {}", self.socket_addr())]
+2334#[cfg_attr(test, derive(Arbitrary))]
+2335pub(crate) struct ReachOut {
+2336    /// The sequence number of the NAT Traversal attempts
+2337    pub(crate) round: VarInt,
+2338    /// Address to use
+2339    pub(crate) ip: IpAddr,
+2340    /// Port to use with this address
+2341    pub(crate) port: u16,
+2342}
+2343
+2344// TODO(@divma): remove
+2345#[allow(dead_code)]
+2346impl ReachOut {
+2347    /// Smallest number of bytes this type of frame is guaranteed to fit within
+2348    pub(crate) const SIZE_BOUND: usize = Self {
+2349        round: VarInt::MAX,
+2350        ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
+2351        port: u16::MAX,
+2352    }
+2353    .size();
+2354
+2355    pub(crate) const fn new(round: VarInt, (ip, port): (IpAddr, u16)) -> Self {
+2356        Self { round, ip, port }
+2357    }
+2358
+2359    /// Get the [`FrameType`] for this frame
+2360    pub(crate) const fn get_type(&self) -> FrameType {
+2361        if self.ip.is_ipv6() {
+2362            FrameType::ReachOutAtIpv6
+2363        } else {
+2364            FrameType::ReachOutAtIpv4
+2365        }
+2366    }
+2367
+2368    /// Compute the number of bytes needed to encode the frame
+2369    pub(crate) const fn size(&self) -> usize {
+2370        let type_size = self.get_type().size();
+2371        let round_bytes = self.round.size();
+2372        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+2373        let port_bytes = 2;
+2374        type_size + round_bytes + ip_bytes + port_bytes
+2375    }
+2376
+2377    /// Read the frame contents from the buffer
+2378    ///
+2379    /// Should only be called when the frame type has been identified as
+2380    /// [`FrameType::ReachOutAtIpv4`] or [`FrameType::ReachOutAtIpv6`].
+2381    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+2382        let round = bytes.get()?;
+2383        let ip = if is_ipv6 {
+2384            IpAddr::V6(bytes.get()?)
+2385        } else {
+2386            IpAddr::V4(bytes.get()?)
+2387        };
+2388        let port = bytes.get()?;
+2389        Ok(Self { round, ip, port })
+2390    }
+2391
+2392    /// Give the [`SocketAddr`] encoded in the frame
+2393    pub(crate) fn socket_addr(&self) -> SocketAddr {
+2394        (self.ip, self.port).into()
+2395    }
+2396}
+2397
+2398impl Encodable for ReachOut {
+2399    fn encode<W: BufMut>(&self, buf: &mut W) {
+2400        buf.write(self.get_type());
+2401        buf.write(self.round);
+2402        match self.ip {
+2403            IpAddr::V4(ipv4_addr) => {
+2404                buf.write(ipv4_addr);
+2405            }
+2406            IpAddr::V6(ipv6_addr) => {
+2407                buf.write(ipv6_addr);
+2408            }
+2409        }
+2410        buf.write::<u16>(self.port);
+2411    }
+2412}
+2413
+2414/// Frame signaling an address is no longer being advertised
+2415#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord, derive_more::Display)]
+2416#[cfg_attr(test, derive(Arbitrary))]
+2417#[display("REMOVE_ADDRESS seq_no: {seq_no}")]
+2418pub(crate) struct RemoveAddress {
+2419    /// The sequence number of the address advertisement to be removed
+2420    pub(crate) seq_no: VarInt,
+2421}
+2422
+2423// TODO(@divma): remove
+2424#[allow(dead_code)]
+2425impl RemoveAddress {
+2426    /// [`FrameType`] of this frame
+2427    pub(crate) const TYPE: FrameType = FrameType::RemoveAddress;
+2428
+2429    /// Smallest number of bytes this type of frame is guaranteed to fit within
+2430    pub(crate) const SIZE_BOUND: usize = Self::new(VarInt::MAX).size();
+2431
+2432    pub(crate) const fn new(seq_no: VarInt) -> Self {
+2433        Self { seq_no }
+2434    }
+2435
+2436    /// Compute the number of bytes needed to encode the frame
+2437    pub(crate) const fn size(&self) -> usize {
+2438        let type_size = Self::TYPE.size();
+2439        let seq_no_bytes = self.seq_no.size();
+2440        type_size + seq_no_bytes
+2441    }
+2442
+2443    /// Read the frame contents from the buffer
+2444    ///
+2445    /// Should only be called when the frame type has been identified as
+2446    /// [`FrameType::RemoveAddress`].
+2447    pub(crate) fn read<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2448        Ok(Self {
+2449            seq_no: bytes.get()?,
+2450        })
+2451    }
+2452
+2453    const fn get_type(&self) -> FrameType {
+2454        FrameType::RemoveAddress
+2455    }
+2456}
+2457
+2458impl Encodable for RemoveAddress {
+2459    fn encode<W: BufMut>(&self, buf: &mut W) {
+2460        buf.write(Self::TYPE);
+2461        buf.write(self.seq_no);
+2462    }
+2463}
+2464
+2465/// Helper struct for display implementations.
+2466// NOTE: Due to lifetimes in fmt::Arguments it's not possible to make this a simple function that
+2467// avoids allocations.
+2468struct DisplayOption<T: Display> {
+2469    field_name: &'static str,
+2470    op: Option<T>,
+2471}
+2472
+2473impl<T: Display> DisplayOption<T> {
+2474    fn new(field_name: &'static str, op: Option<T>) -> Self {
+2475        Self { field_name, op }
+2476    }
+2477}
+2478
+2479impl<T: Display> Display for DisplayOption<T> {
+2480    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+2481        if let Some(x) = self.op.as_ref() {
+2482            write!(f, "{}: {x}", self.field_name)
+2483        } else {
+2484            fmt::Result::Ok(())
+2485        }
+2486    }
+2487}
+2488
+2489#[cfg(test)]
+2490mod test {
+2491    use super::*;
+2492    use crate::coding::Encodable;
+2493    use assert_matches::assert_matches;
+2494
+2495    #[test]
+2496    fn frame_type() {
+2497        assert_eq!(
+2498            FrameType::try_from(FrameType::Padding.to_u64()),
+2499            Ok(FrameType::Padding),
+2500        );
+2501
+2502        assert_eq!(
+2503            FrameType::try_from(FrameType::Datagram(DatagramInfo(0x30)).to_u64()),
+2504            Ok(FrameType::Datagram(DatagramInfo(0x30))),
+2505        );
+2506
+2507        assert_eq!(
+2508            FrameType::try_from(FrameType::Stream(StreamInfo(0x08)).to_u64()),
+2509            Ok(FrameType::Stream(StreamInfo(0x08))),
+2510        );
+2511    }
+2512
+2513    #[track_caller]
+2514    fn frames(buf: Vec<u8>) -> Vec<Frame> {
+2515        Iter::new(Bytes::from(buf))
+2516            .unwrap()
+2517            .collect::<Result<Vec<_>, _>>()
+2518            .unwrap()
+2519    }
+2520
+2521    #[test]
+2522    fn ack_coding() {
+2523        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
+2524        let mut ranges = ArrayRangeSet::new();
+2525        for &packet in PACKETS {
+2526            ranges.insert(packet..packet + 1);
+2527        }
+2528        let mut buf = Vec::new();
+2529        const ECN: EcnCounts = EcnCounts {
+2530            ect0: 42,
+2531            ect1: 24,
+2532            ce: 12,
+2533        };
+2534        Ack::encoder(42, &ranges, Some(&ECN)).encode(&mut buf);
+2535        let frames = frames(buf);
+2536        assert_eq!(frames.len(), 1);
+2537        match frames[0] {
+2538            Frame::Ack(ref ack) => {
+2539                let mut packets = ack.iter().flatten().collect::<Vec<_>>();
+2540                packets.sort_unstable();
+2541                assert_eq!(&packets[..], PACKETS);
+2542                assert_eq!(ack.ecn, Some(ECN));
+2543            }
+2544            ref x => panic!("incorrect frame {x:?}"),
+2545        }
+2546    }
+2547
+2548    #[test]
+2549    #[allow(clippy::range_plus_one)]
+2550    fn path_ack_coding_with_ecn() {
+2551        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
+2552        let mut ranges = ArrayRangeSet::new();
+2553        for &packet in PACKETS {
+2554            ranges.insert(packet..packet + 1);
+2555        }
+2556        let mut buf = Vec::new();
+2557        const ECN: EcnCounts = EcnCounts {
+2558            ect0: 42,
+2559            ect1: 24,
+2560            ce: 12,
+2561        };
+2562        const PATH_ID: PathId = PathId::MAX;
+2563        PathAck::encoder(PATH_ID, 42, &ranges, Some(&ECN)).encode(&mut buf);
+2564        let frames = frames(buf);
+2565        assert_eq!(frames.len(), 1);
+2566        match frames[0] {
+2567            Frame::PathAck(ref ack) => {
+2568                assert_eq!(ack.path_id, PATH_ID);
+2569                let mut packets = ack.ranges.iter().flatten().collect::<Vec<_>>();
+2570                packets.sort_unstable();
+2571                assert_eq!(&packets[..], PACKETS);
+2572                assert_eq!(ack.ecn, Some(ECN));
+2573            }
+2574            ref x => panic!("incorrect frame {x:?}"),
+2575        }
+2576    }
+2577
+2578    #[test]
+2579    #[allow(clippy::range_plus_one)]
+2580    fn path_ack_coding_no_ecn() {
+2581        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
+2582        let mut ranges = ArrayRangeSet::new();
+2583        for &packet in PACKETS {
+2584            ranges.insert(packet..packet + 1);
+2585        }
+2586        let mut buf = Vec::new();
+2587        const PATH_ID: PathId = PathId::MAX;
+2588        PathAck::encoder(PATH_ID, 42, &ranges, None).encode(&mut buf);
+2589        let frames = frames(buf);
+2590        assert_eq!(frames.len(), 1);
+2591        match frames[0] {
+2592            Frame::PathAck(ref ack) => {
+2593                assert_eq!(ack.path_id, PATH_ID);
+2594                let mut packets = ack.ranges.iter().flatten().collect::<Vec<_>>();
+2595                packets.sort_unstable();
+2596                assert_eq!(&packets[..], PACKETS);
+2597                assert_eq!(ack.ecn, None);
+2598            }
+2599            ref x => panic!("incorrect frame {x:?}"),
+2600        }
+2601    }
+2602
+2603    #[test]
+2604    fn ack_frequency_coding() {
+2605        let mut buf = Vec::new();
+2606        let original = AckFrequency {
+2607            sequence: VarInt(42),
+2608            ack_eliciting_threshold: VarInt(20),
+2609            request_max_ack_delay: VarInt(50_000),
+2610            reordering_threshold: VarInt(1),
+2611        };
+2612        original.encode(&mut buf);
+2613        let frames = frames(buf);
+2614        assert_eq!(frames.len(), 1);
+2615        match &frames[0] {
+2616            Frame::AckFrequency(decoded) => assert_eq!(decoded, &original),
+2617            x => panic!("incorrect frame {x:?}"),
+2618        }
+2619    }
+2620
+2621    #[test]
+2622    fn immediate_ack_coding() {
+2623        let mut buf = Vec::new();
+2624        FrameType::ImmediateAck.encode(&mut buf);
+2625        let frames = frames(buf);
+2626        assert_eq!(frames.len(), 1);
+2627        assert_matches!(&frames[0], Frame::ImmediateAck);
+2628    }
+2629
+2630    /// Test that encoding and decoding [`ObservedAddr`] produces the same result.
+2631    #[test]
+2632    fn test_observed_addr_roundrip() {
+2633        let observed_addr = ObservedAddr {
+2634            seq_no: VarInt(42),
+2635            ip: std::net::Ipv4Addr::LOCALHOST.into(),
+2636            port: 4242,
+2637        };
+2638        let mut buf = Vec::with_capacity(observed_addr.size());
+2639        observed_addr.encode(&mut buf);
+2640
+2641        assert_eq!(
+2642            observed_addr.size(),
+2643            buf.len(),
+2644            "expected written bytes and actual size differ"
+2645        );
+2646
+2647        let mut decoded = frames(buf);
+2648        assert_eq!(decoded.len(), 1);
+2649        match decoded.pop().expect("non empty") {
+2650            Frame::ObservedAddr(decoded) => assert_eq!(decoded, observed_addr),
+2651            x => panic!("incorrect frame {x:?}"),
+2652        }
+2653    }
+2654
+2655    #[test]
+2656    fn test_path_abandon_roundtrip() {
+2657        let abandon = PathAbandon {
+2658            path_id: PathId(42),
+2659            error_code: TransportErrorCode::NO_ERROR,
+2660        };
+2661        let mut buf = Vec::new();
+2662        abandon.encode(&mut buf);
+2663
+2664        let mut decoded = frames(buf);
+2665        assert_eq!(decoded.len(), 1);
+2666        match decoded.pop().expect("non empty") {
+2667            Frame::PathAbandon(decoded) => assert_eq!(decoded, abandon),
+2668            x => panic!("incorrect frame {x:?}"),
+2669        }
+2670    }
+2671
+2672    #[test]
+2673    fn test_path_status_available_roundtrip() {
+2674        let path_status_available = PathStatusAvailable {
+2675            path_id: PathId(42),
+2676            status_seq_no: VarInt(73),
+2677        };
+2678        let mut buf = Vec::new();
+2679        path_status_available.encode(&mut buf);
+2680
+2681        let mut decoded = frames(buf);
+2682        assert_eq!(decoded.len(), 1);
+2683        match decoded.pop().expect("non empty") {
+2684            Frame::PathStatusAvailable(decoded) => assert_eq!(decoded, path_status_available),
+2685            x => panic!("incorrect frame {x:?}"),
+2686        }
+2687    }
+2688
+2689    #[test]
+2690    fn test_path_status_backup_roundtrip() {
+2691        let path_status_backup = PathStatusBackup {
+2692            path_id: PathId(42),
+2693            status_seq_no: VarInt(73),
+2694        };
+2695        let mut buf = Vec::new();
+2696        path_status_backup.encode(&mut buf);
+2697
+2698        let mut decoded = frames(buf);
+2699        assert_eq!(decoded.len(), 1);
+2700        match decoded.pop().expect("non empty") {
+2701            Frame::PathStatusBackup(decoded) => assert_eq!(decoded, path_status_backup),
+2702            x => panic!("incorrect frame {x:?}"),
+2703        }
+2704    }
+2705
+2706    #[test]
+2707    fn test_path_new_connection_id_roundtrip() {
+2708        let cid = NewConnectionId {
+2709            path_id: Some(PathId(22)),
+2710            sequence: 31,
+2711            retire_prior_to: 13,
+2712            id: ConnectionId::new(&[0xAB; 8]),
+2713            reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]),
+2714        };
+2715        let mut buf = Vec::new();
+2716        cid.encode(&mut buf);
+2717
+2718        let mut decoded = frames(buf);
+2719        assert_eq!(decoded.len(), 1);
+2720        match decoded.pop().expect("non empty") {
+2721            Frame::NewConnectionId(decoded) => assert_eq!(decoded, cid),
+2722            x => panic!("incorrect frame {x:?}"),
+2723        }
+2724    }
+2725
+2726    #[test]
+2727    fn test_path_retire_connection_id_roundtrip() {
+2728        let retire_cid = RetireConnectionId {
+2729            path_id: Some(PathId(22)),
+2730            sequence: 31,
+2731        };
+2732        let mut buf = Vec::new();
+2733        retire_cid.encode(&mut buf);
+2734
+2735        let mut decoded = frames(buf);
+2736        assert_eq!(decoded.len(), 1);
+2737        match decoded.pop().expect("non empty") {
+2738            Frame::RetireConnectionId(decoded) => assert_eq!(decoded, retire_cid),
+2739            x => panic!("incorrect frame {x:?}"),
+2740        }
+2741    }
+2742
+2743    #[test]
+2744    fn test_paths_blocked_path_cids_blocked_roundtrip() {
+2745        let mut buf = Vec::new();
+2746
+2747        let frame0 = PathsBlocked(PathId(22));
+2748        frame0.encode(&mut buf);
+2749        let frame1 = PathCidsBlocked {
+2750            path_id: PathId(23),
+2751            next_seq: VarInt(32),
+2752        };
+2753        frame1.encode(&mut buf);
+2754
+2755        let mut decoded = frames(buf);
+2756        assert_eq!(decoded.len(), 2);
+2757        match decoded.pop().expect("non empty") {
+2758            Frame::PathCidsBlocked(decoded) => assert_eq!(decoded, frame1),
+2759            x => panic!("incorrect frame {x:?}"),
+2760        }
+2761        match decoded.pop().expect("non empty") {
+2762            Frame::PathsBlocked(decoded) => assert_eq!(decoded, frame0),
+2763            x => panic!("incorrect frame {x:?}"),
+2764        }
+2765    }
+2766
+2767    /// Test that encoding and decoding [`AddAddress`] produces the same result
+2768    #[test]
+2769    fn test_add_address_roundrip() {
+2770        let add_address = AddAddress {
+2771            seq_no: VarInt(42),
+2772            ip: std::net::Ipv4Addr::LOCALHOST.into(),
+2773            port: 4242,
+2774        };
+2775        let mut buf = Vec::with_capacity(add_address.size());
+2776        add_address.encode(&mut buf);
+2777
+2778        assert_eq!(
+2779            add_address.size(),
+2780            buf.len(),
+2781            "expected written bytes and actual size differ"
+2782        );
+2783
+2784        let mut decoded = frames(buf);
+2785        assert_eq!(decoded.len(), 1);
+2786        match decoded.pop().expect("non empty") {
+2787            Frame::AddAddress(decoded) => assert_eq!(decoded, add_address),
+2788            x => panic!("incorrect frame {x:?}"),
+2789        }
+2790    }
+2791
+2792    /// Test that encoding and decoding [`AddAddress`] produces the same result
+2793    #[test]
+2794    fn test_reach_out_roundrip() {
+2795        let reach_out = ReachOut {
+2796            round: VarInt(42),
+2797            ip: std::net::Ipv6Addr::LOCALHOST.into(),
+2798            port: 4242,
+2799        };
+2800        let mut buf = Vec::with_capacity(reach_out.size());
+2801        reach_out.encode(&mut buf);
+2802
+2803        assert_eq!(
+2804            reach_out.size(),
+2805            buf.len(),
+2806            "expected written bytes and actual size differ"
+2807        );
+2808
+2809        let mut decoded = frames(buf);
+2810        assert_eq!(decoded.len(), 1);
+2811        match decoded.pop().expect("non empty") {
+2812            Frame::ReachOut(decoded) => assert_eq!(decoded, reach_out),
+2813            x => panic!("incorrect frame {x:?}"),
+2814        }
+2815    }
+2816
+2817    /// Test that encoding and decoding [`RemoveAddress`] produces the same result
+2818    #[test]
+2819    fn test_remove_address_roundrip() {
+2820        let remove_addr = RemoveAddress::new(VarInt(10));
+2821        let mut buf = Vec::with_capacity(remove_addr.size());
+2822        remove_addr.encode(&mut buf);
+2823
+2824        assert_eq!(
+2825            remove_addr.size(),
+2826            buf.len(),
+2827            "expected written bytes and actual size differ"
+2828        );
+2829
+2830        let mut decoded = frames(buf);
+2831        assert_eq!(decoded.len(), 1);
+2832        match decoded.pop().expect("non empty") {
+2833            Frame::RemoveAddress(decoded) => assert_eq!(decoded, remove_addr),
+2834            x => panic!("incorrect frame {x:?}"),
+2835        }
+2836    }
+2837}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/iroh_hp.rs.html b/pr/395/docs/src/iroh_quinn_proto/iroh_hp.rs.html new file mode 100644 index 000000000..e1b51b1bc --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/iroh_hp.rs.html @@ -0,0 +1,593 @@ +iroh_hp.rs - source

iroh_quinn_proto/
iroh_hp.rs

1//! iroh NAT Traversal
+2
+3use std::{
+4    collections::hash_map::Entry,
+5    net::{IpAddr, SocketAddr},
+6};
+7
+8use identity_hash::IntMap;
+9use rustc_hash::{FxHashMap, FxHashSet};
+10use tracing::trace;
+11
+12use crate::{
+13    PathId, Side, VarInt,
+14    frame::{AddAddress, ReachOut, RemoveAddress},
+15};
+16
+17type IpPort = (IpAddr, u16);
+18
+19/// Errors that the nat traversal state might encounter.
+20#[derive(Debug, thiserror::Error)]
+21pub enum Error {
+22    /// An endpoint (local or remote) tried to add too many addresses to their advertised set
+23    #[error("Tried to add too many addresses to their advertised set")]
+24    TooManyAddresses,
+25    /// The operation is not allowed for this endpoint's connection side
+26    #[error("Not allowed for this endpoint's connection side")]
+27    WrongConnectionSide,
+28    /// The extension was not negotiated
+29    #[error("Iroh's nat traversal was not negotiated")]
+30    ExtensionNotNegotiated,
+31    /// Not enough addresses to complete the operation
+32    #[error("Not enough addresses")]
+33    NotEnoughAddresses,
+34    /// Nat traversal attempt failed due to a multipath error
+35    #[error("Failed to establish paths {0}")]
+36    Multipath(super::PathError),
+37    /// Attempted to initiate NAT traversal on a closed, or closing connection.
+38    #[error("The connection is already closed")]
+39    Closed,
+40}
+41
+42pub(crate) struct NatTraversalRound {
+43    /// Sequence number to use for the new reach out frames.
+44    pub(crate) new_round: VarInt,
+45    /// Addresses to use to send reach out frames.
+46    pub(crate) reach_out_at: Vec<IpPort>,
+47    /// Remotes to probe by attempting to open new paths.
+48    ///
+49    /// The addresses include their Id, so that it can be used to signal these should be returned
+50    /// in a nat traversal continuation by calling [`ClientState::report_in_continuation`].
+51    ///
+52    /// These are filtered and mapped to the IP family the local socket supports.
+53    pub(crate) addresses_to_probe: Vec<(VarInt, IpPort)>,
+54    /// [`PathId`]s of the cancelled round.
+55    pub(crate) prev_round_path_ids: Vec<PathId>,
+56}
+57
+58/// Event emitted when the client receives ADD_ADDRESS or REMOVE_ADDRESS frames.
+59#[derive(Debug, Clone)]
+60pub enum Event {
+61    /// An ADD_ADDRESS frame was received.
+62    AddressAdded(SocketAddr),
+63    /// A REMOVE_ADDRESS frame was received.
+64    AddressRemoved(SocketAddr),
+65}
+66
+67/// State kept for Iroh's nat traversal
+68#[derive(Debug, Default)]
+69pub(crate) enum State {
+70    #[default]
+71    NotNegotiated,
+72    ClientSide(ClientState),
+73    ServerSide(ServerState),
+74}
+75
+76#[derive(Debug)]
+77pub(crate) struct ClientState {
+78    /// Max number of remote addresses we allow
+79    ///
+80    /// This is set by the local endpoint.
+81    max_remote_addresses: usize,
+82    /// Max number of local addresses allowed
+83    ///
+84    /// This is set by the remote endpoint.
+85    max_local_addresses: usize,
+86    /// Candidate addresses the remote server reports as potentially reachable, to use for nat
+87    /// traversal attempts.
+88    ///
+89    /// These are indexed by their advertised Id. For each address, whether the address should be
+90    /// reported in nat traversal continuations is kept.
+91    remote_addresses: FxHashMap<VarInt, (IpPort, bool)>,
+92    /// Candidate addresses the local client reports as potentially reachable, to use for nat
+93    /// traversal attempts.
+94    local_addresses: FxHashSet<IpPort>,
+95    /// Current nat holepunching round.
+96    round: VarInt,
+97    /// [`PathId`]s used to probe remotes assigned to this round.
+98    round_path_ids: Vec<PathId>,
+99}
+100
+101impl ClientState {
+102    fn new(max_remote_addresses: usize, max_local_addresses: usize) -> Self {
+103        Self {
+104            max_remote_addresses,
+105            max_local_addresses,
+106            remote_addresses: Default::default(),
+107            local_addresses: Default::default(),
+108            round: Default::default(),
+109            round_path_ids: Default::default(),
+110        }
+111    }
+112
+113    fn add_local_address(&mut self, address: IpPort) -> Result<(), Error> {
+114        if self.local_addresses.len() < self.max_local_addresses {
+115            self.local_addresses.insert(address);
+116            Ok(())
+117        } else if self.local_addresses.contains(&address) {
+118            // at capacity, but the address is known, no issues here
+119            Ok(())
+120        } else {
+121            // at capacity and the address is new
+122            Err(Error::TooManyAddresses)
+123        }
+124    }
+125
+126    fn remove_local_address(&mut self, address: &IpPort) {
+127        self.local_addresses.remove(address);
+128    }
+129
+130    /// Initiates a new nat traversal round.
+131    ///
+132    /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT`
+133    /// frames, and initiating probing of the known remote addresses. When a new round is
+134    /// initiated, the previous one is cancelled, and paths that have not been opened should be
+135    /// closed.
+136    ///
+137    /// `ipv6` indicates if the connection runs on a socket that supports IPv6. If so, then all
+138    /// addresses returned in [`NatTraversalRound`] will be IPv6 addresses (and IPv4-mapped IPv6
+139    /// addresses if necessary). Otherwise they're all IPv4 addresses.
+140    /// See also [`map_to_local_socket_family`].
+141    pub(crate) fn initiate_nat_traversal_round(
+142        &mut self,
+143        ipv6: bool,
+144    ) -> Result<NatTraversalRound, Error> {
+145        if self.local_addresses.is_empty() {
+146            return Err(Error::NotEnoughAddresses);
+147        }
+148
+149        let prev_round_path_ids = std::mem::take(&mut self.round_path_ids);
+150        self.round = self.round.saturating_add(1u8);
+151        let mut addresses_to_probe = Vec::with_capacity(self.remote_addresses.len());
+152        for (id, ((ip, port), report_in_continuation)) in self.remote_addresses.iter_mut() {
+153            *report_in_continuation = false;
+154
+155            if let Some(ip) = map_to_local_socket_family(*ip, ipv6) {
+156                addresses_to_probe.push((*id, (ip, *port)));
+157            } else {
+158                trace!(?ip, "not using IPv6 nat candidate for IPv4 socket");
+159            }
+160        }
+161
+162        Ok(NatTraversalRound {
+163            new_round: self.round,
+164            reach_out_at: self.local_addresses.iter().copied().collect(),
+165            addresses_to_probe,
+166            prev_round_path_ids,
+167        })
+168    }
+169
+170    /// Mark a remote address to be reported back in a nat traversal continuation if the error is
+171    /// considered spurious from a nat traversal point of view.
+172    ///
+173    /// Ids not present are silently ignored.
+174    pub(crate) fn report_in_continuation(&mut self, id: VarInt, e: crate::PathError) {
+175        match e {
+176            crate::PathError::MaxPathIdReached | crate::PathError::RemoteCidsExhausted => {
+177                if let Some((_address, report_in_continuation)) = self.remote_addresses.get_mut(&id)
+178                {
+179                    *report_in_continuation = true;
+180                }
+181            }
+182            _ => {}
+183        }
+184    }
+185
+186    /// Returns an address that needs to be probed, if any.
+187    ///
+188    /// The address will not be returned twice unless marked as such again with
+189    /// [`Self::report_in_continuation`].
+190    ///
+191    /// `ipv6` indicates if the connection runs on a socket that supports IPv6. If so, then all
+192    /// addresses returned in [`NatTraversalRound`] will be IPv6 addresses (and IPv4-mapped IPv6
+193    /// addresses if necessary). Otherwise they're all IPv4 addresses.
+194    /// See also [`map_to_local_socket_family`].
+195    pub(crate) fn continue_nat_traversal_round(&mut self, ipv6: bool) -> Option<(VarInt, IpPort)> {
+196        // this being random depends on iteration not returning always on the same order
+197        let (id, (address, report_in_continuation)) = self
+198            .remote_addresses
+199            .iter_mut()
+200            .filter(|(_id, (_addr, report))| *report)
+201            .filter_map(|(id, ((ip, port), report))| {
+202                // only continue with addresses we can send on our local socket
+203                let Some(ip) = map_to_local_socket_family(*ip, ipv6) else {
+204                    trace!(?ip, "not using IPv6 nat candidate for IPv4 socket");
+205                    return None;
+206                };
+207                Some((*id, ((ip, *port), report)))
+208            })
+209            .next()?;
+210        *report_in_continuation = false;
+211        Some((id, address))
+212    }
+213
+214    /// Add a [`PathId`] as part of the current attempts to create paths based on the server's
+215    /// advertised addresses.
+216    pub(crate) fn set_round_path_ids(&mut self, path_ids: Vec<PathId>) {
+217        self.round_path_ids = path_ids;
+218    }
+219
+220    /// Add a [`PathId`] as part of the current attempts to create paths based on the server's
+221    /// advertised addresses.
+222    pub(crate) fn add_round_path_id(&mut self, path_id: PathId) {
+223        self.round_path_ids.push(path_id);
+224    }
+225
+226    /// Adds an address to the remote set
+227    ///
+228    /// On success returns the address if it was new to the set. It will error when the set has no
+229    /// capacity for the address.
+230    pub(crate) fn add_remote_address(
+231        &mut self,
+232        add_addr: AddAddress,
+233    ) -> Result<Option<SocketAddr>, Error> {
+234        let AddAddress { seq_no, ip, port } = add_addr;
+235        let address = (ip, port);
+236        let allow_new = self.remote_addresses.len() < self.max_remote_addresses;
+237        match self.remote_addresses.entry(seq_no) {
+238            Entry::Occupied(mut occupied_entry) => {
+239                let is_update = occupied_entry.get().0 != address;
+240                if is_update {
+241                    occupied_entry.insert((address, false));
+242                }
+243                // The value might be different. This should not happen, but we assume that the new
+244                // address is more recent than the previous, and thus worth updating
+245                Ok(is_update.then_some(address.into()))
+246            }
+247            Entry::Vacant(vacant_entry) if allow_new => {
+248                vacant_entry.insert((address, false));
+249                Ok(Some(address.into()))
+250            }
+251            _ => Err(Error::TooManyAddresses),
+252        }
+253    }
+254
+255    /// Removes an address from the remote set
+256    ///
+257    /// Returns whether the address was present.
+258    pub(crate) fn remove_remote_address(
+259        &mut self,
+260        remove_addr: RemoveAddress,
+261    ) -> Option<SocketAddr> {
+262        self.remote_addresses
+263            .remove(&remove_addr.seq_no)
+264            .map(|(address, _report_in_continuation)| address.into())
+265    }
+266
+267    /// Checks that a received remote address is valid
+268    ///
+269    /// An address is valid as long as it does not change the value of a known address id.
+270    pub(crate) fn check_remote_address(&self, add_addr: &AddAddress) -> bool {
+271        match self.remote_addresses.get(&add_addr.seq_no) {
+272            None => true,
+273            Some((existing, _)) => existing == &add_addr.ip_port(),
+274        }
+275    }
+276
+277    pub(crate) fn get_remote_nat_traversal_addresses(&self) -> Vec<SocketAddr> {
+278        self.remote_addresses
+279            .values()
+280            .map(|(address, _report_in_continuation)| (*address).into())
+281            .collect()
+282    }
+283}
+284
+285#[derive(Debug)]
+286pub(crate) struct ServerState {
+287    /// Max number of remote addresses we allow.
+288    ///
+289    /// This is set by the local endpoint.
+290    max_remote_addresses: usize,
+291    /// Max number of local addresses allowed.
+292    ///
+293    /// This is set by the remote endpoint.
+294    max_local_addresses: usize,
+295    /// Candidate addresses the server reports as potentially reachable, to use for nat
+296    /// traversal attempts.
+297    local_addresses: FxHashMap<IpPort, VarInt>,
+298    /// The next id to use for local addresses sent to the client.
+299    next_local_addr_id: VarInt,
+300    /// Current nat holepunching round
+301    ///
+302    /// Servers keep track of the client's most recent round and cancel probing related to previous
+303    /// rounds.
+304    round: VarInt,
+305    /// Addresses to which PATH_CHALLENGES need to be sent.
+306    pending_probes: FxHashSet<IpPort>,
+307    /// Sent PATH_CHALLENGES for this round.
+308    ///
+309    /// This is used to validate the remotes assigned to each token.
+310    active_probes: IntMap<u64, IpPort>,
+311}
+312
+313impl ServerState {
+314    fn new(max_remote_addresses: usize, max_local_addresses: usize) -> Self {
+315        Self {
+316            max_remote_addresses,
+317            max_local_addresses,
+318            local_addresses: Default::default(),
+319            next_local_addr_id: Default::default(),
+320            round: Default::default(),
+321            pending_probes: Default::default(),
+322            active_probes: Default::default(),
+323        }
+324    }
+325
+326    fn add_local_address(&mut self, address: IpPort) -> Result<Option<AddAddress>, Error> {
+327        let allow_new = self.local_addresses.len() < self.max_local_addresses;
+328        match self.local_addresses.entry(address) {
+329            Entry::Occupied(_) => Ok(None),
+330            Entry::Vacant(vacant_entry) if allow_new => {
+331                let id = self.next_local_addr_id;
+332                self.next_local_addr_id = self.next_local_addr_id.saturating_add(1u8);
+333                vacant_entry.insert(id);
+334                Ok(Some(AddAddress::new(address, id)))
+335            }
+336            _ => Err(Error::TooManyAddresses),
+337        }
+338    }
+339
+340    fn remove_local_address(&mut self, address: &IpPort) -> Option<RemoveAddress> {
+341        self.local_addresses.remove(address).map(RemoveAddress::new)
+342    }
+343
+344    /// Handles a received [`ReachOut`].
+345    ///
+346    /// This might ignore the reach out frame if it belongs to an older round or if
+347    /// the reach out can't be handled by an ipv4-only local socket.
+348    pub(crate) fn handle_reach_out(
+349        &mut self,
+350        reach_out: ReachOut,
+351        ipv6: bool,
+352    ) -> Result<(), Error> {
+353        let ReachOut { round, ip, port } = reach_out;
+354
+355        if round < self.round {
+356            trace!(current_round=%self.round, "ignoring REACH_OUT for previous round");
+357            return Ok(());
+358        }
+359        let Some(ip) = map_to_local_socket_family(ip, ipv6) else {
+360            trace!("Ignoring IPv6 REACH_OUT frame due to not supporting IPv6 locally");
+361            return Ok(());
+362        };
+363
+364        if round > self.round {
+365            self.round = round;
+366            self.pending_probes.clear();
+367            // TODO(@divma): This log is here because I'm not sure if dropping the challenges
+368            // without further interaction with the connection is going to cause issues.
+369            for (token, remote) in self.active_probes.drain() {
+370                let remote: SocketAddr = remote.into();
+371                trace!(token=format!("{:08x}", token), %remote, "dropping nat traversal challenge pending response");
+372            }
+373        } else if self.pending_probes.len() >= self.max_remote_addresses {
+374            return Err(Error::TooManyAddresses);
+375        }
+376        self.pending_probes.insert((ip, port));
+377        Ok(())
+378    }
+379
+380    pub(crate) fn next_probe(&mut self) -> Option<ServerProbing<'_>> {
+381        self.pending_probes
+382            .iter()
+383            .next()
+384            .copied()
+385            .map(|remote| ServerProbing {
+386                remote,
+387                pending_probes: &mut self.pending_probes,
+388                active_probes: &mut self.active_probes,
+389            })
+390    }
+391}
+392
+393pub(crate) struct ServerProbing<'a> {
+394    remote: IpPort,
+395    pending_probes: &'a mut FxHashSet<IpPort>,
+396    active_probes: &'a mut IntMap<u64, IpPort>,
+397}
+398
+399impl<'a> ServerProbing<'a> {
+400    pub(crate) fn finish(self, token: u64) {
+401        self.pending_probes.remove(&self.remote);
+402        self.active_probes.insert(token, self.remote);
+403    }
+404
+405    pub(crate) fn remote(&self) -> SocketAddr {
+406        self.remote.into()
+407    }
+408}
+409
+410impl State {
+411    pub(crate) fn new(max_remote_addresses: u8, max_local_addresses: u8, side: Side) -> Self {
+412        match side {
+413            Side::Client => Self::ClientSide(ClientState::new(
+414                max_remote_addresses.into(),
+415                max_local_addresses.into(),
+416            )),
+417            Side::Server => Self::ServerSide(ServerState::new(
+418                max_remote_addresses.into(),
+419                max_local_addresses.into(),
+420            )),
+421        }
+422    }
+423
+424    pub(crate) fn client_side(&self) -> Result<&ClientState, Error> {
+425        match self {
+426            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+427            Self::ClientSide(client_side) => Ok(client_side),
+428            Self::ServerSide(_) => Err(Error::WrongConnectionSide),
+429        }
+430    }
+431
+432    pub(crate) fn client_side_mut(&mut self) -> Result<&mut ClientState, Error> {
+433        match self {
+434            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+435            Self::ClientSide(client_side) => Ok(client_side),
+436            Self::ServerSide(_) => Err(Error::WrongConnectionSide),
+437        }
+438    }
+439
+440    pub(crate) fn server_side_mut(&mut self) -> Result<&mut ServerState, Error> {
+441        match self {
+442            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+443            Self::ClientSide(_) => Err(Error::WrongConnectionSide),
+444            Self::ServerSide(server_side) => Ok(server_side),
+445        }
+446    }
+447
+448    /// Adds a local address to use for nat traversal.
+449    ///
+450    /// When this endpoint is the server within the connection, these addresses will be sent to the
+451    /// client in add address frames. For clients, these addresses will be sent in reach out frames
+452    /// when nat traversal attempts are initiated.
+453    ///
+454    /// If a frame should be sent, it is returned.
+455    pub(crate) fn add_local_address(
+456        &mut self,
+457        address: SocketAddr,
+458    ) -> Result<Option<AddAddress>, Error> {
+459        let ip_port = IpPort::from((address.ip(), address.port()));
+460        match self {
+461            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+462            Self::ClientSide(client_state) => {
+463                client_state.add_local_address(ip_port)?;
+464                Ok(None)
+465            }
+466            Self::ServerSide(server_state) => server_state.add_local_address(ip_port),
+467        }
+468    }
+469
+470    /// Removes a local address from the advertised set for nat traversal.
+471    ///
+472    /// When this endpoint is the server, removed addresses must be reported with remove address
+473    /// frames. Clients will simply stop reporting these addresses in reach out frames.
+474    ///
+475    /// If a frame should be sent, it is returned.
+476    pub(crate) fn remove_local_address(
+477        &mut self,
+478        address: SocketAddr,
+479    ) -> Result<Option<RemoveAddress>, Error> {
+480        let address = IpPort::from((address.ip(), address.port()));
+481        match self {
+482            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+483            Self::ClientSide(client_state) => {
+484                client_state.remove_local_address(&address);
+485                Ok(None)
+486            }
+487            Self::ServerSide(server_state) => Ok(server_state.remove_local_address(&address)),
+488        }
+489    }
+490
+491    pub(crate) fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, Error> {
+492        match self {
+493            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+494            Self::ClientSide(client_state) => Ok(client_state
+495                .local_addresses
+496                .iter()
+497                .copied()
+498                .map(Into::into)
+499                .collect()),
+500            Self::ServerSide(server_state) => Ok(server_state
+501                .local_addresses
+502                .keys()
+503                .copied()
+504                .map(Into::into)
+505                .collect()),
+506        }
+507    }
+508}
+509
+510/// Returns the given address as canonicalized IP address.
+511///
+512/// This checks that the address family is supported by our local socket.
+513/// If it is supported, then the address is mapped to the respective IP address.
+514/// If the given address is an IPv6 address, but our local socket doesn't support
+515/// IPv6, then this returns `None`.
+516pub(crate) fn map_to_local_socket_family(address: IpAddr, ipv6: bool) -> Option<IpAddr> {
+517    let ip = match address {
+518        IpAddr::V4(addr) if ipv6 => IpAddr::V6(addr.to_ipv6_mapped()),
+519        IpAddr::V4(_) => address,
+520        IpAddr::V6(_) if ipv6 => address,
+521        IpAddr::V6(addr) => IpAddr::V4(addr.to_ipv4_mapped()?),
+522    };
+523    Some(ip)
+524}
+525
+526#[cfg(test)]
+527mod tests {
+528    use super::*;
+529
+530    #[test]
+531    fn test_basic_server_state() {
+532        let mut state = ServerState::new(2, 2);
+533
+534        state
+535            .handle_reach_out(
+536                ReachOut {
+537                    round: 1u32.into(),
+538                    ip: std::net::Ipv4Addr::LOCALHOST.into(),
+539                    port: 1,
+540                },
+541                true,
+542            )
+543            .unwrap();
+544
+545        state
+546            .handle_reach_out(
+547                ReachOut {
+548                    round: 1u32.into(),
+549                    ip: "1.1.1.1".parse().unwrap(), //std::net::Ipv4Addr::LOCALHOST.into(),
+550                    port: 2,
+551                },
+552                true,
+553            )
+554            .unwrap();
+555
+556        dbg!(&state);
+557        assert_eq!(state.pending_probes.len(), 2);
+558        assert_eq!(state.active_probes.len(), 0);
+559
+560        let probe = state.next_probe().unwrap();
+561        probe.finish(1);
+562        let probe = state.next_probe().unwrap();
+563        probe.finish(2);
+564
+565        assert!(state.next_probe().is_none());
+566        assert_eq!(state.pending_probes.len(), 0);
+567        assert_eq!(state.active_probes.len(), 2);
+568    }
+569
+570    #[test]
+571    fn test_map_to_local_socket() {
+572        assert_eq!(
+573            map_to_local_socket_family("1.1.1.1".parse().unwrap(), false),
+574            Some("1.1.1.1".parse().unwrap())
+575        );
+576        assert_eq!(
+577            map_to_local_socket_family("1.1.1.1".parse().unwrap(), true),
+578            Some("::ffff:1.1.1.1".parse().unwrap())
+579        );
+580        assert_eq!(
+581            map_to_local_socket_family("::1".parse().unwrap(), true),
+582            Some("::1".parse().unwrap())
+583        );
+584        assert_eq!(
+585            map_to_local_socket_family("::1".parse().unwrap(), false),
+586            None
+587        );
+588        assert_eq!(
+589            map_to_local_socket_family("::ffff:1.1.1.1".parse().unwrap(), false),
+590            Some("1.1.1.1".parse().unwrap())
+591        )
+592    }
+593}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/lib.rs.html b/pr/395/docs/src/iroh_quinn_proto/lib.rs.html new file mode 100644 index 000000000..dc09141bc --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/lib.rs.html @@ -0,0 +1,430 @@ +lib.rs - source

iroh_quinn_proto/
lib.rs

1//! Low-level protocol logic for the QUIC protoocol
+2//!
+3//! quinn-proto contains a fully deterministic implementation of QUIC protocol logic. It contains
+4//! no networking code and does not get any relevant timestamps from the operating system. Most
+5//! users may want to use the futures-based quinn API instead.
+6//!
+7//! The quinn-proto API might be of interest if you want to use it from a C or C++ project
+8//! through C bindings or if you want to use a different event loop than the one tokio provides.
+9//!
+10//! The most important types are `Endpoint`, which conceptually represents the protocol state for
+11//! a single socket and mostly manages configuration and dispatches incoming datagrams to the
+12//! related `Connection`. `Connection` types contain the bulk of the protocol logic related to
+13//! managing a single connection and all the related state (such as streams).
+14
+15#![cfg_attr(not(fuzzing), warn(missing_docs))]
+16#![cfg_attr(test, allow(dead_code))]
+17// Fixes welcome:
+18#![allow(clippy::too_many_arguments)]
+19#![warn(unreachable_pub)]
+20#![warn(clippy::use_self)]
+21
+22use std::{
+23    fmt,
+24    net::{IpAddr, SocketAddr},
+25    ops,
+26};
+27
+28mod cid_queue;
+29pub mod coding;
+30mod constant_time;
+31mod range_set;
+32#[cfg(all(test, any(feature = "rustls-aws-lc-rs", feature = "rustls-ring")))]
+33mod tests;
+34pub mod transport_parameters;
+35mod varint;
+36
+37pub use varint::{VarInt, VarIntBoundsExceeded};
+38
+39#[cfg(feature = "bloom")]
+40mod bloom_token_log;
+41#[cfg(feature = "bloom")]
+42pub use bloom_token_log::BloomTokenLog;
+43
+44pub(crate) mod connection;
+45pub use crate::connection::{
+46    Chunk, Chunks, ClosePathError, ClosedPath, ClosedStream, Connection, ConnectionError,
+47    ConnectionStats, Datagrams, Event, FinishError, FrameStats, MultipathNotNegotiated, PathError,
+48    PathEvent, PathId, PathStats, PathStatus, ReadError, ReadableError, RecvStream, RttEstimator,
+49    SendDatagramError, SendStream, SetPathStatusError, ShouldTransmit, StreamEvent, Streams,
+50    UdpStats, WriteError, Written,
+51};
+52#[cfg(test)]
+53use test_strategy::Arbitrary;
+54
+55#[cfg(feature = "rustls")]
+56pub use rustls;
+57
+58mod config;
+59#[cfg(doc)]
+60pub use config::DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED;
+61pub use config::{
+62    AckFrequencyConfig, ClientConfig, ConfigError, EndpointConfig, IdleTimeout, MtuDiscoveryConfig,
+63    ServerConfig, StdSystemTime, TimeSource, TransportConfig, ValidationTokenConfig,
+64};
+65#[cfg(feature = "qlog")]
+66pub use config::{QlogConfig, QlogFactory, QlogFileFactory};
+67
+68pub mod crypto;
+69
+70mod frame;
+71pub use crate::frame::{
+72    ApplicationClose, ConnectionClose, Datagram, DatagramInfo, FrameType, InvalidFrameId,
+73    MaybeFrame, StreamInfo,
+74};
+75use crate::{
+76    coding::{Decodable, Encodable},
+77    frame::Frame,
+78};
+79
+80mod endpoint;
+81pub use crate::endpoint::{
+82    AcceptError, ConnectError, ConnectionHandle, DatagramEvent, Endpoint, Incoming, RetryError,
+83};
+84
+85mod packet;
+86pub use packet::{
+87    ConnectionIdParser, FixedLengthConnectionIdParser, LongType, PacketDecodeError, PartialDecode,
+88    ProtectedHeader, ProtectedInitialHeader,
+89};
+90
+91mod shared;
+92pub use crate::shared::{ConnectionEvent, ConnectionId, EcnCodepoint, EndpointEvent};
+93
+94mod transport_error;
+95pub use crate::transport_error::{Code as TransportErrorCode, Error as TransportError};
+96
+97pub mod congestion;
+98
+99mod cid_generator;
+100pub use crate::cid_generator::{
+101    ConnectionIdGenerator, HashedConnectionIdGenerator, InvalidCid, RandomConnectionIdGenerator,
+102};
+103
+104mod token;
+105use token::ResetToken;
+106pub use token::{NoneTokenLog, NoneTokenStore, TokenLog, TokenReuseError, TokenStore};
+107
+108mod address_discovery;
+109
+110mod token_memory_cache;
+111pub use token_memory_cache::TokenMemoryCache;
+112
+113pub mod iroh_hp;
+114
+115// Deal with time
+116#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
+117pub(crate) use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
+118#[cfg(all(target_family = "wasm", target_os = "unknown"))]
+119pub(crate) use web_time::{Duration, Instant, SystemTime, UNIX_EPOCH};
+120
+121#[cfg(feature = "bench")]
+122pub mod bench_exports {
+123    //! Exports for benchmarks
+124    pub use crate::connection::send_buffer::send_buffer_benches;
+125}
+126
+127#[cfg(fuzzing)]
+128pub mod fuzzing {
+129    pub use crate::connection::{Retransmits, State as ConnectionState, StreamsState};
+130    pub use crate::frame::ResetStream;
+131    pub use crate::packet::PartialDecode;
+132    pub use crate::transport_parameters::TransportParameters;
+133    pub use bytes::{BufMut, BytesMut};
+134
+135    #[cfg(feature = "arbitrary")]
+136    use arbitrary::{Arbitrary, Result, Unstructured};
+137
+138    #[cfg(feature = "arbitrary")]
+139    impl<'arbitrary> Arbitrary<'arbitrary> for TransportParameters {
+140        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
+141            Ok(Self {
+142                initial_max_streams_bidi: u.arbitrary()?,
+143                initial_max_streams_uni: u.arbitrary()?,
+144                ack_delay_exponent: u.arbitrary()?,
+145                max_udp_payload_size: u.arbitrary()?,
+146                ..Self::default()
+147            })
+148        }
+149    }
+150
+151    #[derive(Debug)]
+152    pub struct PacketParams {
+153        pub local_cid_len: usize,
+154        pub buf: BytesMut,
+155        pub grease_quic_bit: bool,
+156    }
+157
+158    #[cfg(feature = "arbitrary")]
+159    impl<'arbitrary> Arbitrary<'arbitrary> for PacketParams {
+160        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
+161            let local_cid_len: usize = u.int_in_range(0..=crate::MAX_CID_SIZE)?;
+162            let bytes: Vec<u8> = Vec::arbitrary(u)?;
+163            let mut buf = BytesMut::new();
+164            buf.put_slice(&bytes[..]);
+165            Ok(Self {
+166                local_cid_len,
+167                buf,
+168                grease_quic_bit: bool::arbitrary(u)?,
+169            })
+170        }
+171    }
+172}
+173
+174/// The QUIC protocol version implemented.
+175pub const DEFAULT_SUPPORTED_VERSIONS: &[u32] = &[
+176    0x00000001,
+177    0xff00_001d,
+178    0xff00_001e,
+179    0xff00_001f,
+180    0xff00_0020,
+181    0xff00_0021,
+182    0xff00_0022,
+183];
+184
+185/// Whether an endpoint was the initiator of a connection
+186#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+187#[cfg_attr(test, derive(Arbitrary))]
+188#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+189pub enum Side {
+190    /// The initiator of a connection
+191    Client = 0,
+192    /// The acceptor of a connection
+193    Server = 1,
+194}
+195
+196impl Side {
+197    #[inline]
+198    /// Shorthand for `self == Side::Client`
+199    pub fn is_client(self) -> bool {
+200        self == Self::Client
+201    }
+202
+203    #[inline]
+204    /// Shorthand for `self == Side::Server`
+205    pub fn is_server(self) -> bool {
+206        self == Self::Server
+207    }
+208}
+209
+210impl ops::Not for Side {
+211    type Output = Self;
+212    fn not(self) -> Self {
+213        match self {
+214            Self::Client => Self::Server,
+215            Self::Server => Self::Client,
+216        }
+217    }
+218}
+219
+220/// Whether a stream communicates data in both directions or only from the initiator
+221#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
+222#[cfg_attr(test, derive(Arbitrary))]
+223#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+224pub enum Dir {
+225    /// Data flows in both directions
+226    Bi = 0,
+227    /// Data flows only from the stream's initiator
+228    Uni = 1,
+229}
+230
+231impl Dir {
+232    fn iter() -> impl Iterator<Item = Self> {
+233        [Self::Bi, Self::Uni].iter().cloned()
+234    }
+235}
+236
+237impl fmt::Display for Dir {
+238    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+239        use Dir::*;
+240        f.pad(match *self {
+241            Bi => "bidirectional",
+242            Uni => "unidirectional",
+243        })
+244    }
+245}
+246
+247/// Identifier for a stream within a particular connection
+248#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+249#[cfg_attr(test, derive(Arbitrary))]
+250pub struct StreamId(#[cfg_attr(test, strategy(crate::varint::varint_u64()))] u64);
+251
+252impl fmt::Display for StreamId {
+253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+254        let initiator = match self.initiator() {
+255            Side::Client => "client",
+256            Side::Server => "server",
+257        };
+258        let dir = match self.dir() {
+259            Dir::Uni => "uni",
+260            Dir::Bi => "bi",
+261        };
+262        write!(
+263            f,
+264            "{} {}directional stream {}",
+265            initiator,
+266            dir,
+267            self.index()
+268        )
+269    }
+270}
+271
+272impl StreamId {
+273    /// Create a new StreamId
+274    pub fn new(initiator: Side, dir: Dir, index: u64) -> Self {
+275        Self((index << 2) | ((dir as u64) << 1) | initiator as u64)
+276    }
+277    /// Which side of a connection initiated the stream
+278    pub fn initiator(self) -> Side {
+279        if self.0 & 0x1 == 0 {
+280            Side::Client
+281        } else {
+282            Side::Server
+283        }
+284    }
+285    /// Which directions data flows in
+286    pub fn dir(self) -> Dir {
+287        if self.0 & 0x2 == 0 { Dir::Bi } else { Dir::Uni }
+288    }
+289    /// Distinguishes streams of the same initiator and directionality
+290    pub fn index(self) -> u64 {
+291        self.0 >> 2
+292    }
+293}
+294
+295impl From<StreamId> for VarInt {
+296    fn from(x: StreamId) -> Self {
+297        unsafe { Self::from_u64_unchecked(x.0) }
+298    }
+299}
+300
+301impl From<VarInt> for StreamId {
+302    fn from(v: VarInt) -> Self {
+303        Self(v.0)
+304    }
+305}
+306
+307impl From<StreamId> for u64 {
+308    fn from(x: StreamId) -> Self {
+309        x.0
+310    }
+311}
+312
+313impl Decodable for StreamId {
+314    fn decode<B: bytes::Buf>(buf: &mut B) -> coding::Result<Self> {
+315        VarInt::decode(buf).map(|x| Self(x.into_inner()))
+316    }
+317}
+318
+319impl Encodable for StreamId {
+320    fn encode<B: bytes::BufMut>(&self, buf: &mut B) {
+321        VarInt::from_u64(self.0).unwrap().encode(buf);
+322    }
+323}
+324
+325#[cfg(feature = "arbitrary")]
+326impl<'arbitrary> arbitrary::Arbitrary<'arbitrary> for StreamId {
+327    fn arbitrary(u: &mut arbitrary::Unstructured<'arbitrary>) -> arbitrary::Result<Self> {
+328        Ok(VarInt::arbitrary(u)?.into())
+329    }
+330}
+331
+332/// An outgoing packet
+333#[derive(Debug)]
+334#[must_use]
+335pub struct Transmit {
+336    /// The socket this datagram should be sent to
+337    pub destination: SocketAddr,
+338    /// Explicit congestion notification bits to set on the packet
+339    pub ecn: Option<EcnCodepoint>,
+340    /// Amount of data written to the caller-supplied buffer
+341    pub size: usize,
+342    /// The segment size if this transmission contains multiple datagrams.
+343    /// This is `None` if the transmit only contains a single datagram
+344    pub segment_size: Option<usize>,
+345    /// Optional source IP address for the datagram
+346    pub src_ip: Option<IpAddr>,
+347}
+348
+349//
+350// Useful internal constants
+351//
+352
+353/// The maximum number of CIDs we bother to issue per path
+354const LOCAL_CID_COUNT: u64 = 12;
+355const RESET_TOKEN_SIZE: usize = 16;
+356const MAX_CID_SIZE: usize = 20;
+357const MIN_INITIAL_SIZE: u16 = 1200;
+358/// <https://www.rfc-editor.org/rfc/rfc9000.html#name-datagram-size>
+359const INITIAL_MTU: u16 = 1200;
+360const MAX_UDP_PAYLOAD: u16 = 65527;
+361const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
+362/// Maximum number of streams that can be uniquely identified by a stream ID
+363const MAX_STREAM_COUNT: u64 = 1 << 60;
+364
+365/// Identifies a network path by the combination of remote and local addresses
+366///
+367/// Including the local ensures good behavior when the host has multiple IP addresses on the same
+368/// subnet and zero-length connection IDs are in use or when multipath is enabled and multiple
+369/// paths exist with the same remote, but different local IP interfaces.
+370#[derive(Hash, Eq, PartialEq, Copy, Clone)]
+371pub struct FourTuple {
+372    /// The remote side of this tuple
+373    pub remote: SocketAddr,
+374    /// The local side of this tuple.
+375    ///
+376    /// The socket is irrelevant for our intents and purposes:
+377    /// When we send, we can only specify the `src_ip`, not the source port.
+378    /// So even if we track the port, we won't be able to make use of it.
+379    pub local_ip: Option<IpAddr>,
+380}
+381
+382impl FourTuple {
+383    /// Returns whether we think the other address probably represents the same path
+384    /// as ours.
+385    ///
+386    /// If we have a local IP set, then we're exact and only match if the 4-tuples are
+387    /// exactly equal.
+388    /// If we don't have a local IP set, then we only check the remote addresses for equality.
+389    pub fn is_probably_same_path(&self, other: &Self) -> bool {
+390        self.remote == other.remote && (self.local_ip.is_none() || self.local_ip == other.local_ip)
+391    }
+392
+393    /// Updates this tuple's local address iff
+394    /// - it was unset before,
+395    /// - the other tuple has the same remote, and
+396    /// - the other tuple has a local address set.
+397    ///
+398    /// Returns whether this and the other remote are now fully equal.
+399    pub fn update_local_if_same_remote(&mut self, other: &Self) -> bool {
+400        if self.remote != other.remote {
+401            return false;
+402        }
+403        if self.local_ip.is_some() && self.local_ip != other.local_ip {
+404            return false;
+405        }
+406        self.local_ip = other.local_ip;
+407        true
+408    }
+409}
+410
+411impl fmt::Display for FourTuple {
+412    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+413        f.write_str("(local: ")?;
+414        if let Some(local_ip) = &self.local_ip {
+415            local_ip.fmt(f)?;
+416            f.write_str(", ")?;
+417        } else {
+418            f.write_str("<unspecified>, ")?;
+419        }
+420        f.write_str("remote: ")?;
+421        self.remote.fmt(f)?;
+422        f.write_str(")")
+423    }
+424}
+425
+426impl fmt::Debug for FourTuple {
+427    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+428        fmt::Display::fmt(&self, f)
+429    }
+430}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/packet.rs.html b/pr/395/docs/src/iroh_quinn_proto/packet.rs.html new file mode 100644 index 000000000..d3966a99c --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/packet.rs.html @@ -0,0 +1,1079 @@ +packet.rs - source

iroh_quinn_proto/
packet.rs

1use std::{cmp::Ordering, io, ops::Range, str};
+2
+3use bytes::{Buf, BufMut, Bytes, BytesMut};
+4use thiserror::Error;
+5
+6use crate::{
+7    ConnectionId, PathId,
+8    coding::{self, BufExt, BufMutExt},
+9    crypto,
+10};
+11
+12/// Decodes a QUIC packet's invariant header
+13///
+14/// Due to packet number encryption, it is impossible to fully decode a header
+15/// (which includes a variable-length packet number) without crypto context.
+16/// The crypto context (represented by the `Crypto` type in Quinn) is usually
+17/// part of the `Connection`, or can be derived from the destination CID for
+18/// Initial packets.
+19///
+20/// To cope with this, we decode the invariant header (which should be stable
+21/// across QUIC versions), which gives us the destination CID and allows us
+22/// to inspect the version and packet type (which depends on the version).
+23/// This information allows us to fully decode and decrypt the packet.
+24#[cfg_attr(test, derive(Clone))]
+25#[derive(Debug)]
+26pub struct PartialDecode {
+27    plain_header: ProtectedHeader,
+28    buf: io::Cursor<BytesMut>,
+29}
+30
+31#[allow(clippy::len_without_is_empty)]
+32impl PartialDecode {
+33    /// Begin decoding a QUIC packet from `bytes`, returning any trailing data not part of that packet
+34    pub fn new(
+35        bytes: BytesMut,
+36        cid_parser: &(impl ConnectionIdParser + ?Sized),
+37        supported_versions: &[u32],
+38        grease_quic_bit: bool,
+39    ) -> Result<(Self, Option<BytesMut>), PacketDecodeError> {
+40        let mut buf = io::Cursor::new(bytes);
+41        let plain_header =
+42            ProtectedHeader::decode(&mut buf, cid_parser, supported_versions, grease_quic_bit)?;
+43        let dgram_len = buf.get_ref().len();
+44        let packet_len = plain_header
+45            .payload_len()
+46            .map(|len| (buf.position() + len) as usize)
+47            .unwrap_or(dgram_len);
+48        match dgram_len.cmp(&packet_len) {
+49            Ordering::Equal => Ok((Self { plain_header, buf }, None)),
+50            Ordering::Less => Err(PacketDecodeError::InvalidHeader(
+51                "packet too short to contain payload length",
+52            )),
+53            Ordering::Greater => {
+54                let rest = Some(buf.get_mut().split_off(packet_len));
+55                Ok((Self { plain_header, buf }, rest))
+56            }
+57        }
+58    }
+59
+60    /// The underlying partially-decoded packet data
+61    pub(crate) fn data(&self) -> &[u8] {
+62        self.buf.get_ref()
+63    }
+64
+65    pub(crate) fn initial_header(&self) -> Option<&ProtectedInitialHeader> {
+66        self.plain_header.as_initial()
+67    }
+68
+69    pub(crate) fn has_long_header(&self) -> bool {
+70        !matches!(self.plain_header, ProtectedHeader::Short { .. })
+71    }
+72
+73    pub(crate) fn is_initial(&self) -> bool {
+74        self.space() == Some(SpaceId::Initial)
+75    }
+76
+77    pub(crate) fn space(&self) -> Option<SpaceId> {
+78        use ProtectedHeader::*;
+79        match self.plain_header {
+80            Initial { .. } => Some(SpaceId::Initial),
+81            Long {
+82                ty: LongType::Handshake,
+83                ..
+84            } => Some(SpaceId::Handshake),
+85            Long {
+86                ty: LongType::ZeroRtt,
+87                ..
+88            } => Some(SpaceId::Data),
+89            Short { .. } => Some(SpaceId::Data),
+90            _ => None,
+91        }
+92    }
+93
+94    pub(crate) fn is_0rtt(&self) -> bool {
+95        match self.plain_header {
+96            ProtectedHeader::Long { ty, .. } => ty == LongType::ZeroRtt,
+97            _ => false,
+98        }
+99    }
+100
+101    /// The destination connection ID of the packet
+102    pub fn dst_cid(&self) -> ConnectionId {
+103        self.plain_header.dst_cid()
+104    }
+105
+106    /// Length of QUIC packet being decoded
+107    #[allow(unreachable_pub)] // fuzzing only
+108    pub fn len(&self) -> usize {
+109        self.buf.get_ref().len()
+110    }
+111
+112    pub(crate) fn finish(
+113        self,
+114        header_crypto: Option<&dyn crypto::HeaderKey>,
+115    ) -> Result<Packet, PacketDecodeError> {
+116        use ProtectedHeader::*;
+117        let Self {
+118            plain_header,
+119            mut buf,
+120        } = self;
+121
+122        if let Initial(ProtectedInitialHeader {
+123            dst_cid,
+124            src_cid,
+125            token_pos,
+126            version,
+127            ..
+128        }) = plain_header
+129        {
+130            let number = Self::decrypt_header(&mut buf, header_crypto.unwrap())?;
+131            let header_len = buf.position() as usize;
+132            let mut bytes = buf.into_inner();
+133
+134            let header_data = bytes.split_to(header_len).freeze();
+135            let token = header_data.slice(token_pos.start..token_pos.end);
+136            return Ok(Packet {
+137                header: Header::Initial(InitialHeader {
+138                    dst_cid,
+139                    src_cid,
+140                    token,
+141                    number,
+142                    version,
+143                }),
+144                header_data,
+145                payload: bytes,
+146            });
+147        }
+148
+149        let header = match plain_header {
+150            Long {
+151                ty,
+152                dst_cid,
+153                src_cid,
+154                version,
+155                ..
+156            } => Header::Long {
+157                ty,
+158                dst_cid,
+159                src_cid,
+160                number: Self::decrypt_header(&mut buf, header_crypto.unwrap())?,
+161                version,
+162            },
+163            Retry {
+164                dst_cid,
+165                src_cid,
+166                version,
+167            } => Header::Retry {
+168                dst_cid,
+169                src_cid,
+170                version,
+171            },
+172            Short { spin, dst_cid, .. } => {
+173                let number = Self::decrypt_header(&mut buf, header_crypto.unwrap())?;
+174                let key_phase = buf.get_ref()[0] & KEY_PHASE_BIT != 0;
+175                Header::Short {
+176                    spin,
+177                    key_phase,
+178                    dst_cid,
+179                    number,
+180                }
+181            }
+182            VersionNegotiate {
+183                random,
+184                dst_cid,
+185                src_cid,
+186            } => Header::VersionNegotiate {
+187                random,
+188                dst_cid,
+189                src_cid,
+190            },
+191            Initial { .. } => unreachable!(),
+192        };
+193
+194        let header_len = buf.position() as usize;
+195        let mut bytes = buf.into_inner();
+196        Ok(Packet {
+197            header,
+198            header_data: bytes.split_to(header_len).freeze(),
+199            payload: bytes,
+200        })
+201    }
+202
+203    fn decrypt_header(
+204        buf: &mut io::Cursor<BytesMut>,
+205        header_crypto: &dyn crypto::HeaderKey,
+206    ) -> Result<PacketNumber, PacketDecodeError> {
+207        let packet_length = buf.get_ref().len();
+208        let pn_offset = buf.position() as usize;
+209        if packet_length < pn_offset + 4 + header_crypto.sample_size() {
+210            return Err(PacketDecodeError::InvalidHeader(
+211                "packet too short to extract header protection sample",
+212            ));
+213        }
+214
+215        header_crypto.decrypt(pn_offset, buf.get_mut());
+216
+217        let len = PacketNumber::decode_len(buf.get_ref()[0]);
+218        PacketNumber::decode(len, buf)
+219    }
+220}
+221
+222/// A buffer that can tell how much has been written to it already
+223///
+224/// This is commonly used for when a buffer is passed and the user may not write past a
+225/// given size. It allows the user of such a buffer to know the current cursor position in
+226/// the buffer. The maximum write size is usually passed in the same unit as
+227/// [`BufLen::len`]: bytes since the buffer start.
+228pub(crate) trait BufLen {
+229    /// Returns the number of bytes written into the buffer so far
+230    fn len(&self) -> usize;
+231}
+232
+233impl BufLen for Vec<u8> {
+234    fn len(&self) -> usize {
+235        self.len()
+236    }
+237}
+238
+239pub(crate) struct Packet {
+240    pub(crate) header: Header,
+241    pub(crate) header_data: Bytes,
+242    pub(crate) payload: BytesMut,
+243}
+244
+245impl Packet {
+246    pub(crate) fn reserved_bits_valid(&self) -> bool {
+247        let mask = match self.header {
+248            Header::Short { .. } => SHORT_RESERVED_BITS,
+249            _ => LONG_RESERVED_BITS,
+250        };
+251        self.header_data[0] & mask == 0
+252    }
+253}
+254
+255pub(crate) struct InitialPacket {
+256    pub(crate) header: InitialHeader,
+257    pub(crate) header_data: Bytes,
+258    pub(crate) payload: BytesMut,
+259}
+260
+261impl From<InitialPacket> for Packet {
+262    fn from(x: InitialPacket) -> Self {
+263        Self {
+264            header: Header::Initial(x.header),
+265            header_data: x.header_data,
+266            payload: x.payload,
+267        }
+268    }
+269}
+270
+271#[cfg_attr(test, derive(Clone))]
+272#[derive(Debug)]
+273pub(crate) enum Header {
+274    Initial(InitialHeader),
+275    Long {
+276        ty: LongType,
+277        dst_cid: ConnectionId,
+278        src_cid: ConnectionId,
+279        number: PacketNumber,
+280        version: u32,
+281    },
+282    Retry {
+283        dst_cid: ConnectionId,
+284        src_cid: ConnectionId,
+285        version: u32,
+286    },
+287    Short {
+288        spin: bool,
+289        key_phase: bool,
+290        dst_cid: ConnectionId,
+291        number: PacketNumber,
+292    },
+293    VersionNegotiate {
+294        random: u8,
+295        src_cid: ConnectionId,
+296        dst_cid: ConnectionId,
+297    },
+298}
+299
+300impl Header {
+301    pub(crate) fn encode(&self, w: &mut (impl BufMut + BufLen)) -> PartialEncode {
+302        use Header::*;
+303        let start = w.len();
+304        match *self {
+305            Initial(InitialHeader {
+306                ref dst_cid,
+307                ref src_cid,
+308                ref token,
+309                number,
+310                version,
+311            }) => {
+312                w.write(u8::from(LongHeaderType::Initial) | number.tag());
+313                w.write(version);
+314                dst_cid.encode_long(w);
+315                src_cid.encode_long(w);
+316                w.write_var(token.len() as u64);
+317                w.put_slice(token);
+318                w.write::<u16>(0); // Placeholder for payload length; see `set_payload_length`
+319                number.encode(w);
+320                PartialEncode {
+321                    start,
+322                    header_len: w.len() - start,
+323                    pn: Some((number.len(), true)),
+324                }
+325            }
+326            Long {
+327                ty,
+328                ref dst_cid,
+329                ref src_cid,
+330                number,
+331                version,
+332            } => {
+333                w.write(u8::from(LongHeaderType::Standard(ty)) | number.tag());
+334                w.write(version);
+335                dst_cid.encode_long(w);
+336                src_cid.encode_long(w);
+337                w.write::<u16>(0); // Placeholder for payload length; see `set_payload_length`
+338                number.encode(w);
+339                PartialEncode {
+340                    start,
+341                    header_len: w.len() - start,
+342                    pn: Some((number.len(), true)),
+343                }
+344            }
+345            Retry {
+346                ref dst_cid,
+347                ref src_cid,
+348                version,
+349            } => {
+350                w.write(u8::from(LongHeaderType::Retry));
+351                w.write(version);
+352                dst_cid.encode_long(w);
+353                src_cid.encode_long(w);
+354                PartialEncode {
+355                    start,
+356                    header_len: w.len() - start,
+357                    pn: None,
+358                }
+359            }
+360            Short {
+361                spin,
+362                key_phase,
+363                ref dst_cid,
+364                number,
+365            } => {
+366                w.write(
+367                    FIXED_BIT
+368                        | if key_phase { KEY_PHASE_BIT } else { 0 }
+369                        | if spin { SPIN_BIT } else { 0 }
+370                        | number.tag(),
+371                );
+372                w.put_slice(dst_cid);
+373                number.encode(w);
+374                PartialEncode {
+375                    start,
+376                    header_len: w.len() - start,
+377                    pn: Some((number.len(), false)),
+378                }
+379            }
+380            VersionNegotiate {
+381                ref random,
+382                ref dst_cid,
+383                ref src_cid,
+384            } => {
+385                w.write(0x80u8 | random);
+386                w.write::<u32>(0);
+387                dst_cid.encode_long(w);
+388                src_cid.encode_long(w);
+389                PartialEncode {
+390                    start,
+391                    header_len: w.len() - start,
+392                    pn: None,
+393                }
+394            }
+395        }
+396    }
+397
+398    /// Whether the packet is encrypted on the wire
+399    pub(crate) fn is_protected(&self) -> bool {
+400        !matches!(*self, Self::Retry { .. } | Self::VersionNegotiate { .. })
+401    }
+402
+403    pub(crate) fn number(&self) -> Option<PacketNumber> {
+404        use Header::*;
+405        Some(match *self {
+406            Initial(InitialHeader { number, .. }) => number,
+407            Long { number, .. } => number,
+408            Short { number, .. } => number,
+409            _ => {
+410                return None;
+411            }
+412        })
+413    }
+414
+415    pub(crate) fn space(&self) -> SpaceId {
+416        use Header::*;
+417        match *self {
+418            Short { .. } => SpaceId::Data,
+419            Long {
+420                ty: LongType::ZeroRtt,
+421                ..
+422            } => SpaceId::Data,
+423            Long {
+424                ty: LongType::Handshake,
+425                ..
+426            } => SpaceId::Handshake,
+427            _ => SpaceId::Initial,
+428        }
+429    }
+430
+431    pub(crate) fn key_phase(&self) -> bool {
+432        match *self {
+433            Self::Short { key_phase, .. } => key_phase,
+434            _ => false,
+435        }
+436    }
+437
+438    pub(crate) fn is_short(&self) -> bool {
+439        matches!(*self, Self::Short { .. })
+440    }
+441
+442    pub(crate) fn is_1rtt(&self) -> bool {
+443        self.is_short()
+444    }
+445
+446    pub(crate) fn is_0rtt(&self) -> bool {
+447        matches!(
+448            *self,
+449            Self::Long {
+450                ty: LongType::ZeroRtt,
+451                ..
+452            }
+453        )
+454    }
+455
+456    pub(crate) fn dst_cid(&self) -> ConnectionId {
+457        use Header::*;
+458        match *self {
+459            Initial(InitialHeader { dst_cid, .. }) => dst_cid,
+460            Long { dst_cid, .. } => dst_cid,
+461            Retry { dst_cid, .. } => dst_cid,
+462            Short { dst_cid, .. } => dst_cid,
+463            VersionNegotiate { dst_cid, .. } => dst_cid,
+464        }
+465    }
+466
+467    /// Is this packet allowed to be coalesced with others?
+468    ///
+469    /// Ref <https://www.rfc-editor.org/rfc/rfc9000.html#name-coalescing-packets>
+470    pub(crate) fn can_coalesce(&self) -> bool {
+471        use Header::*;
+472        match *self {
+473            Initial(_) => true,
+474            Long { .. } => true,
+475            Retry { .. } => false,
+476            Short { .. } => false,
+477            VersionNegotiate { .. } => false,
+478        }
+479    }
+480
+481    /// Whether the payload of this packet contains QUIC frames
+482    pub(crate) fn has_frames(&self) -> bool {
+483        use Header::*;
+484        match *self {
+485            Initial(_) => true,
+486            Long { .. } => true,
+487            Retry { .. } => false,
+488            Short { .. } => true,
+489            VersionNegotiate { .. } => false,
+490        }
+491    }
+492
+493    #[cfg(feature = "qlog")]
+494    pub(crate) fn src_cid(&self) -> Option<ConnectionId> {
+495        match self {
+496            Self::Initial(initial_header) => Some(initial_header.src_cid),
+497            Self::Long { src_cid, .. } => Some(*src_cid),
+498            Self::Retry { src_cid, .. } => Some(*src_cid),
+499            Self::Short { .. } => None,
+500            Self::VersionNegotiate { src_cid, .. } => Some(*src_cid),
+501        }
+502    }
+503}
+504
+505pub(crate) struct PartialEncode {
+506    pub(crate) start: usize,
+507    pub(crate) header_len: usize,
+508    // Packet number length, payload length needed
+509    pn: Option<(usize, bool)>,
+510}
+511
+512impl PartialEncode {
+513    pub(crate) fn finish(
+514        self,
+515        buf: &mut [u8],
+516        header_crypto: &dyn crypto::HeaderKey,
+517        crypto: Option<(u64, PathId, &dyn crypto::PacketKey)>,
+518    ) {
+519        let Self { header_len, pn, .. } = self;
+520        let (pn_len, write_len) = match pn {
+521            Some((pn_len, write_len)) => (pn_len, write_len),
+522            None => return,
+523        };
+524
+525        let pn_pos = header_len - pn_len;
+526        if write_len {
+527            let len = buf.len() - header_len + pn_len;
+528            assert!(len < 2usize.pow(14)); // Fits in reserved space
+529            let mut slice = &mut buf[pn_pos - 2..pn_pos];
+530            slice.put_u16(len as u16 | (0b01 << 14));
+531        }
+532
+533        if let Some((packet_number, path_id, crypto)) = crypto {
+534            crypto.encrypt(path_id, packet_number, buf, header_len);
+535        }
+536
+537        debug_assert!(
+538            pn_pos + 4 + header_crypto.sample_size() <= buf.len(),
+539            "packet must be padded to at least {} bytes for header protection sampling",
+540            pn_pos + 4 + header_crypto.sample_size()
+541        );
+542        header_crypto.encrypt(pn_pos, buf);
+543    }
+544
+545    /// Creates a [`PartialEncode`] that has not encoded a header into the buffer.
+546    ///
+547    /// This is used exclusively for testing as such a type is otherwise invalid.
+548    #[cfg(test)]
+549    pub(crate) fn no_header() -> Self {
+550        Self {
+551            start: 0,
+552            header_len: 0,
+553            pn: None,
+554        }
+555    }
+556}
+557
+558/// Plain packet header
+559#[derive(Clone, Debug)]
+560pub enum ProtectedHeader {
+561    /// An Initial packet header
+562    Initial(ProtectedInitialHeader),
+563    /// A Long packet header, as used during the handshake
+564    Long {
+565        /// Type of the Long header packet
+566        ty: LongType,
+567        /// Destination Connection ID
+568        dst_cid: ConnectionId,
+569        /// Source Connection ID
+570        src_cid: ConnectionId,
+571        /// Length of the packet payload
+572        len: u64,
+573        /// QUIC version
+574        version: u32,
+575    },
+576    /// A Retry packet header
+577    Retry {
+578        /// Destination Connection ID
+579        dst_cid: ConnectionId,
+580        /// Source Connection ID
+581        src_cid: ConnectionId,
+582        /// QUIC version
+583        version: u32,
+584    },
+585    /// A short packet header, as used during the data phase
+586    Short {
+587        /// Spin bit
+588        spin: bool,
+589        /// Destination Connection ID
+590        dst_cid: ConnectionId,
+591    },
+592    /// A Version Negotiation packet header
+593    VersionNegotiate {
+594        /// Random value
+595        random: u8,
+596        /// Destination Connection ID
+597        dst_cid: ConnectionId,
+598        /// Source Connection ID
+599        src_cid: ConnectionId,
+600    },
+601}
+602
+603impl ProtectedHeader {
+604    fn as_initial(&self) -> Option<&ProtectedInitialHeader> {
+605        match self {
+606            Self::Initial(x) => Some(x),
+607            _ => None,
+608        }
+609    }
+610
+611    /// The destination Connection ID of the packet
+612    pub fn dst_cid(&self) -> ConnectionId {
+613        use ProtectedHeader::*;
+614        match self {
+615            Initial(header) => header.dst_cid,
+616            &Long { dst_cid, .. } => dst_cid,
+617            &Retry { dst_cid, .. } => dst_cid,
+618            &Short { dst_cid, .. } => dst_cid,
+619            &VersionNegotiate { dst_cid, .. } => dst_cid,
+620        }
+621    }
+622
+623    fn payload_len(&self) -> Option<u64> {
+624        use ProtectedHeader::*;
+625        match self {
+626            Initial(ProtectedInitialHeader { len, .. }) | Long { len, .. } => Some(*len),
+627            _ => None,
+628        }
+629    }
+630
+631    /// Decode a plain header from given buffer, with given [`ConnectionIdParser`].
+632    pub fn decode(
+633        buf: &mut io::Cursor<BytesMut>,
+634        cid_parser: &(impl ConnectionIdParser + ?Sized),
+635        supported_versions: &[u32],
+636        grease_quic_bit: bool,
+637    ) -> Result<Self, PacketDecodeError> {
+638        let first = buf.get::<u8>()?;
+639        if !grease_quic_bit && first & FIXED_BIT == 0 {
+640            return Err(PacketDecodeError::InvalidHeader("fixed bit unset"));
+641        }
+642        if first & LONG_HEADER_FORM == 0 {
+643            let spin = first & SPIN_BIT != 0;
+644
+645            Ok(Self::Short {
+646                spin,
+647                dst_cid: cid_parser.parse(buf)?,
+648            })
+649        } else {
+650            let version = buf.get::<u32>()?;
+651
+652            let dst_cid = ConnectionId::decode_long(buf)
+653                .ok_or(PacketDecodeError::InvalidHeader("malformed cid"))?;
+654            let src_cid = ConnectionId::decode_long(buf)
+655                .ok_or(PacketDecodeError::InvalidHeader("malformed cid"))?;
+656
+657            // TODO: Support long CIDs for compatibility with future QUIC versions
+658            if version == 0 {
+659                let random = first & !LONG_HEADER_FORM;
+660                return Ok(Self::VersionNegotiate {
+661                    random,
+662                    dst_cid,
+663                    src_cid,
+664                });
+665            }
+666
+667            if !supported_versions.contains(&version) {
+668                return Err(PacketDecodeError::UnsupportedVersion {
+669                    src_cid,
+670                    dst_cid,
+671                    version,
+672                });
+673            }
+674
+675            match LongHeaderType::from_byte(first)? {
+676                LongHeaderType::Initial => {
+677                    let token_len = buf.get_var()? as usize;
+678                    let token_start = buf.position() as usize;
+679                    if token_len > buf.remaining() {
+680                        return Err(PacketDecodeError::InvalidHeader("token out of bounds"));
+681                    }
+682                    buf.advance(token_len);
+683
+684                    let len = buf.get_var()?;
+685                    Ok(Self::Initial(ProtectedInitialHeader {
+686                        dst_cid,
+687                        src_cid,
+688                        token_pos: token_start..token_start + token_len,
+689                        len,
+690                        version,
+691                    }))
+692                }
+693                LongHeaderType::Retry => Ok(Self::Retry {
+694                    dst_cid,
+695                    src_cid,
+696                    version,
+697                }),
+698                LongHeaderType::Standard(ty) => Ok(Self::Long {
+699                    ty,
+700                    dst_cid,
+701                    src_cid,
+702                    len: buf.get_var()?,
+703                    version,
+704                }),
+705            }
+706        }
+707    }
+708}
+709
+710/// Header of an Initial packet, before decryption
+711#[derive(Clone, Debug)]
+712pub struct ProtectedInitialHeader {
+713    /// Destination Connection ID
+714    pub dst_cid: ConnectionId,
+715    /// Source Connection ID
+716    pub src_cid: ConnectionId,
+717    /// The position of a token in the packet buffer
+718    pub token_pos: Range<usize>,
+719    /// Length of the packet payload
+720    pub len: u64,
+721    /// QUIC version
+722    pub version: u32,
+723}
+724
+725#[derive(Clone, Debug)]
+726pub(crate) struct InitialHeader {
+727    pub(crate) dst_cid: ConnectionId,
+728    pub(crate) src_cid: ConnectionId,
+729    pub(crate) token: Bytes,
+730    pub(crate) number: PacketNumber,
+731    pub(crate) version: u32,
+732}
+733
+734// An encoded packet number
+735#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+736pub(crate) enum PacketNumber {
+737    U8(u8),
+738    U16(u16),
+739    U24(u32),
+740    U32(u32),
+741}
+742
+743impl PacketNumber {
+744    pub(crate) fn new(n: u64, largest_acked: u64) -> Self {
+745        let range = (n - largest_acked) * 2;
+746        if range < 1 << 8 {
+747            Self::U8(n as u8)
+748        } else if range < 1 << 16 {
+749            Self::U16(n as u16)
+750        } else if range < 1 << 24 {
+751            Self::U24(n as u32)
+752        } else if range < 1 << 32 {
+753            Self::U32(n as u32)
+754        } else {
+755            panic!("packet number too large to encode")
+756        }
+757    }
+758
+759    pub(crate) fn len(self) -> usize {
+760        use PacketNumber::*;
+761        match self {
+762            U8(_) => 1,
+763            U16(_) => 2,
+764            U24(_) => 3,
+765            U32(_) => 4,
+766        }
+767    }
+768
+769    pub(crate) fn encode<W: BufMut>(self, w: &mut W) {
+770        use PacketNumber::*;
+771        match self {
+772            U8(x) => w.write(x),
+773            U16(x) => w.write(x),
+774            U24(x) => w.put_uint(u64::from(x), 3),
+775            U32(x) => w.write(x),
+776        }
+777    }
+778
+779    pub(crate) fn decode<R: Buf>(len: usize, r: &mut R) -> Result<Self, PacketDecodeError> {
+780        use PacketNumber::*;
+781        let pn = match len {
+782            1 => U8(r.get()?),
+783            2 => U16(r.get()?),
+784            3 => U24(r.get_uint(3) as u32),
+785            4 => U32(r.get()?),
+786            _ => unreachable!(),
+787        };
+788        Ok(pn)
+789    }
+790
+791    pub(crate) fn decode_len(tag: u8) -> usize {
+792        1 + (tag & 0x03) as usize
+793    }
+794
+795    fn tag(self) -> u8 {
+796        use PacketNumber::*;
+797        match self {
+798            U8(_) => 0b00,
+799            U16(_) => 0b01,
+800            U24(_) => 0b10,
+801            U32(_) => 0b11,
+802        }
+803    }
+804
+805    pub(crate) fn expand(self, expected: u64) -> u64 {
+806        // From Appendix A
+807        use PacketNumber::*;
+808        let truncated = match self {
+809            U8(x) => u64::from(x),
+810            U16(x) => u64::from(x),
+811            U24(x) => u64::from(x),
+812            U32(x) => u64::from(x),
+813        };
+814        let nbits = self.len() * 8;
+815        let win = 1 << nbits;
+816        let hwin = win / 2;
+817        let mask = win - 1;
+818        // The incoming packet number should be greater than expected - hwin and less than or equal
+819        // to expected + hwin
+820        //
+821        // This means we can't just strip the trailing bits from expected and add the truncated
+822        // because that might yield a value outside the window.
+823        //
+824        // The following code calculates a candidate value and makes sure it's within the packet
+825        // number window.
+826        let candidate = (expected & !mask) | truncated;
+827        if expected.checked_sub(hwin).is_some_and(|x| candidate <= x) {
+828            candidate + win
+829        } else if candidate > expected + hwin && candidate > win {
+830            candidate - win
+831        } else {
+832            candidate
+833        }
+834    }
+835}
+836
+837/// A [`ConnectionIdParser`] implementation that assumes the connection ID is of fixed length
+838pub struct FixedLengthConnectionIdParser {
+839    expected_len: usize,
+840}
+841
+842impl FixedLengthConnectionIdParser {
+843    /// Create a new instance of `FixedLengthConnectionIdParser`
+844    pub fn new(expected_len: usize) -> Self {
+845        Self { expected_len }
+846    }
+847}
+848
+849impl ConnectionIdParser for FixedLengthConnectionIdParser {
+850    fn parse(&self, buffer: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError> {
+851        (buffer.remaining() >= self.expected_len)
+852            .then(|| ConnectionId::from_buf(buffer, self.expected_len))
+853            .ok_or(PacketDecodeError::InvalidHeader("packet too small"))
+854    }
+855}
+856
+857/// Parse connection id in short header packet
+858pub trait ConnectionIdParser {
+859    /// Parse a connection id from given buffer
+860    fn parse(&self, buf: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError>;
+861}
+862
+863/// Long packet type including non-uniform cases
+864#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+865pub(crate) enum LongHeaderType {
+866    Initial,
+867    Retry,
+868    Standard(LongType),
+869}
+870
+871impl LongHeaderType {
+872    fn from_byte(b: u8) -> Result<Self, PacketDecodeError> {
+873        use {LongHeaderType::*, LongType::*};
+874        debug_assert!(b & LONG_HEADER_FORM != 0, "not a long packet");
+875        Ok(match (b & 0x30) >> 4 {
+876            0x0 => Initial,
+877            0x1 => Standard(ZeroRtt),
+878            0x2 => Standard(Handshake),
+879            0x3 => Retry,
+880            _ => unreachable!(),
+881        })
+882    }
+883}
+884
+885impl From<LongHeaderType> for u8 {
+886    fn from(ty: LongHeaderType) -> Self {
+887        use {LongHeaderType::*, LongType::*};
+888        match ty {
+889            Initial => LONG_HEADER_FORM | FIXED_BIT,
+890            Standard(ZeroRtt) => LONG_HEADER_FORM | FIXED_BIT | (0x1 << 4),
+891            Standard(Handshake) => LONG_HEADER_FORM | FIXED_BIT | (0x2 << 4),
+892            Retry => LONG_HEADER_FORM | FIXED_BIT | (0x3 << 4),
+893        }
+894    }
+895}
+896
+897/// Long packet types with uniform header structure
+898#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+899pub enum LongType {
+900    /// Handshake packet
+901    Handshake,
+902    /// 0-RTT packet
+903    ZeroRtt,
+904}
+905
+906/// Packet decode error
+907#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+908pub enum PacketDecodeError {
+909    /// Packet uses a QUIC version that is not supported
+910    #[error("unsupported version {version:x}")]
+911    UnsupportedVersion {
+912        /// Source Connection ID
+913        src_cid: ConnectionId,
+914        /// Destination Connection ID
+915        dst_cid: ConnectionId,
+916        /// The version that was unsupported
+917        version: u32,
+918    },
+919    /// The packet header is invalid
+920    #[error("invalid header: {0}")]
+921    InvalidHeader(&'static str),
+922}
+923
+924impl From<coding::UnexpectedEnd> for PacketDecodeError {
+925    fn from(_: coding::UnexpectedEnd) -> Self {
+926        Self::InvalidHeader("unexpected end of packet")
+927    }
+928}
+929
+930pub(crate) const LONG_HEADER_FORM: u8 = 0x80;
+931pub(crate) const FIXED_BIT: u8 = 0x40;
+932pub(crate) const SPIN_BIT: u8 = 0x20;
+933const SHORT_RESERVED_BITS: u8 = 0x18;
+934const LONG_RESERVED_BITS: u8 = 0x0c;
+935const KEY_PHASE_BIT: u8 = 0x04;
+936
+937/// Packet number space identifiers
+938#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+939pub(crate) enum SpaceId {
+940    /// Unprotected packets, used to bootstrap the handshake
+941    Initial = 0,
+942    Handshake = 1,
+943    /// Application data space, used for 0-RTT and post-handshake/1-RTT packets
+944    Data = 2,
+945}
+946
+947impl SpaceId {
+948    pub(crate) fn iter() -> impl Iterator<Item = Self> {
+949        [Self::Initial, Self::Handshake, Self::Data].iter().cloned()
+950    }
+951
+952    /// Returns the next higher packet space.
+953    ///
+954    /// Returns `None` if at  [`SpaceId::Data`].
+955    pub(crate) fn next(&self) -> Option<Self> {
+956        match self {
+957            Self::Initial => Some(Self::Handshake),
+958            Self::Handshake => Some(Self::Data),
+959            Self::Data => None,
+960        }
+961    }
+962}
+963
+964#[cfg(test)]
+965mod tests {
+966    use super::*;
+967    use hex_literal::hex;
+968    use std::io;
+969
+970    fn check_pn(typed: PacketNumber, encoded: &[u8]) {
+971        let mut buf = Vec::new();
+972        typed.encode(&mut buf);
+973        assert_eq!(&buf[..], encoded);
+974        let decoded = PacketNumber::decode(typed.len(), &mut io::Cursor::new(&buf)).unwrap();
+975        assert_eq!(typed, decoded);
+976    }
+977
+978    #[test]
+979    fn roundtrip_packet_numbers() {
+980        check_pn(PacketNumber::U8(0x7f), &hex!("7f"));
+981        check_pn(PacketNumber::U16(0x80), &hex!("0080"));
+982        check_pn(PacketNumber::U16(0x3fff), &hex!("3fff"));
+983        check_pn(PacketNumber::U32(0x0000_4000), &hex!("0000 4000"));
+984        check_pn(PacketNumber::U32(0xffff_ffff), &hex!("ffff ffff"));
+985    }
+986
+987    #[test]
+988    fn pn_encode() {
+989        check_pn(PacketNumber::new(0x10, 0), &hex!("10"));
+990        check_pn(PacketNumber::new(0x100, 0), &hex!("0100"));
+991        check_pn(PacketNumber::new(0x10000, 0), &hex!("010000"));
+992    }
+993
+994    #[test]
+995    fn pn_expand_roundtrip() {
+996        for expected in 0..1024 {
+997            for actual in expected..1024 {
+998                assert_eq!(actual, PacketNumber::new(actual, expected).expand(expected));
+999            }
+1000        }
+1001    }
+1002
+1003    #[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+1004    #[test]
+1005    fn header_encoding() {
+1006        use crate::Side;
+1007        use crate::crypto::rustls::{initial_keys, initial_suite_from_provider};
+1008        #[cfg(all(feature = "rustls-aws-lc-rs", not(feature = "rustls-ring")))]
+1009        use rustls::crypto::aws_lc_rs::default_provider;
+1010        #[cfg(feature = "rustls-ring")]
+1011        use rustls::crypto::ring::default_provider;
+1012        use rustls::quic::Version;
+1013
+1014        let dcid = ConnectionId::new(&hex!("06b858ec6f80452b"));
+1015        let provider = default_provider();
+1016
+1017        let suite = initial_suite_from_provider(&std::sync::Arc::new(provider)).unwrap();
+1018        let client = initial_keys(Version::V1, dcid, Side::Client, &suite);
+1019        let mut buf = Vec::new();
+1020        let header = Header::Initial(InitialHeader {
+1021            number: PacketNumber::U8(0),
+1022            src_cid: ConnectionId::new(&[]),
+1023            dst_cid: dcid,
+1024            token: Bytes::new(),
+1025            version: crate::DEFAULT_SUPPORTED_VERSIONS[0],
+1026        });
+1027        let encode = header.encode(&mut buf);
+1028        let header_len = buf.len();
+1029        buf.resize(header_len + 16 + client.packet.local.tag_len(), 0);
+1030        encode.finish(
+1031            &mut buf,
+1032            &*client.header.local,
+1033            Some((0, PathId::ZERO, &*client.packet.local)),
+1034        );
+1035
+1036        for byte in &buf {
+1037            print!("{byte:02x}");
+1038        }
+1039        println!();
+1040        assert_eq!(
+1041            buf[..],
+1042            hex!(
+1043                "c8000000010806b858ec6f80452b00004021be
+1044                 3ef50807b84191a196f760a6dad1e9d1c430c48952cba0148250c21c0a6a70e1"
+1045            )[..]
+1046        );
+1047
+1048        let server = initial_keys(Version::V1, dcid, Side::Server, &suite);
+1049        let supported_versions = crate::DEFAULT_SUPPORTED_VERSIONS.to_vec();
+1050        let decode = PartialDecode::new(
+1051            buf.as_slice().into(),
+1052            &FixedLengthConnectionIdParser::new(0),
+1053            &supported_versions,
+1054            false,
+1055        )
+1056        .unwrap()
+1057        .0;
+1058        let mut packet = decode.finish(Some(&*server.header.remote)).unwrap();
+1059        assert_eq!(
+1060            packet.header_data[..],
+1061            hex!("c0000000010806b858ec6f80452b0000402100")[..]
+1062        );
+1063        server
+1064            .packet
+1065            .remote
+1066            .decrypt(PathId::ZERO, 0, &packet.header_data, &mut packet.payload)
+1067            .unwrap();
+1068        assert_eq!(packet.payload[..], [0; 16]);
+1069        match packet.header {
+1070            Header::Initial(InitialHeader {
+1071                number: PacketNumber::U8(0),
+1072                ..
+1073            }) => {}
+1074            _ => {
+1075                panic!("unexpected header {:?}", packet.header);
+1076            }
+1077        }
+1078    }
+1079}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/range_set/array_range_set.rs.html b/pr/395/docs/src/iroh_quinn_proto/range_set/array_range_set.rs.html new file mode 100644 index 000000000..a8ccd40d8 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/range_set/array_range_set.rs.html @@ -0,0 +1,253 @@ +array_range_set.rs - source

iroh_quinn_proto/range_set/
array_range_set.rs

1use std::fmt::{self, Write};
+2use std::ops::Range;
+3
+4use tinyvec::TinyVec;
+5
+6/// A set of u64 values optimized for long runs and random insert/delete/contains
+7///
+8/// `ArrayRangeSet` uses an array representation, where each array entry represents
+9/// a range.
+10///
+11/// The array-based RangeSet provides 2 benefits:
+12/// - There exists an inline representation, which avoids the need of heap
+13///   allocating ACK ranges for SentFrames for small ranges.
+14/// - Iterating over ranges should usually be faster since there is only
+15///   a single cache-friendly contiguous range.
+16///
+17/// `ArrayRangeSet` is especially useful for tracking ACK ranges where the amount
+18/// of ranges is usually very low (since ACK numbers are in consecutive fashion
+19/// unless reordering or packet loss occur).
+20#[derive(Default, PartialEq, Eq)]
+21pub(crate) struct ArrayRangeSet(TinyVec<[Range<u64>; ARRAY_RANGE_SET_INLINE_CAPACITY]>);
+22
+23impl fmt::Debug for ArrayRangeSet {
+24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+25        f.write_char('[')?;
+26        let mut first = true;
+27        for range in self.0.iter() {
+28            if !first {
+29                f.write_char(',')?;
+30            }
+31            write!(f, "{range:?}")?;
+32            first = false;
+33        }
+34        f.write_char(']')?;
+35        Ok(())
+36    }
+37}
+38
+39/// The capacity of elements directly stored in [`ArrayRangeSet`]
+40///
+41/// An inline capacity of 2 is chosen to keep `SentFrame` below 128 bytes.
+42const ARRAY_RANGE_SET_INLINE_CAPACITY: usize = 2;
+43
+44impl Clone for ArrayRangeSet {
+45    fn clone(&self) -> Self {
+46        // tinyvec keeps the heap representation after clones.
+47        // We rather prefer the inline representation for clones if possible,
+48        // since clones (e.g. for storage in `SentFrames`) are rarely mutated
+49        if self.0.is_inline() || self.0.len() > ARRAY_RANGE_SET_INLINE_CAPACITY {
+50            return Self(self.0.clone());
+51        }
+52
+53        let mut vec = TinyVec::new();
+54        vec.extend_from_slice(self.0.as_slice());
+55        Self(vec)
+56    }
+57}
+58
+59impl ArrayRangeSet {
+60    pub(crate) fn new() -> Self {
+61        Default::default()
+62    }
+63
+64    pub(crate) fn iter(&self) -> impl DoubleEndedIterator<Item = Range<u64>> + '_ {
+65        self.0.iter().cloned()
+66    }
+67
+68    pub(crate) fn elts(&self) -> impl Iterator<Item = u64> + '_ {
+69        self.iter().flatten()
+70    }
+71
+72    pub(crate) fn len(&self) -> usize {
+73        self.0.len()
+74    }
+75
+76    #[cfg(test)]
+77    pub(crate) fn contains(&self, x: u64) -> bool {
+78        for range in self.0.iter() {
+79            if range.start > x {
+80                // We only get here if there was no prior range that contained x
+81                return false;
+82            } else if range.contains(&x) {
+83                return true;
+84            }
+85        }
+86        false
+87    }
+88
+89    pub(crate) fn iter_range(&self, range: Range<u64>) -> impl Iterator<Item = Range<u64>> + '_ {
+90        self.iter().filter_map(move |r| {
+91            if r.end > range.start && r.start < range.end {
+92                Some(r.start.max(range.start)..r.end.min(range.end))
+93            } else {
+94                None
+95            }
+96        })
+97    }
+98
+99    pub(crate) fn insert_one(&mut self, x: u64) -> bool {
+100        self.insert(x..x + 1)
+101    }
+102
+103    pub(crate) fn insert(&mut self, x: Range<u64>) -> bool {
+104        let mut result = false;
+105
+106        if x.is_empty() {
+107            // Don't try to deal with ranges where x.end <= x.start
+108            return false;
+109        }
+110
+111        let mut idx = 0;
+112        while idx != self.0.len() {
+113            let range = &mut self.0[idx];
+114
+115            if range.start > x.end {
+116                // The range is fully before this range and therefore not extensible.
+117                // Add a new range to the left
+118                self.0.insert(idx, x);
+119                return true;
+120            } else if range.start > x.start {
+121                // The new range starts before this range but overlaps.
+122                // Extend the current range to the left
+123                // Note that we don't have to merge a potential left range, since
+124                // this case would have been captured by merging the right range
+125                // in the previous loop iteration
+126                result = true;
+127                range.start = x.start;
+128            }
+129
+130            // At this point we have handled all parts of the new range which
+131            // are in front of the current range. Now we handle everything from
+132            // the start of the current range
+133
+134            if x.end <= range.end {
+135                // Fully contained
+136                return result;
+137            } else if x.start <= range.end {
+138                // Extend the current range to the end of the new range.
+139                // Since it's not contained it must be bigger
+140                range.end = x.end;
+141
+142                // Merge all follow-up ranges which overlap
+143                while idx != self.0.len() - 1 {
+144                    let curr = self.0[idx].clone();
+145                    let next = self.0[idx + 1].clone();
+146                    if curr.end >= next.start {
+147                        self.0[idx].end = next.end.max(curr.end);
+148                        self.0.remove(idx + 1);
+149                    } else {
+150                        break;
+151                    }
+152                }
+153
+154                return true;
+155            }
+156
+157            idx += 1;
+158        }
+159
+160        // Insert a range at the end
+161        self.0.push(x);
+162        true
+163    }
+164
+165    pub(crate) fn remove(&mut self, x: Range<u64>) -> bool {
+166        let mut result = false;
+167
+168        if x.is_empty() {
+169            // Don't try to deal with ranges where x.end <= x.start
+170            return false;
+171        }
+172
+173        let mut idx = 0;
+174        while idx != self.0.len() && x.start != x.end {
+175            let range = self.0[idx].clone();
+176
+177            if x.end <= range.start {
+178                // The range is fully before this range
+179                return result;
+180            } else if x.start >= range.end {
+181                // The range is fully after this range
+182                idx += 1;
+183                continue;
+184            }
+185
+186            // The range overlaps with this range
+187            result = true;
+188
+189            let left = range.start..x.start;
+190            let right = x.end..range.end;
+191            if left.is_empty() && right.is_empty() {
+192                self.0.remove(idx);
+193            } else if left.is_empty() {
+194                self.0[idx] = right;
+195                idx += 1;
+196            } else if right.is_empty() {
+197                self.0[idx] = left;
+198                idx += 1;
+199            } else {
+200                self.0[idx] = right;
+201                self.0.insert(idx, left);
+202                idx += 2;
+203            }
+204        }
+205
+206        result
+207    }
+208
+209    pub(crate) fn is_empty(&self) -> bool {
+210        self.0.is_empty()
+211    }
+212
+213    pub(crate) fn pop_min(&mut self) -> Option<Range<u64>> {
+214        if !self.0.is_empty() {
+215            Some(self.0.remove(0))
+216        } else {
+217            None
+218        }
+219    }
+220
+221    pub(crate) fn min(&self) -> Option<u64> {
+222        self.iter().next().map(|x| x.start)
+223    }
+224
+225    pub(crate) fn max(&self) -> Option<u64> {
+226        self.iter().next_back().map(|x| x.end - 1)
+227    }
+228}
+229
+230#[cfg(test)]
+231impl proptest::arbitrary::Arbitrary for ArrayRangeSet {
+232    type Parameters = ();
+233    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+234
+235    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
+236        use proptest::prelude::*;
+237        // Generate 1-8 ranges. Each range is defined by a gap from the previous and a size.
+238        // We use small values to keep encoding reasonable.
+239        prop::collection::vec((1u64..100, 1u64..50), 0..8)
+240            .prop_map(|gaps_and_sizes| {
+241                let mut ranges = Self::new();
+242                let mut pos = 0u64;
+243                for (gap, size) in gaps_and_sizes {
+244                    let start = pos + gap;
+245                    let end = start + size;
+246                    ranges.insert(start..end);
+247                    pos = end;
+248                }
+249                ranges
+250            })
+251            .boxed()
+252    }
+253}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/range_set/mod.rs.html b/pr/395/docs/src/iroh_quinn_proto/range_set/mod.rs.html new file mode 100644 index 000000000..d8326bb7f --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/range_set/mod.rs.html @@ -0,0 +1,5 @@ +mod.rs - source

iroh_quinn_proto/range_set/
mod.rs

1mod array_range_set;
+2#[cfg(test)]
+3mod tests;
+4
+5pub(crate) use array_range_set::ArrayRangeSet;
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/shared.rs.html b/pr/395/docs/src/iroh_quinn_proto/shared.rs.html new file mode 100644 index 000000000..7dd30351a --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/shared.rs.html @@ -0,0 +1,201 @@ +shared.rs - source

iroh_quinn_proto/
shared.rs

1use std::{fmt, net::SocketAddr};
+2
+3use bytes::{Buf, BufMut, BytesMut};
+4
+5use crate::FourTuple;
+6use crate::PathId;
+7use crate::{Duration, Instant, MAX_CID_SIZE, ResetToken, coding::BufExt, packet::PartialDecode};
+8
+9/// Events sent from an Endpoint to a Connection
+10#[derive(Debug)]
+11pub struct ConnectionEvent(pub(crate) ConnectionEventInner);
+12
+13#[derive(Debug)]
+14pub(crate) enum ConnectionEventInner {
+15    /// A datagram has been received for the Connection
+16    Datagram(DatagramConnectionEvent),
+17    /// New connection identifiers have been issued for the Connection
+18    NewIdentifiers(Vec<IssuedCid>, Instant, usize, Option<Duration>),
+19}
+20
+21/// Variant of [`ConnectionEventInner`].
+22#[derive(Debug)]
+23pub(crate) struct DatagramConnectionEvent {
+24    pub(crate) now: Instant,
+25    pub(crate) network_path: FourTuple,
+26    pub(crate) path_id: PathId,
+27    pub(crate) ecn: Option<EcnCodepoint>,
+28    pub(crate) first_decode: PartialDecode,
+29    pub(crate) remaining: Option<BytesMut>,
+30}
+31
+32/// Events sent from a Connection to an Endpoint
+33#[derive(Debug)]
+34pub struct EndpointEvent(pub(crate) EndpointEventInner);
+35
+36impl EndpointEvent {
+37    /// Construct an event that indicating that a `Connection` will no longer emit events
+38    ///
+39    /// Useful for notifying an `Endpoint` that a `Connection` has been destroyed outside of the
+40    /// usual state machine flow, e.g. when being dropped by the user.
+41    pub fn drained() -> Self {
+42        Self(EndpointEventInner::Drained)
+43    }
+44
+45    /// Determine whether this is the last event a `Connection` will emit
+46    ///
+47    /// Useful for determining when connection-related event loop state can be freed.
+48    pub fn is_drained(&self) -> bool {
+49        self.0 == EndpointEventInner::Drained
+50    }
+51}
+52
+53#[derive(Clone, Debug, Eq, PartialEq)]
+54pub(crate) enum EndpointEventInner {
+55    /// The connection has been drained
+56    Drained,
+57    /// The connection has a new active reset token
+58    ///
+59    /// Whenever the connection switches to a new remote CID issued by the peer, it also
+60    /// switches the matching reset token that can be used to abort this connection. This
+61    /// event provides a new reset token for the active remote CID.
+62    ResetToken(PathId, SocketAddr, ResetToken),
+63    /// Retire the remotely issued reset token for a path, without replacing it with a new one
+64    ///
+65    /// This is like `ResetToken` above, but without replacing the `ResetToken` with a new
+66    /// one. See `ConnectionIndex::connection_reset_tokens`.
+67    RetireResetToken(PathId),
+68    /// The connection needs connection identifiers
+69    NeedIdentifiers(PathId, Instant, u64),
+70    /// Retire a locally issued CID
+71    ///
+72    /// Stop routing connection ID for this sequence number to the connection
+73    /// When `bool == true`, a new connection ID will be issued to peer
+74    RetireConnectionId(Instant, PathId, u64, bool),
+75}
+76
+77/// Protocol-level identifier for a connection.
+78///
+79/// Mainly useful for identifying this connection's packets on the wire with tools like Wireshark.
+80#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
+81#[cfg_attr(test, derive(test_strategy::Arbitrary))]
+82pub struct ConnectionId {
+83    /// length of CID
+84    #[cfg_attr(test, strategy(1u8..=MAX_CID_SIZE as u8))]
+85    len: u8,
+86    /// CID in byte array
+87    bytes: [u8; MAX_CID_SIZE],
+88}
+89
+90impl ConnectionId {
+91    /// Construct cid from byte array
+92    pub fn new(bytes: &[u8]) -> Self {
+93        debug_assert!(bytes.len() <= MAX_CID_SIZE);
+94        let mut res = Self {
+95            len: bytes.len() as u8,
+96            bytes: [0; MAX_CID_SIZE],
+97        };
+98        res.bytes[..bytes.len()].copy_from_slice(bytes);
+99        res
+100    }
+101
+102    /// Constructs cid by reading `len` bytes from a `Buf`
+103    ///
+104    /// Callers need to assure that `buf.remaining() >= len`
+105    pub fn from_buf(buf: &mut (impl Buf + ?Sized), len: usize) -> Self {
+106        debug_assert!(len <= MAX_CID_SIZE);
+107        let mut res = Self {
+108            len: len as u8,
+109            bytes: [0; MAX_CID_SIZE],
+110        };
+111        buf.copy_to_slice(&mut res[..len]);
+112        res
+113    }
+114
+115    pub(crate) fn len(&self) -> usize {
+116        self.len as usize
+117    }
+118
+119    /// Decode from long header format
+120    pub(crate) fn decode_long(buf: &mut impl Buf) -> Option<Self> {
+121        let len = buf.get::<u8>().ok()? as usize;
+122        match len > MAX_CID_SIZE || buf.remaining() < len {
+123            false => Some(Self::from_buf(buf, len)),
+124            true => None,
+125        }
+126    }
+127
+128    /// Encode in long header format
+129    pub(crate) fn encode_long(&self, buf: &mut impl BufMut) {
+130        buf.put_u8(self.len() as u8);
+131        buf.put_slice(self);
+132    }
+133}
+134
+135impl ::std::ops::Deref for ConnectionId {
+136    type Target = [u8];
+137    fn deref(&self) -> &[u8] {
+138        &self.bytes[0..self.len as usize]
+139    }
+140}
+141
+142impl ::std::ops::DerefMut for ConnectionId {
+143    fn deref_mut(&mut self) -> &mut [u8] {
+144        &mut self.bytes[0..self.len as usize]
+145    }
+146}
+147
+148impl fmt::Debug for ConnectionId {
+149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+150        self.bytes[0..self.len as usize].fmt(f)
+151    }
+152}
+153
+154impl fmt::Display for ConnectionId {
+155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+156        for byte in self.iter() {
+157            write!(f, "{byte:02x}")?;
+158        }
+159        Ok(())
+160    }
+161}
+162
+163/// Explicit congestion notification codepoint
+164#[repr(u8)]
+165#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+166pub enum EcnCodepoint {
+167    /// The ECT(0) codepoint, indicating that an endpoint is ECN-capable
+168    Ect0 = 0b10,
+169    /// The ECT(1) codepoint, indicating that an endpoint is ECN-capable
+170    Ect1 = 0b01,
+171    /// The CE codepoint, signalling that congestion was experienced
+172    Ce = 0b11,
+173}
+174
+175impl EcnCodepoint {
+176    /// Create new object from the given bits
+177    pub fn from_bits(x: u8) -> Option<Self> {
+178        use EcnCodepoint::*;
+179        Some(match x & 0b11 {
+180            0b10 => Ect0,
+181            0b01 => Ect1,
+182            0b11 => Ce,
+183            _ => {
+184                return None;
+185            }
+186        })
+187    }
+188
+189    /// Returns whether the codepoint is a CE, signalling that congestion was experienced
+190    pub fn is_ce(self) -> bool {
+191        matches!(self, Self::Ce)
+192    }
+193}
+194
+195#[derive(Debug, Copy, Clone)]
+196pub(crate) struct IssuedCid {
+197    pub(crate) path_id: PathId,
+198    pub(crate) sequence: u64,
+199    pub(crate) id: ConnectionId,
+200    pub(crate) reset_token: ResetToken,
+201}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/token.rs.html b/pr/395/docs/src/iroh_quinn_proto/token.rs.html new file mode 100644 index 000000000..ca26f97ef --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/token.rs.html @@ -0,0 +1,506 @@ +token.rs - source

iroh_quinn_proto/
token.rs

1use std::{
+2    fmt,
+3    mem::size_of,
+4    net::{IpAddr, SocketAddr},
+5};
+6
+7use bytes::{Buf, BufMut, Bytes};
+8use rand::Rng;
+9
+10use crate::{
+11    Duration, RESET_TOKEN_SIZE, ServerConfig, SystemTime, UNIX_EPOCH,
+12    coding::{BufExt, BufMutExt},
+13    crypto::{HandshakeTokenKey, HmacKey},
+14    packet::InitialHeader,
+15    shared::ConnectionId,
+16};
+17
+18/// Responsible for limiting clients' ability to reuse validation tokens
+19///
+20/// [_RFC 9000 § 8.1.4:_](https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1.4)
+21///
+22/// > Attackers could replay tokens to use servers as amplifiers in DDoS attacks. To protect
+23/// > against such attacks, servers MUST ensure that replay of tokens is prevented or limited.
+24/// > Servers SHOULD ensure that tokens sent in Retry packets are only accepted for a short time,
+25/// > as they are returned immediately by clients. Tokens that are provided in NEW_TOKEN frames
+26/// > (Section 19.7) need to be valid for longer but SHOULD NOT be accepted multiple times.
+27/// > Servers are encouraged to allow tokens to be used only once, if possible; tokens MAY include
+28/// > additional information about clients to further narrow applicability or reuse.
+29///
+30/// `TokenLog` pertains only to tokens provided in NEW_TOKEN frames.
+31pub trait TokenLog: Send + Sync {
+32    /// Record that the token was used and, ideally, return a token reuse error if the token may
+33    /// have been already used previously
+34    ///
+35    /// False negatives and false positives are both permissible. Called when a client uses an
+36    /// address validation token.
+37    ///
+38    /// Parameters:
+39    /// - `nonce`: A server-generated random unique value for the token.
+40    /// - `issued`: The time the server issued the token.
+41    /// - `lifetime`: The expiration time of address validation tokens sent via NEW_TOKEN frames,
+42    ///   as configured by [`ServerValidationTokenConfig::lifetime`][1].
+43    ///
+44    /// [1]: crate::ValidationTokenConfig::lifetime
+45    ///
+46    /// ## Security & Performance
+47    ///
+48    /// To the extent that it is possible to repeatedly trigger false negatives (returning `Ok` for
+49    /// a token which has been reused), an attacker could use the server to perform [amplification
+50    /// attacks][2]. The QUIC specification requires that this be limited, if not prevented fully.
+51    ///
+52    /// A false positive (returning `Err` for a token which has never been used) is not a security
+53    /// vulnerability; it is permissible for a `TokenLog` to always return `Err`. A false positive
+54    /// causes the token to be ignored, which may cause the transmission of some 0.5-RTT data to be
+55    /// delayed until the handshake completes, if a sufficient amount of 0.5-RTT data it sent.
+56    ///
+57    /// [2]: https://en.wikipedia.org/wiki/Denial-of-service_attack#Amplification
+58    fn check_and_insert(
+59        &self,
+60        nonce: u128,
+61        issued: SystemTime,
+62        lifetime: Duration,
+63    ) -> Result<(), TokenReuseError>;
+64}
+65
+66/// Error for when a validation token may have been reused
+67pub struct TokenReuseError;
+68
+69/// Null implementation of [`TokenLog`], which never accepts tokens
+70pub struct NoneTokenLog;
+71
+72impl TokenLog for NoneTokenLog {
+73    fn check_and_insert(&self, _: u128, _: SystemTime, _: Duration) -> Result<(), TokenReuseError> {
+74        Err(TokenReuseError)
+75    }
+76}
+77
+78/// Responsible for storing validation tokens received from servers and retrieving them for use in
+79/// subsequent connections
+80pub trait TokenStore: Send + Sync {
+81    /// Potentially store a token for later one-time use
+82    ///
+83    /// Called when a NEW_TOKEN frame is received from the server.
+84    fn insert(&self, server_name: &str, token: Bytes);
+85
+86    /// Try to find and take a token that was stored with the given server name
+87    ///
+88    /// The same token must never be returned from `take` twice, as doing so can be used to
+89    /// de-anonymize a client's traffic.
+90    ///
+91    /// Called when trying to connect to a server. It is always ok for this to return `None`.
+92    fn take(&self, server_name: &str) -> Option<Bytes>;
+93}
+94
+95/// Null implementation of [`TokenStore`], which does not store any tokens
+96pub struct NoneTokenStore;
+97
+98impl TokenStore for NoneTokenStore {
+99    fn insert(&self, _: &str, _: Bytes) {}
+100    fn take(&self, _: &str) -> Option<Bytes> {
+101        None
+102    }
+103}
+104
+105/// State in an `Incoming` determined by a token or lack thereof
+106#[derive(Debug)]
+107pub(crate) struct IncomingToken {
+108    pub(crate) retry_src_cid: Option<ConnectionId>,
+109    pub(crate) orig_dst_cid: ConnectionId,
+110    pub(crate) validated: bool,
+111}
+112
+113impl IncomingToken {
+114    /// Construct for an `Incoming` given the first packet header, or error if the connection
+115    /// cannot be established
+116    pub(crate) fn from_header(
+117        header: &InitialHeader,
+118        server_config: &ServerConfig,
+119        remote_address: SocketAddr,
+120    ) -> Result<Self, InvalidRetryTokenError> {
+121        let unvalidated = Self {
+122            retry_src_cid: None,
+123            orig_dst_cid: header.dst_cid,
+124            validated: false,
+125        };
+126
+127        // Decode token or short-circuit
+128        if header.token.is_empty() {
+129            return Ok(unvalidated);
+130        }
+131
+132        // In cases where a token cannot be decrypted/decoded, we must allow for the possibility
+133        // that this is caused not by client malfeasance, but by the token having been generated by
+134        // an incompatible endpoint, e.g. a different version or a neighbor behind the same load
+135        // balancer. In such cases we proceed as if there was no token.
+136        //
+137        // [_RFC 9000 § 8.1.3:_](https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1.3-10)
+138        //
+139        // > If the token is invalid, then the server SHOULD proceed as if the client did not have
+140        // > a validated address, including potentially sending a Retry packet.
+141        let Some(retry) = Token::decode(&*server_config.token_key, &header.token) else {
+142            return Ok(unvalidated);
+143        };
+144
+145        // Validate token, then convert into Self
+146        match retry.payload {
+147            TokenPayload::Retry {
+148                address,
+149                orig_dst_cid,
+150                issued,
+151            } => {
+152                if address != remote_address {
+153                    return Err(InvalidRetryTokenError);
+154                }
+155                if issued + server_config.retry_token_lifetime < server_config.time_source.now() {
+156                    return Err(InvalidRetryTokenError);
+157                }
+158
+159                Ok(Self {
+160                    retry_src_cid: Some(header.dst_cid),
+161                    orig_dst_cid,
+162                    validated: true,
+163                })
+164            }
+165            TokenPayload::Validation { ip, issued } => {
+166                if ip != remote_address.ip() {
+167                    return Ok(unvalidated);
+168                }
+169                if issued + server_config.validation_token.lifetime
+170                    < server_config.time_source.now()
+171                {
+172                    return Ok(unvalidated);
+173                }
+174                if server_config
+175                    .validation_token
+176                    .log
+177                    .check_and_insert(retry.nonce, issued, server_config.validation_token.lifetime)
+178                    .is_err()
+179                {
+180                    return Ok(unvalidated);
+181                }
+182
+183                Ok(Self {
+184                    retry_src_cid: None,
+185                    orig_dst_cid: header.dst_cid,
+186                    validated: true,
+187                })
+188            }
+189        }
+190    }
+191}
+192
+193/// Error for a token being unambiguously from a Retry packet, and not valid
+194///
+195/// The connection cannot be established.
+196pub(crate) struct InvalidRetryTokenError;
+197
+198/// Retry or validation token
+199pub(crate) struct Token {
+200    /// Content that is encrypted from the client
+201    pub(crate) payload: TokenPayload,
+202    /// Randomly generated value, which must be unique, and is visible to the client
+203    nonce: u128,
+204}
+205
+206impl Token {
+207    /// Construct with newly sampled randomness
+208    pub(crate) fn new(payload: TokenPayload, rng: &mut impl Rng) -> Self {
+209        Self {
+210            nonce: rng.random(),
+211            payload,
+212        }
+213    }
+214
+215    /// Encode and encrypt
+216    pub(crate) fn encode(&self, key: &dyn HandshakeTokenKey) -> Vec<u8> {
+217        let mut buf = Vec::new();
+218
+219        // Encode payload
+220        match self.payload {
+221            TokenPayload::Retry {
+222                address,
+223                orig_dst_cid,
+224                issued,
+225            } => {
+226                buf.put_u8(TokenType::Retry as u8);
+227                encode_addr(&mut buf, address);
+228                orig_dst_cid.encode_long(&mut buf);
+229                encode_unix_secs(&mut buf, issued);
+230            }
+231            TokenPayload::Validation { ip, issued } => {
+232                buf.put_u8(TokenType::Validation as u8);
+233                encode_ip(&mut buf, ip);
+234                encode_unix_secs(&mut buf, issued);
+235            }
+236        }
+237
+238        // Encrypt
+239        let aead_key = key.aead_from_hkdf(&self.nonce.to_le_bytes());
+240        aead_key.seal(&mut buf, &[]).unwrap();
+241        buf.extend(&self.nonce.to_le_bytes());
+242
+243        buf
+244    }
+245
+246    /// Decode and decrypt
+247    fn decode(key: &dyn HandshakeTokenKey, raw_token_bytes: &[u8]) -> Option<Self> {
+248        // Decrypt
+249
+250        let nonce_slice_start = raw_token_bytes.len().checked_sub(size_of::<u128>())?;
+251        let (sealed_token, nonce_bytes) = raw_token_bytes.split_at_checked(nonce_slice_start)?;
+252
+253        let nonce = u128::from_le_bytes(nonce_bytes.try_into().unwrap());
+254
+255        let aead_key = key.aead_from_hkdf(nonce_bytes);
+256        let mut sealed_token = sealed_token.to_vec();
+257        let data = aead_key.open(&mut sealed_token, &[]).ok()?;
+258
+259        // Decode payload
+260        let mut reader = &data[..];
+261        let payload = match TokenType::from_byte((&mut reader).get::<u8>().ok()?)? {
+262            TokenType::Retry => TokenPayload::Retry {
+263                address: decode_addr(&mut reader)?,
+264                orig_dst_cid: ConnectionId::decode_long(&mut reader)?,
+265                issued: decode_unix_secs(&mut reader)?,
+266            },
+267            TokenType::Validation => TokenPayload::Validation {
+268                ip: decode_ip(&mut reader)?,
+269                issued: decode_unix_secs(&mut reader)?,
+270            },
+271        };
+272
+273        if !reader.is_empty() {
+274            // Consider extra bytes a decoding error (it may be from an incompatible endpoint)
+275            return None;
+276        }
+277
+278        Some(Self { nonce, payload })
+279    }
+280}
+281
+282/// Content of a [`Token`] that is encrypted from the client
+283pub(crate) enum TokenPayload {
+284    /// Token originating from a Retry packet
+285    Retry {
+286        /// The client's address
+287        address: SocketAddr,
+288        /// The destination connection ID set in the very first packet from the client
+289        orig_dst_cid: ConnectionId,
+290        /// The time at which this token was issued
+291        issued: SystemTime,
+292    },
+293    /// Token originating from a NEW_TOKEN frame
+294    Validation {
+295        /// The client's IP address (its port is likely to change between sessions)
+296        ip: IpAddr,
+297        /// The time at which this token was issued
+298        issued: SystemTime,
+299    },
+300}
+301
+302/// Variant tag for a [`TokenPayload`]
+303#[derive(Copy, Clone)]
+304#[repr(u8)]
+305enum TokenType {
+306    Retry = 0,
+307    Validation = 1,
+308}
+309
+310impl TokenType {
+311    fn from_byte(n: u8) -> Option<Self> {
+312        use TokenType::*;
+313        [Retry, Validation].into_iter().find(|ty| *ty as u8 == n)
+314    }
+315}
+316
+317fn encode_addr(buf: &mut Vec<u8>, address: SocketAddr) {
+318    encode_ip(buf, address.ip());
+319    buf.put_u16(address.port());
+320}
+321
+322fn decode_addr<B: Buf>(buf: &mut B) -> Option<SocketAddr> {
+323    let ip = decode_ip(buf)?;
+324    let port = buf.get().ok()?;
+325    Some(SocketAddr::new(ip, port))
+326}
+327
+328fn encode_ip(buf: &mut Vec<u8>, ip: IpAddr) {
+329    match ip {
+330        IpAddr::V4(x) => {
+331            buf.put_u8(0);
+332            buf.put_slice(&x.octets());
+333        }
+334        IpAddr::V6(x) => {
+335            buf.put_u8(1);
+336            buf.put_slice(&x.octets());
+337        }
+338    }
+339}
+340
+341fn decode_ip<B: Buf>(buf: &mut B) -> Option<IpAddr> {
+342    match buf.get::<u8>().ok()? {
+343        0 => buf.get().ok().map(IpAddr::V4),
+344        1 => buf.get().ok().map(IpAddr::V6),
+345        _ => None,
+346    }
+347}
+348
+349fn encode_unix_secs(buf: &mut Vec<u8>, time: SystemTime) {
+350    buf.write::<u64>(
+351        time.duration_since(UNIX_EPOCH)
+352            .unwrap_or_default()
+353            .as_secs(),
+354    );
+355}
+356
+357fn decode_unix_secs<B: Buf>(buf: &mut B) -> Option<SystemTime> {
+358    Some(UNIX_EPOCH + Duration::from_secs(buf.get::<u64>().ok()?))
+359}
+360
+361/// Stateless reset token
+362///
+363/// Used for an endpoint to securely communicate that it has lost state for a connection.
+364#[allow(clippy::derived_hash_with_manual_eq)] // Custom PartialEq impl matches derived semantics
+365#[derive(Debug, Copy, Clone, Hash)]
+366pub(crate) struct ResetToken([u8; RESET_TOKEN_SIZE]);
+367
+368impl ResetToken {
+369    pub(crate) fn new(key: &dyn HmacKey, id: ConnectionId) -> Self {
+370        let mut signature = vec![0; key.signature_len()];
+371        key.sign(&id, &mut signature);
+372        // TODO: Server ID??
+373        let mut result = [0; RESET_TOKEN_SIZE];
+374        result.copy_from_slice(&signature[..RESET_TOKEN_SIZE]);
+375        result.into()
+376    }
+377}
+378
+379impl PartialEq for ResetToken {
+380    fn eq(&self, other: &Self) -> bool {
+381        crate::constant_time::eq(&self.0, &other.0)
+382    }
+383}
+384
+385impl Eq for ResetToken {}
+386
+387impl From<[u8; RESET_TOKEN_SIZE]> for ResetToken {
+388    fn from(x: [u8; RESET_TOKEN_SIZE]) -> Self {
+389        Self(x)
+390    }
+391}
+392
+393impl std::ops::Deref for ResetToken {
+394    type Target = [u8];
+395    fn deref(&self) -> &[u8] {
+396        &self.0
+397    }
+398}
+399
+400impl fmt::Display for ResetToken {
+401    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+402        for byte in self.iter() {
+403            write!(f, "{byte:02x}")?;
+404        }
+405        Ok(())
+406    }
+407}
+408
+409#[cfg(all(test, any(feature = "aws-lc-rs", feature = "ring")))]
+410mod test {
+411    use super::*;
+412    #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+413    use aws_lc_rs::hkdf;
+414    use rand::prelude::*;
+415    #[cfg(feature = "ring")]
+416    use ring::hkdf;
+417
+418    fn token_round_trip(payload: TokenPayload) -> TokenPayload {
+419        let rng = &mut rand::rng();
+420        let token = Token::new(payload, rng);
+421        let mut master_key = [0; 64];
+422        rng.fill_bytes(&mut master_key);
+423        let prk = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key);
+424        let encoded = token.encode(&prk);
+425        let decoded = Token::decode(&prk, &encoded).expect("token didn't decrypt / decode");
+426        assert_eq!(token.nonce, decoded.nonce);
+427        decoded.payload
+428    }
+429
+430    #[test]
+431    fn retry_token_sanity() {
+432        use crate::MAX_CID_SIZE;
+433        use crate::cid_generator::{ConnectionIdGenerator, RandomConnectionIdGenerator};
+434        use crate::{Duration, UNIX_EPOCH};
+435
+436        use std::net::Ipv6Addr;
+437
+438        let address_1 = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 4433);
+439        let orig_dst_cid_1 = RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid();
+440        let issued_1 = UNIX_EPOCH + Duration::from_secs(42); // Fractional seconds would be lost
+441        let payload_1 = TokenPayload::Retry {
+442            address: address_1,
+443            orig_dst_cid: orig_dst_cid_1,
+444            issued: issued_1,
+445        };
+446        let TokenPayload::Retry {
+447            address: address_2,
+448            orig_dst_cid: orig_dst_cid_2,
+449            issued: issued_2,
+450        } = token_round_trip(payload_1)
+451        else {
+452            panic!("token decoded as wrong variant");
+453        };
+454
+455        assert_eq!(address_1, address_2);
+456        assert_eq!(orig_dst_cid_1, orig_dst_cid_2);
+457        assert_eq!(issued_1, issued_2);
+458    }
+459
+460    #[test]
+461    fn validation_token_sanity() {
+462        use crate::{Duration, UNIX_EPOCH};
+463
+464        use std::net::Ipv6Addr;
+465
+466        let ip_1 = Ipv6Addr::LOCALHOST.into();
+467        let issued_1 = UNIX_EPOCH + Duration::from_secs(42); // Fractional seconds would be lost
+468
+469        let payload_1 = TokenPayload::Validation {
+470            ip: ip_1,
+471            issued: issued_1,
+472        };
+473        let TokenPayload::Validation {
+474            ip: ip_2,
+475            issued: issued_2,
+476        } = token_round_trip(payload_1)
+477        else {
+478            panic!("token decoded as wrong variant");
+479        };
+480
+481        assert_eq!(ip_1, ip_2);
+482        assert_eq!(issued_1, issued_2);
+483    }
+484
+485    #[test]
+486    fn invalid_token_returns_err() {
+487        use super::*;
+488        use rand::RngCore;
+489
+490        let rng = &mut rand::rng();
+491
+492        let mut master_key = [0; 64];
+493        rng.fill_bytes(&mut master_key);
+494
+495        let prk = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key);
+496
+497        let mut invalid_token = Vec::new();
+498
+499        let mut random_data = [0; 32];
+500        rand::rng().fill_bytes(&mut random_data);
+501        invalid_token.put_slice(&random_data);
+502
+503        // Assert: garbage sealed data returns err
+504        assert!(Token::decode(&prk, &invalid_token).is_none());
+505    }
+506}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/token_memory_cache.rs.html b/pr/395/docs/src/iroh_quinn_proto/token_memory_cache.rs.html new file mode 100644 index 000000000..45007f185 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/token_memory_cache.rs.html @@ -0,0 +1,247 @@ +token_memory_cache.rs - source

iroh_quinn_proto/
token_memory_cache.rs

1//! Storing tokens sent from servers in NEW_TOKEN frames and using them in subsequent connections
+2
+3use std::{
+4    collections::{HashMap, VecDeque, hash_map},
+5    sync::{Arc, Mutex},
+6};
+7
+8use bytes::Bytes;
+9use lru_slab::LruSlab;
+10use tracing::trace;
+11
+12use crate::token::TokenStore;
+13
+14/// `TokenStore` implementation that stores up to `N` tokens per server name for up to a
+15/// limited number of server names, in-memory
+16#[derive(Debug)]
+17pub struct TokenMemoryCache(Mutex<State>);
+18
+19impl TokenMemoryCache {
+20    /// Construct empty
+21    pub fn new(max_server_names: u32, max_tokens_per_server: usize) -> Self {
+22        Self(Mutex::new(State::new(
+23            max_server_names,
+24            max_tokens_per_server,
+25        )))
+26    }
+27}
+28
+29impl TokenStore for TokenMemoryCache {
+30    fn insert(&self, server_name: &str, token: Bytes) {
+31        trace!(%server_name, "storing token");
+32        self.0.lock().unwrap().store(server_name, token)
+33    }
+34
+35    fn take(&self, server_name: &str) -> Option<Bytes> {
+36        let token = self.0.lock().unwrap().take(server_name);
+37        trace!(%server_name, found=%token.is_some(), "taking token");
+38        token
+39    }
+40}
+41
+42/// Defaults to a maximum of 256 servers and 2 tokens per server
+43impl Default for TokenMemoryCache {
+44    fn default() -> Self {
+45        Self::new(256, 2)
+46    }
+47}
+48
+49/// Lockable inner state of `TokenMemoryCache`
+50#[derive(Debug)]
+51struct State {
+52    max_server_names: u32,
+53    max_tokens_per_server: usize,
+54    // map from server name to index in lru
+55    lookup: HashMap<Arc<str>, u32>,
+56    lru: LruSlab<CacheEntry>,
+57}
+58
+59impl State {
+60    fn new(max_server_names: u32, max_tokens_per_server: usize) -> Self {
+61        Self {
+62            max_server_names,
+63            max_tokens_per_server,
+64            lookup: HashMap::new(),
+65            lru: LruSlab::default(),
+66        }
+67    }
+68
+69    fn store(&mut self, server_name: &str, token: Bytes) {
+70        if self.max_server_names == 0 {
+71            // the rest of this method assumes that we can always insert a new entry so long as
+72            // we're willing to evict a pre-existing entry. thus, an entry limit of 0 is an edge
+73            // case we must short-circuit on now.
+74            return;
+75        }
+76        if self.max_tokens_per_server == 0 {
+77            // similarly to above, the rest of this method assumes that we can always push a new
+78            // token to a queue so long as we're willing to evict a pre-existing token, so we
+79            // short-circuit on the edge case of a token limit of 0.
+80            return;
+81        }
+82
+83        let server_name = Arc::<str>::from(server_name);
+84        match self.lookup.entry(server_name.clone()) {
+85            hash_map::Entry::Occupied(hmap_entry) => {
+86                // key already exists, push the new token to its token queue
+87                let tokens = &mut self.lru.get_mut(*hmap_entry.get()).tokens;
+88                if tokens.len() >= self.max_tokens_per_server {
+89                    debug_assert!(tokens.len() == self.max_tokens_per_server);
+90                    tokens.pop_front().unwrap();
+91                }
+92                tokens.push_back(token);
+93            }
+94            hash_map::Entry::Vacant(hmap_entry) => {
+95                // key does not yet exist, create a new one, evicting the oldest if necessary
+96                let removed_key = if self.lru.len() >= self.max_server_names {
+97                    // unwrap safety: max_server_names is > 0, so there's at least one entry, so
+98                    //                lru() is some
+99                    Some(self.lru.remove(self.lru.lru().unwrap()).server_name)
+100                } else {
+101                    None
+102                };
+103
+104                hmap_entry.insert(self.lru.insert(CacheEntry::new(server_name, token)));
+105
+106                // for borrowing reasons, we must defer removing the evicted hmap entry to here
+107                if let Some(removed_slot) = removed_key {
+108                    let removed = self.lookup.remove(&removed_slot);
+109                    debug_assert!(removed.is_some());
+110                }
+111            }
+112        };
+113    }
+114
+115    fn take(&mut self, server_name: &str) -> Option<Bytes> {
+116        let slab_key = *self.lookup.get(server_name)?;
+117
+118        // pop from entry's token queue
+119        let entry = self.lru.get_mut(slab_key);
+120        // unwrap safety: we never leave tokens empty
+121        let token = entry.tokens.pop_front().unwrap();
+122
+123        if entry.tokens.is_empty() {
+124            // token stack emptied, remove entry
+125            self.lru.remove(slab_key);
+126            self.lookup.remove(server_name);
+127        }
+128
+129        Some(token)
+130    }
+131}
+132
+133/// Cache entry within `TokenMemoryCache`'s LRU slab
+134#[derive(Debug)]
+135struct CacheEntry {
+136    server_name: Arc<str>,
+137    // invariant: tokens is never empty
+138    tokens: VecDeque<Bytes>,
+139}
+140
+141impl CacheEntry {
+142    /// Construct with a single token
+143    fn new(server_name: Arc<str>, token: Bytes) -> Self {
+144        let mut tokens = VecDeque::new();
+145        tokens.push_back(token);
+146        Self {
+147            server_name,
+148            tokens,
+149        }
+150    }
+151}
+152
+153#[cfg(test)]
+154mod tests {
+155    use std::collections::VecDeque;
+156
+157    use super::*;
+158    use rand::prelude::*;
+159    use rand_pcg::Pcg32;
+160    use tracing::info;
+161
+162    fn new_rng() -> impl Rng {
+163        Pcg32::from_seed(0xdeadbeefdeadbeefdeadbeefdeadbeefu128.to_le_bytes())
+164    }
+165
+166    #[test]
+167    fn cache_test() {
+168        let mut rng = new_rng();
+169        const N: usize = 2;
+170
+171        for _ in 0..10 {
+172            let mut cache_1: Vec<(u32, VecDeque<Bytes>)> = Vec::new(); // keep it sorted oldest to newest
+173            let cache_2 = TokenMemoryCache::new(20, 2);
+174
+175            for i in 0..200 {
+176                let server_name = rng.random::<u32>() % 10;
+177                if rng.random_bool(0.666) {
+178                    // store
+179                    let token = Bytes::from(vec![i]);
+180                    info!("STORE {server_name} {token:?}");
+181                    if let Some((j, _)) = cache_1
+182                        .iter()
+183                        .enumerate()
+184                        .find(|&(_, &(server_name_2, _))| server_name_2 == server_name)
+185                    {
+186                        let (_, mut queue) = cache_1.remove(j);
+187                        queue.push_back(token.clone());
+188                        if queue.len() > N {
+189                            queue.pop_front();
+190                        }
+191                        cache_1.push((server_name, queue));
+192                    } else {
+193                        let mut queue = VecDeque::new();
+194                        queue.push_back(token.clone());
+195                        cache_1.push((server_name, queue));
+196                        if cache_1.len() > 20 {
+197                            cache_1.remove(0);
+198                        }
+199                    }
+200                    cache_2.insert(&server_name.to_string(), token);
+201                } else {
+202                    // take
+203                    info!("TAKE {server_name}");
+204                    let expecting = cache_1
+205                        .iter()
+206                        .enumerate()
+207                        .find(|&(_, &(server_name_2, _))| server_name_2 == server_name)
+208                        .map(|(j, _)| j)
+209                        .map(|j| {
+210                            let (_, mut queue) = cache_1.remove(j);
+211                            let token = queue.pop_front().unwrap();
+212                            if !queue.is_empty() {
+213                                cache_1.push((server_name, queue));
+214                            }
+215                            token
+216                        });
+217                    info!("EXPECTING {expecting:?}");
+218                    assert_eq!(cache_2.take(&server_name.to_string()), expecting);
+219                }
+220            }
+221        }
+222    }
+223
+224    #[test]
+225    fn zero_max_server_names() {
+226        // test that this edge case doesn't panic
+227        let cache = TokenMemoryCache::new(0, 2);
+228        for i in 0..10 {
+229            cache.insert(&i.to_string(), Bytes::from(vec![i]));
+230            for j in 0..10 {
+231                assert!(cache.take(&j.to_string()).is_none());
+232            }
+233        }
+234    }
+235
+236    #[test]
+237    fn zero_queue_length() {
+238        // test that this edge case doesn't panic
+239        let cache = TokenMemoryCache::new(256, 0);
+240        for i in 0..10 {
+241            cache.insert(&i.to_string(), Bytes::from(vec![i]));
+242            for j in 0..10 {
+243                assert!(cache.take(&j.to_string()).is_none());
+244            }
+245        }
+246    }
+247}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/transport_error.rs.html b/pr/395/docs/src/iroh_quinn_proto/transport_error.rs.html new file mode 100644 index 000000000..806b6eaa5 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/transport_error.rs.html @@ -0,0 +1,166 @@ +transport_error.rs - source

iroh_quinn_proto/
transport_error.rs

1use std::{fmt, sync::Arc};
+2
+3use bytes::{Buf, BufMut};
+4
+5use crate::{
+6    VarInt,
+7    coding::{self, BufExt, BufMutExt, Decodable, Encodable},
+8    frame::MaybeFrame,
+9};
+10
+11/// Transport-level errors occur when a peer violates the protocol specification
+12///
+13/// # Note
+14///
+15/// The `PartialEq` implementation for this type performs comparison on the `code` field only
+16#[derive(Debug, Clone)]
+17#[non_exhaustive]
+18pub struct Error {
+19    /// Type of error
+20    pub code: Code,
+21    /// Frame type that triggered the error
+22    pub frame: MaybeFrame,
+23    /// Human-readable explanation of the reason
+24    pub reason: String,
+25    /// An underlying crypto (e.g. TLS) layer error
+26    pub crypto: Option<Arc<dyn std::error::Error + Send + Sync>>,
+27}
+28
+29impl Error {
+30    /// Construct an error with a code and a reason
+31    pub fn new(code: Code, reason: String) -> Self {
+32        Self {
+33            code,
+34            frame: MaybeFrame::None,
+35            reason,
+36            crypto: None,
+37        }
+38    }
+39}
+40
+41impl PartialEq for Error {
+42    fn eq(&self, other: &Self) -> bool {
+43        self.code == other.code
+44    }
+45}
+46
+47impl Eq for Error {}
+48
+49impl fmt::Display for Error {
+50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+51        self.code.fmt(f)?;
+52        if self.frame != MaybeFrame::None {
+53            write!(f, " in {}", self.frame)?;
+54        }
+55        if !self.reason.is_empty() {
+56            write!(f, ": {}", self.reason)?;
+57        }
+58        Ok(())
+59    }
+60}
+61
+62impl std::error::Error for Error {}
+63
+64/// Transport-level error code
+65#[derive(Copy, Clone, Eq, PartialEq)]
+66#[cfg_attr(test, derive(test_strategy::Arbitrary))]
+67pub struct Code(#[cfg_attr(test, strategy(crate::varint::varint_u64()))] u64);
+68
+69impl Code {
+70    /// Create QUIC error code from TLS alert code
+71    pub fn crypto(code: u8) -> Self {
+72        Self(0x100 | u64::from(code))
+73    }
+74}
+75
+76impl Decodable for Code {
+77    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+78        Ok(Self(buf.get_var()?))
+79    }
+80}
+81
+82impl Encodable for Code {
+83    fn encode<B: BufMut>(&self, buf: &mut B) {
+84        buf.write_var(self.0)
+85    }
+86}
+87
+88impl From<Code> for u64 {
+89    fn from(x: Code) -> Self {
+90        x.0
+91    }
+92}
+93
+94impl From<VarInt> for Code {
+95    fn from(value: VarInt) -> Self {
+96        Self(value.0)
+97    }
+98}
+99
+100impl From<Code> for VarInt {
+101    fn from(value: Code) -> Self {
+102        Self(value.0)
+103    }
+104}
+105
+106macro_rules! errors {
+107    {$($name:ident($val:expr) $desc:expr;)*} => {
+108        #[allow(non_snake_case, unused)]
+109        impl Error {
+110            $(
+111            pub(crate) fn $name<T>(reason: T) -> Self where T: Into<String> {
+112                Self::new(Code::$name, reason.into())
+113            }
+114            )*
+115        }
+116
+117        impl Code {
+118            $(#[doc = $desc] pub const $name: Self = Code($val);)*
+119        }
+120
+121        impl fmt::Debug for Code {
+122            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+123                match self.0 {
+124                    $($val => f.write_str(stringify!($name)),)*
+125                    x if (0x100..0x200).contains(&x) => write!(f, "Code::crypto({:02x})", self.0 as u8),
+126                    _ => write!(f, "Code({:x})", self.0),
+127                }
+128            }
+129        }
+130
+131        impl fmt::Display for Code {
+132            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+133                match self.0 {
+134                    $($val => f.write_str($desc),)*
+135                    // We're trying to be abstract over the crypto protocol, so human-readable descriptions here is tricky.
+136                    _ if self.0 >= 0x100 && self.0 < 0x200 => write!(f, "the cryptographic handshake failed: error {}", self.0 & 0xFF),
+137                    _ => f.write_str("unknown error"),
+138                }
+139            }
+140        }
+141    }
+142}
+143
+144errors! {
+145    NO_ERROR(0x0) "the connection is being closed abruptly in the absence of any error";
+146    INTERNAL_ERROR(0x1) "the endpoint encountered an internal error and cannot continue with the connection";
+147    CONNECTION_REFUSED(0x2) "the server refused to accept a new connection";
+148    FLOW_CONTROL_ERROR(0x3) "received more data than permitted in advertised data limits";
+149    STREAM_LIMIT_ERROR(0x4) "received a frame for a stream identifier that exceeded advertised the stream limit for the corresponding stream type";
+150    STREAM_STATE_ERROR(0x5) "received a frame for a stream that was not in a state that permitted that frame";
+151    FINAL_SIZE_ERROR(0x6) "received a STREAM frame or a RESET_STREAM frame containing a different final size to the one already established";
+152    FRAME_ENCODING_ERROR(0x7) "received a frame that was badly formatted";
+153    TRANSPORT_PARAMETER_ERROR(0x8) "received transport parameters that were badly formatted, included an invalid value, was absent even though it is mandatory, was present though it is forbidden, or is otherwise in error";
+154    CONNECTION_ID_LIMIT_ERROR(0x9) "the number of connection IDs provided by the peer exceeds the advertised active_connection_id_limit";
+155    PROTOCOL_VIOLATION(0xA) "detected an error with protocol compliance that was not covered by more specific error codes";
+156    INVALID_TOKEN(0xB) "received an invalid Retry Token in a client Initial";
+157    APPLICATION_ERROR(0xC) "the application or application protocol caused the connection to be closed during the handshake";
+158    CRYPTO_BUFFER_EXCEEDED(0xD) "received more data in CRYPTO frames than can be buffered";
+159    KEY_UPDATE_ERROR(0xE) "key update error";
+160    AEAD_LIMIT_REACHED(0xF) "the endpoint has reached the confidentiality or integrity limit for the AEAD algorithm";
+161    NO_VIABLE_PATH(0x10) "no viable network path exists";
+162    APPLICATION_ABANDON_PATH(0x3e) "Path abandoned at the application's request";
+163    PATH_RESOURCE_LIMIT_REACHED(0x3e75) "Path abandoned due to resource limitations in the transport";
+164    PATH_UNSTABLE_OR_POOR(0x3e76) "Path abandoned due to unstable interfaces";
+165    NO_CID_AVAILABLE_FOR_PATH(0x3e77) "Path abandoned due to no available connection IDs for the path";
+166}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/transport_parameters.rs.html b/pr/395/docs/src/iroh_quinn_proto/transport_parameters.rs.html new file mode 100644 index 000000000..1af5de8a6 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/transport_parameters.rs.html @@ -0,0 +1,979 @@ +transport_parameters.rs - source

iroh_quinn_proto/
transport_parameters.rs

1//! QUIC connection transport parameters
+2//!
+3//! The `TransportParameters` type is used to represent the transport parameters
+4//! negotiated by peers while establishing a QUIC connection. This process
+5//! happens as part of the establishment of the TLS session. As such, the types
+6//! contained in this modules should generally only be referred to by custom
+7//! implementations of the `crypto::Session` trait.
+8
+9use std::{
+10    convert::TryFrom,
+11    net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
+12    num::NonZeroU8,
+13};
+14
+15use bytes::{Buf, BufMut};
+16use rand::{Rng as _, RngCore, seq::SliceRandom as _};
+17use thiserror::Error;
+18
+19use crate::{
+20    LOCAL_CID_COUNT, MAX_CID_SIZE, MAX_STREAM_COUNT, RESET_TOKEN_SIZE, ResetToken, Side,
+21    TIMER_GRANULARITY, TransportError, VarInt, address_discovery,
+22    cid_generator::ConnectionIdGenerator,
+23    cid_queue::CidQueue,
+24    coding::{BufExt, BufMutExt, UnexpectedEnd},
+25    config::{EndpointConfig, ServerConfig, TransportConfig},
+26    connection::PathId,
+27    shared::ConnectionId,
+28};
+29
+30// Apply a given macro to a list of all the transport parameters having integer types, along with
+31// their codes and default values. Using this helps us avoid error-prone duplication of the
+32// contained information across decoding, encoding, and the `Default` impl. Whenever we want to do
+33// something with transport parameters, we'll handle the bulk of cases by writing a macro that
+34// takes a list of arguments in this form, then passing it to this macro.
+35macro_rules! apply_params {
+36    ($macro:ident) => {
+37        $macro! {
+38            // #[doc] name (id) = default,
+39            /// Milliseconds, disabled if zero
+40            max_idle_timeout(MaxIdleTimeout) = 0,
+41            /// Limits the size of UDP payloads that the endpoint is willing to receive
+42            max_udp_payload_size(MaxUdpPayloadSize) = 65527,
+43
+44            /// Initial value for the maximum amount of data that can be sent on the connection
+45            initial_max_data(InitialMaxData) = 0,
+46            /// Initial flow control limit for locally-initiated bidirectional streams
+47            initial_max_stream_data_bidi_local(InitialMaxStreamDataBidiLocal) = 0,
+48            /// Initial flow control limit for peer-initiated bidirectional streams
+49            initial_max_stream_data_bidi_remote(InitialMaxStreamDataBidiRemote) = 0,
+50            /// Initial flow control limit for unidirectional streams
+51            initial_max_stream_data_uni(InitialMaxStreamDataUni) = 0,
+52
+53            /// Initial maximum number of bidirectional streams the peer may initiate
+54            initial_max_streams_bidi(InitialMaxStreamsBidi) = 0,
+55            /// Initial maximum number of unidirectional streams the peer may initiate
+56            initial_max_streams_uni(InitialMaxStreamsUni) = 0,
+57
+58            /// Exponent used to decode the ACK Delay field in the ACK frame
+59            ack_delay_exponent(AckDelayExponent) = 3,
+60            /// Maximum amount of time in milliseconds by which the endpoint will delay sending
+61            /// acknowledgments
+62            max_ack_delay(MaxAckDelay) = 25,
+63            /// Maximum number of connection IDs from the peer that an endpoint is willing to store
+64            active_connection_id_limit(ActiveConnectionIdLimit) = 2,
+65        }
+66    };
+67}
+68
+69macro_rules! make_struct {
+70    {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
+71        /// Transport parameters used to negotiate connection-level preferences between peers
+72        #[derive(Debug, Copy, Clone, Eq, PartialEq)]
+73        pub struct TransportParameters {
+74            $($(#[$doc])* pub(crate) $name : VarInt,)*
+75
+76            /// Does the endpoint support active connection migration
+77            pub(crate) disable_active_migration: bool,
+78            /// Maximum size for datagram frames
+79            pub(crate) max_datagram_frame_size: Option<VarInt>,
+80            /// The value that the endpoint included in the Source Connection ID field of the first
+81            /// Initial packet it sends for the connection
+82            pub(crate) initial_src_cid: Option<ConnectionId>,
+83            /// The endpoint is willing to receive QUIC packets containing any value for the fixed
+84            /// bit
+85            pub(crate) grease_quic_bit: bool,
+86
+87            /// Minimum amount of time in microseconds by which the endpoint is able to delay
+88            /// sending acknowledgments
+89            ///
+90            /// If a value is provided, it implies that the endpoint supports QUIC Acknowledgement
+91            /// Frequency
+92            pub(crate) min_ack_delay: Option<VarInt>,
+93
+94            // Server-only
+95            /// The value of the Destination Connection ID field from the first Initial packet sent
+96            /// by the client
+97            pub(crate) original_dst_cid: Option<ConnectionId>,
+98            /// The value that the server included in the Source Connection ID field of a Retry
+99            /// packet
+100            pub(crate) retry_src_cid: Option<ConnectionId>,
+101            /// Token used by the client to verify a stateless reset from the server
+102            pub(crate) stateless_reset_token: Option<ResetToken>,
+103            /// The server's preferred address for communication after handshake completion
+104            pub(crate) preferred_address: Option<PreferredAddress>,
+105
+106            /// The randomly generated reserved transport parameter to sustain future extensibility
+107            /// of transport parameter extensions.
+108            /// When present, it is included during serialization but ignored during deserialization.
+109            pub(crate) grease_transport_parameter: Option<ReservedTransportParameter>,
+110
+111            /// Defines the order in which transport parameters are serialized.
+112            ///
+113            /// This field is initialized only for outgoing `TransportParameters` instances and
+114            /// is set to `None` for `TransportParameters` received from a peer.
+115            pub(crate) write_order: Option<[u8; TransportParameterId::SUPPORTED.len()]>,
+116
+117            /// The role of this peer in address discovery, if any.
+118            pub(crate) address_discovery_role: address_discovery::Role,
+119
+120            /// Multipath extension
+121            pub(crate) initial_max_path_id: Option<PathId>,
+122
+123            /// Nat traversal draft
+124            pub max_remote_nat_traversal_addresses: Option<NonZeroU8>,
+125        }
+126
+127        // We deliberately don't implement the `Default` trait, since that would be public, and
+128        // downstream crates should never construct `TransportParameters` except by decoding those
+129        // supplied by a peer.
+130        impl TransportParameters {
+131            /// Standard defaults, used if the peer does not supply a given parameter.
+132            pub(crate) fn default() -> Self {
+133                Self {
+134                    $($name: VarInt::from_u32($default),)*
+135
+136                    disable_active_migration: false,
+137                    max_datagram_frame_size: None,
+138                    initial_src_cid: None,
+139                    grease_quic_bit: false,
+140                    min_ack_delay: None,
+141
+142                    original_dst_cid: None,
+143                    retry_src_cid: None,
+144                    stateless_reset_token: None,
+145                    preferred_address: None,
+146                    grease_transport_parameter: None,
+147                    write_order: None,
+148                    address_discovery_role: address_discovery::Role::Disabled,
+149                    initial_max_path_id: None,
+150                    max_remote_nat_traversal_addresses: None,
+151                }
+152            }
+153        }
+154    }
+155}
+156
+157apply_params!(make_struct);
+158
+159impl TransportParameters {
+160    pub(crate) fn new(
+161        config: &TransportConfig,
+162        endpoint_config: &EndpointConfig,
+163        cid_gen: &dyn ConnectionIdGenerator,
+164        initial_src_cid: ConnectionId,
+165        server_config: Option<&ServerConfig>,
+166        rng: &mut impl RngCore,
+167    ) -> Self {
+168        Self {
+169            initial_src_cid: Some(initial_src_cid),
+170            initial_max_streams_bidi: config.max_concurrent_bidi_streams,
+171            initial_max_streams_uni: config.max_concurrent_uni_streams,
+172            initial_max_data: config.receive_window,
+173            initial_max_stream_data_bidi_local: config.stream_receive_window,
+174            initial_max_stream_data_bidi_remote: config.stream_receive_window,
+175            initial_max_stream_data_uni: config.stream_receive_window,
+176            max_udp_payload_size: endpoint_config.max_udp_payload_size,
+177            max_idle_timeout: config.max_idle_timeout.unwrap_or(VarInt(0)),
+178            disable_active_migration: server_config.is_some_and(|c| !c.migration),
+179            active_connection_id_limit: if cid_gen.cid_len() == 0 {
+180                2 // i.e. default, i.e. unsent
+181            } else {
+182                CidQueue::LEN as u32
+183            }
+184            .into(),
+185            max_datagram_frame_size: config
+186                .datagram_receive_buffer_size
+187                .map(|x| (x.min(u16::MAX.into()) as u16).into()),
+188            grease_quic_bit: endpoint_config.grease_quic_bit,
+189            min_ack_delay: Some(
+190                VarInt::from_u64(u64::try_from(TIMER_GRANULARITY.as_micros()).unwrap()).unwrap(),
+191            ),
+192            grease_transport_parameter: Some(ReservedTransportParameter::random(rng)),
+193            write_order: Some({
+194                let mut order = std::array::from_fn(|i| i as u8);
+195                order.shuffle(rng);
+196                order
+197            }),
+198            address_discovery_role: config.address_discovery_role,
+199            initial_max_path_id: config.get_initial_max_path_id(),
+200            max_remote_nat_traversal_addresses: config.max_remote_nat_traversal_addresses,
+201            ..Self::default()
+202        }
+203    }
+204
+205    /// Check that these parameters are legal when resuming from
+206    /// certain cached parameters
+207    pub(crate) fn validate_resumption_from(&self, cached: &Self) -> Result<(), TransportError> {
+208        if cached.active_connection_id_limit > self.active_connection_id_limit
+209            || cached.initial_max_data > self.initial_max_data
+210            || cached.initial_max_stream_data_bidi_local > self.initial_max_stream_data_bidi_local
+211            || cached.initial_max_stream_data_bidi_remote > self.initial_max_stream_data_bidi_remote
+212            || cached.initial_max_stream_data_uni > self.initial_max_stream_data_uni
+213            || cached.initial_max_streams_bidi > self.initial_max_streams_bidi
+214            || cached.initial_max_streams_uni > self.initial_max_streams_uni
+215            || cached.max_datagram_frame_size > self.max_datagram_frame_size
+216            || cached.grease_quic_bit && !self.grease_quic_bit
+217            || cached.address_discovery_role != self.address_discovery_role
+218            || cached.max_remote_nat_traversal_addresses != self.max_remote_nat_traversal_addresses
+219        {
+220            return Err(TransportError::PROTOCOL_VIOLATION(
+221                "0-RTT accepted with incompatible transport parameters",
+222            ));
+223        }
+224        Ok(())
+225    }
+226
+227    /// Maximum number of CIDs to issue to this peer
+228    ///
+229    /// Consider both a) the active_connection_id_limit from the other end; and
+230    /// b) LOCAL_CID_COUNT used locally
+231    pub(crate) fn issue_cids_limit(&self) -> u64 {
+232        self.active_connection_id_limit.0.min(LOCAL_CID_COUNT)
+233    }
+234}
+235
+236/// A server's preferred address
+237///
+238/// This is communicated as a transport parameter during TLS session establishment.
+239#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+240pub(crate) struct PreferredAddress {
+241    pub(crate) address_v4: Option<SocketAddrV4>,
+242    pub(crate) address_v6: Option<SocketAddrV6>,
+243    pub(crate) connection_id: ConnectionId,
+244    pub(crate) stateless_reset_token: ResetToken,
+245}
+246
+247impl PreferredAddress {
+248    fn wire_size(&self) -> u16 {
+249        4 + 2 + 16 + 2 + 1 + self.connection_id.len() as u16 + 16
+250    }
+251
+252    fn write<W: BufMut>(&self, w: &mut W) {
+253        w.write(self.address_v4.map_or(Ipv4Addr::UNSPECIFIED, |x| *x.ip()));
+254        w.write::<u16>(self.address_v4.map_or(0, |x| x.port()));
+255        w.write(self.address_v6.map_or(Ipv6Addr::UNSPECIFIED, |x| *x.ip()));
+256        w.write::<u16>(self.address_v6.map_or(0, |x| x.port()));
+257        w.write::<u8>(self.connection_id.len() as u8);
+258        w.put_slice(&self.connection_id);
+259        w.put_slice(&self.stateless_reset_token);
+260    }
+261
+262    fn read<R: Buf>(r: &mut R) -> Result<Self, Error> {
+263        let ip_v4 = r.get::<Ipv4Addr>()?;
+264        let port_v4 = r.get::<u16>()?;
+265        let ip_v6 = r.get::<Ipv6Addr>()?;
+266        let port_v6 = r.get::<u16>()?;
+267        let cid_len = r.get::<u8>()?;
+268        if r.remaining() < cid_len as usize || cid_len > MAX_CID_SIZE as u8 {
+269            return Err(Error::Malformed);
+270        }
+271        let mut stage = [0; MAX_CID_SIZE];
+272        r.copy_to_slice(&mut stage[0..cid_len as usize]);
+273        let cid = ConnectionId::new(&stage[0..cid_len as usize]);
+274        if r.remaining() < 16 {
+275            return Err(Error::Malformed);
+276        }
+277        let mut token = [0; RESET_TOKEN_SIZE];
+278        r.copy_to_slice(&mut token);
+279        let address_v4 = if ip_v4.is_unspecified() && port_v4 == 0 {
+280            None
+281        } else {
+282            Some(SocketAddrV4::new(ip_v4, port_v4))
+283        };
+284        let address_v6 = if ip_v6.is_unspecified() && port_v6 == 0 {
+285            None
+286        } else {
+287            Some(SocketAddrV6::new(ip_v6, port_v6, 0, 0))
+288        };
+289        if address_v4.is_none() && address_v6.is_none() {
+290            return Err(Error::IllegalValue);
+291        }
+292        Ok(Self {
+293            address_v4,
+294            address_v6,
+295            connection_id: cid,
+296            stateless_reset_token: token.into(),
+297        })
+298    }
+299}
+300
+301/// Errors encountered while decoding `TransportParameters`
+302#[derive(Debug, Copy, Clone, Eq, PartialEq, Error)]
+303pub enum Error {
+304    /// Parameters that are semantically invalid
+305    #[error("parameter had illegal value")]
+306    IllegalValue,
+307    /// Catch-all error for problems while decoding transport parameters
+308    #[error("parameters were malformed")]
+309    Malformed,
+310}
+311
+312impl From<Error> for TransportError {
+313    fn from(e: Error) -> Self {
+314        match e {
+315            Error::IllegalValue => Self::TRANSPORT_PARAMETER_ERROR("illegal value"),
+316            Error::Malformed => Self::TRANSPORT_PARAMETER_ERROR("malformed"),
+317        }
+318    }
+319}
+320
+321impl From<UnexpectedEnd> for Error {
+322    fn from(_: UnexpectedEnd) -> Self {
+323        Self::Malformed
+324    }
+325}
+326
+327impl TransportParameters {
+328    /// Encode `TransportParameters` into buffer
+329    pub fn write<W: BufMut>(&self, w: &mut W) {
+330        let ids = match &self.write_order {
+331            Some(order) => order,
+332            None => &std::array::from_fn(|i| i as u8),
+333        };
+334
+335        for idx in ids {
+336            let id = TransportParameterId::SUPPORTED[*idx as usize];
+337            match id {
+338                TransportParameterId::ReservedTransportParameter => {
+339                    if let Some(param) = self.grease_transport_parameter {
+340                        param.write(w);
+341                    }
+342                }
+343                TransportParameterId::StatelessResetToken => {
+344                    if let Some(ref x) = self.stateless_reset_token {
+345                        w.write_var(id as u64);
+346                        w.write_var(16);
+347                        w.put_slice(x);
+348                    }
+349                }
+350                TransportParameterId::DisableActiveMigration => {
+351                    if self.disable_active_migration {
+352                        w.write_var(id as u64);
+353                        w.write_var(0);
+354                    }
+355                }
+356                TransportParameterId::MaxDatagramFrameSize => {
+357                    if let Some(x) = self.max_datagram_frame_size {
+358                        w.write_var(id as u64);
+359                        w.write_var(x.size() as u64);
+360                        w.write(x);
+361                    }
+362                }
+363                TransportParameterId::PreferredAddress => {
+364                    if let Some(ref x) = self.preferred_address {
+365                        w.write_var(id as u64);
+366                        w.write_var(x.wire_size() as u64);
+367                        x.write(w);
+368                    }
+369                }
+370                TransportParameterId::OriginalDestinationConnectionId => {
+371                    if let Some(ref cid) = self.original_dst_cid {
+372                        w.write_var(id as u64);
+373                        w.write_var(cid.len() as u64);
+374                        w.put_slice(cid);
+375                    }
+376                }
+377                TransportParameterId::InitialSourceConnectionId => {
+378                    if let Some(ref cid) = self.initial_src_cid {
+379                        w.write_var(id as u64);
+380                        w.write_var(cid.len() as u64);
+381                        w.put_slice(cid);
+382                    }
+383                }
+384                TransportParameterId::RetrySourceConnectionId => {
+385                    if let Some(ref cid) = self.retry_src_cid {
+386                        w.write_var(id as u64);
+387                        w.write_var(cid.len() as u64);
+388                        w.put_slice(cid);
+389                    }
+390                }
+391                TransportParameterId::GreaseQuicBit => {
+392                    if self.grease_quic_bit {
+393                        w.write_var(id as u64);
+394                        w.write_var(0);
+395                    }
+396                }
+397                TransportParameterId::MinAckDelayDraft07 => {
+398                    if let Some(x) = self.min_ack_delay {
+399                        w.write_var(id as u64);
+400                        w.write_var(x.size() as u64);
+401                        w.write(x);
+402                    }
+403                }
+404                TransportParameterId::ObservedAddr => {
+405                    if let Some(varint_role) = self.address_discovery_role.as_transport_parameter()
+406                    {
+407                        w.write_var(id as u64);
+408                        w.write_var(varint_role.size() as u64);
+409                        w.write(varint_role);
+410                    }
+411                }
+412                TransportParameterId::InitialMaxPathId => {
+413                    if let Some(val) = self.initial_max_path_id {
+414                        w.write_var(id as u64);
+415                        w.write_var(val.size() as u64);
+416                        w.write(val);
+417                    }
+418                }
+419                TransportParameterId::IrohNatTraversal => {
+420                    if let Some(val) = self.max_remote_nat_traversal_addresses {
+421                        w.write_var(id as u64);
+422                        w.write(VarInt(1));
+423                        w.write(val.get());
+424                    }
+425                }
+426                id => {
+427                    macro_rules! write_params {
+428                        {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
+429                            match id {
+430                                $(TransportParameterId::$id => {
+431                                    if self.$name.0 != $default {
+432                                        w.write_var(id as u64);
+433                                        w.write(VarInt::try_from(self.$name.size()).unwrap());
+434                                        w.write(self.$name);
+435                                    }
+436                                })*,
+437                                _ => {
+438                                    unimplemented!("Missing implementation of write for transport parameter with code {id:?}");
+439                                }
+440                            }
+441                        }
+442                    }
+443                    apply_params!(write_params);
+444                }
+445            }
+446        }
+447    }
+448
+449    /// Decode `TransportParameters` from buffer
+450    pub fn read<R: Buf>(side: Side, r: &mut R) -> Result<Self, Error> {
+451        // Initialize to protocol-specified defaults
+452        let mut params = Self::default();
+453
+454        // State to check for duplicate transport parameters.
+455        macro_rules! param_state {
+456            {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {{
+457                struct ParamState {
+458                    $($name: bool,)*
+459                }
+460
+461                ParamState {
+462                    $($name: false,)*
+463                }
+464            }}
+465        }
+466        let mut got = apply_params!(param_state);
+467
+468        while r.has_remaining() {
+469            let id = r.get_var()?;
+470            let len = r.get_var()?;
+471            if (r.remaining() as u64) < len {
+472                return Err(Error::Malformed);
+473            }
+474            let len = len as usize;
+475            let Ok(id) = TransportParameterId::try_from(id) else {
+476                // unknown transport parameters are ignored
+477                r.advance(len);
+478                continue;
+479            };
+480
+481            match id {
+482                TransportParameterId::OriginalDestinationConnectionId => {
+483                    decode_cid(len, &mut params.original_dst_cid, r)?
+484                }
+485                TransportParameterId::StatelessResetToken => {
+486                    if len != 16 || params.stateless_reset_token.is_some() {
+487                        return Err(Error::Malformed);
+488                    }
+489                    let mut tok = [0; RESET_TOKEN_SIZE];
+490                    r.copy_to_slice(&mut tok);
+491                    params.stateless_reset_token = Some(tok.into());
+492                }
+493                TransportParameterId::DisableActiveMigration => {
+494                    if len != 0 || params.disable_active_migration {
+495                        return Err(Error::Malformed);
+496                    }
+497                    params.disable_active_migration = true;
+498                }
+499                TransportParameterId::PreferredAddress => {
+500                    if params.preferred_address.is_some() {
+501                        return Err(Error::Malformed);
+502                    }
+503                    params.preferred_address = Some(PreferredAddress::read(&mut r.take(len))?);
+504                }
+505                TransportParameterId::InitialSourceConnectionId => {
+506                    decode_cid(len, &mut params.initial_src_cid, r)?
+507                }
+508                TransportParameterId::RetrySourceConnectionId => {
+509                    decode_cid(len, &mut params.retry_src_cid, r)?
+510                }
+511                TransportParameterId::MaxDatagramFrameSize => {
+512                    if len > 8 || params.max_datagram_frame_size.is_some() {
+513                        return Err(Error::Malformed);
+514                    }
+515                    params.max_datagram_frame_size = Some(r.get().unwrap());
+516                }
+517                TransportParameterId::GreaseQuicBit => match len {
+518                    0 => params.grease_quic_bit = true,
+519                    _ => return Err(Error::Malformed),
+520                },
+521                TransportParameterId::MinAckDelayDraft07 => {
+522                    params.min_ack_delay = Some(r.get().unwrap())
+523                }
+524                TransportParameterId::ObservedAddr => {
+525                    if !params.address_discovery_role.is_disabled() {
+526                        // duplicate parameter
+527                        return Err(Error::Malformed);
+528                    }
+529                    let value: VarInt = r.get()?;
+530                    if len != value.size() {
+531                        return Err(Error::Malformed);
+532                    }
+533                    params.address_discovery_role = value.try_into()?;
+534                    tracing::debug!(
+535                        role = ?params.address_discovery_role,
+536                        "address discovery enabled for peer"
+537                    );
+538                }
+539                TransportParameterId::InitialMaxPathId => {
+540                    if params.initial_max_path_id.is_some() {
+541                        return Err(Error::Malformed);
+542                    }
+543
+544                    let value: PathId = r.get()?;
+545                    if len != value.size() {
+546                        return Err(Error::Malformed);
+547                    }
+548
+549                    params.initial_max_path_id = Some(value);
+550                }
+551                TransportParameterId::IrohNatTraversal => {
+552                    if params.max_remote_nat_traversal_addresses.is_some() {
+553                        return Err(Error::Malformed);
+554                    }
+555                    if len != 1 {
+556                        return Err(Error::Malformed);
+557                    }
+558
+559                    let value: u8 = r.get()?;
+560                    let value = NonZeroU8::new(value).ok_or(Error::IllegalValue)?;
+561
+562                    params.max_remote_nat_traversal_addresses = Some(value);
+563                }
+564                _ => {
+565                    macro_rules! parse {
+566                        {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
+567                            match id {
+568                                $(TransportParameterId::$id => {
+569                                    let value = r.get::<VarInt>()?;
+570                                    if len != value.size() || got.$name { return Err(Error::Malformed); }
+571                                    params.$name = value.into();
+572                                    got.$name = true;
+573                                })*
+574                                _ => r.advance(len),
+575                            }
+576                        }
+577                    }
+578                    apply_params!(parse);
+579                }
+580            }
+581        }
+582
+583        // Semantic validation
+584
+585        // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.26.1
+586        if params.ack_delay_exponent.0 > 20
+587            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.28.1
+588            || params.max_ack_delay.0 >= 1 << 14
+589            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-6.2.1
+590            || params.active_connection_id_limit.0 < 2
+591            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.10.1
+592            || params.max_udp_payload_size.0 < 1200
+593            // https://www.rfc-editor.org/rfc/rfc9000.html#section-4.6-2
+594            || params.initial_max_streams_bidi.0 > MAX_STREAM_COUNT
+595            || params.initial_max_streams_uni.0 > MAX_STREAM_COUNT
+596            // https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-08.html#section-3-4
+597            || params.min_ack_delay.is_some_and(|min_ack_delay| {
+598                // min_ack_delay uses microseconds, whereas max_ack_delay uses milliseconds
+599                min_ack_delay.0 > params.max_ack_delay.0 * 1_000
+600            })
+601            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-8
+602            || (side.is_server()
+603                && (params.original_dst_cid.is_some()
+604                    || params.preferred_address.is_some()
+605                    || params.retry_src_cid.is_some()
+606                    || params.stateless_reset_token.is_some()))
+607            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.38.1
+608            || params
+609                .preferred_address.is_some_and(|x| x.connection_id.is_empty())
+610        {
+611            return Err(Error::IllegalValue);
+612        }
+613
+614        Ok(params)
+615    }
+616}
+617
+618/// A reserved transport parameter.
+619///
+620/// It has an identifier of the form 31 * N + 27 for the integer value of N.
+621/// Such identifiers are reserved to exercise the requirement that unknown transport parameters be ignored.
+622/// The reserved transport parameter has no semantics and can carry arbitrary values.
+623/// It may be included in transport parameters sent to the peer, and should be ignored when received.
+624///
+625/// See spec: <https://www.rfc-editor.org/rfc/rfc9000.html#section-18.1>
+626#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+627pub(crate) struct ReservedTransportParameter {
+628    /// The reserved identifier of the transport parameter
+629    id: VarInt,
+630
+631    /// Buffer to store the parameter payload
+632    payload: [u8; Self::MAX_PAYLOAD_LEN],
+633
+634    /// The number of bytes to include in the wire format from the `payload` buffer
+635    payload_len: usize,
+636}
+637
+638impl ReservedTransportParameter {
+639    /// Generates a transport parameter with a random payload and a reserved ID.
+640    ///
+641    /// The implementation is inspired by quic-go and quiche:
+642    /// 1. <https://github.com/quic-go/quic-go/blob/3e0a67b2476e1819752f04d75968de042b197b56/internal/wire/transport_parameters.go#L338-L344>
+643    /// 2. <https://github.com/google/quiche/blob/cb1090b20c40e2f0815107857324e99acf6ec567/quiche/quic/core/crypto/transport_parameters.cc#L843-L860>
+644    fn random(rng: &mut impl RngCore) -> Self {
+645        let id = Self::generate_reserved_id(rng);
+646
+647        let payload_len = rng.random_range(0..Self::MAX_PAYLOAD_LEN);
+648
+649        let payload = {
+650            let mut slice = [0u8; Self::MAX_PAYLOAD_LEN];
+651            rng.fill_bytes(&mut slice[..payload_len]);
+652            slice
+653        };
+654
+655        Self {
+656            id,
+657            payload,
+658            payload_len,
+659        }
+660    }
+661
+662    fn write(&self, w: &mut impl BufMut) {
+663        w.write_var(self.id.0);
+664        w.write_var(self.payload_len as u64);
+665        w.put_slice(&self.payload[..self.payload_len]);
+666    }
+667
+668    /// Generates a random reserved identifier of the form `31 * N + 27`, as required by RFC 9000.
+669    /// Reserved transport parameter identifiers are used to test compliance with the requirement
+670    /// that unknown transport parameters must be ignored by peers.
+671    /// See: <https://www.rfc-editor.org/rfc/rfc9000.html#section-18.1> and <https://www.rfc-editor.org/rfc/rfc9000.html#section-22.3>
+672    fn generate_reserved_id(rng: &mut impl RngCore) -> VarInt {
+673        let id = {
+674            let rand = rng.random_range(0u64..(1 << 62) - 27);
+675            let n = rand / 31;
+676            31 * n + 27
+677        };
+678        debug_assert!(
+679            id % 31 == 27,
+680            "generated id does not have the form of 31 * N + 27"
+681        );
+682        VarInt::from_u64(id).expect(
+683            "generated id does fit into range of allowed transport parameter IDs: [0; 2^62)",
+684        )
+685    }
+686
+687    /// The maximum length of the payload to include as the parameter payload.
+688    /// This value is not a specification-imposed limit but is chosen to match
+689    /// the limit used by other implementations of QUIC, e.g., quic-go and quiche.
+690    const MAX_PAYLOAD_LEN: usize = 16;
+691}
+692
+693#[repr(u64)]
+694#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+695pub(crate) enum TransportParameterId {
+696    // https://www.rfc-editor.org/rfc/rfc9000.html#iana-tp-table
+697    OriginalDestinationConnectionId = 0x00,
+698    MaxIdleTimeout = 0x01,
+699    StatelessResetToken = 0x02,
+700    MaxUdpPayloadSize = 0x03,
+701    InitialMaxData = 0x04,
+702    InitialMaxStreamDataBidiLocal = 0x05,
+703    InitialMaxStreamDataBidiRemote = 0x06,
+704    InitialMaxStreamDataUni = 0x07,
+705    InitialMaxStreamsBidi = 0x08,
+706    InitialMaxStreamsUni = 0x09,
+707    AckDelayExponent = 0x0A,
+708    MaxAckDelay = 0x0B,
+709    DisableActiveMigration = 0x0C,
+710    PreferredAddress = 0x0D,
+711    ActiveConnectionIdLimit = 0x0E,
+712    InitialSourceConnectionId = 0x0F,
+713    RetrySourceConnectionId = 0x10,
+714
+715    // Smallest possible ID of reserved transport parameter https://datatracker.ietf.org/doc/html/rfc9000#section-22.3
+716    ReservedTransportParameter = 0x1B,
+717
+718    // https://www.rfc-editor.org/rfc/rfc9221.html#section-3
+719    MaxDatagramFrameSize = 0x20,
+720
+721    // https://www.rfc-editor.org/rfc/rfc9287.html#section-3
+722    GreaseQuicBit = 0x2AB2,
+723
+724    // https://datatracker.ietf.org/doc/html/draft-ietf-quic-ack-frequency#section-10.1
+725    MinAckDelayDraft07 = 0xFF04DE1B,
+726
+727    // <https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/>
+728    ObservedAddr = 0x9f81a176,
+729
+730    // https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath
+731    InitialMaxPathId = 0x3e,
+732
+733    // inspired by https://www.ietf.org/archive/id/draft-seemann-quic-nat-traversal-02.html,
+734    // simplified to iroh's needs
+735    IrohNatTraversal = 0x3d7f91120401,
+736}
+737
+738impl TransportParameterId {
+739    /// Array with all supported transport parameter IDs
+740    const SUPPORTED: [Self; 24] = [
+741        Self::MaxIdleTimeout,
+742        Self::MaxUdpPayloadSize,
+743        Self::InitialMaxData,
+744        Self::InitialMaxStreamDataBidiLocal,
+745        Self::InitialMaxStreamDataBidiRemote,
+746        Self::InitialMaxStreamDataUni,
+747        Self::InitialMaxStreamsBidi,
+748        Self::InitialMaxStreamsUni,
+749        Self::AckDelayExponent,
+750        Self::MaxAckDelay,
+751        Self::ActiveConnectionIdLimit,
+752        Self::ReservedTransportParameter,
+753        Self::StatelessResetToken,
+754        Self::DisableActiveMigration,
+755        Self::MaxDatagramFrameSize,
+756        Self::PreferredAddress,
+757        Self::OriginalDestinationConnectionId,
+758        Self::InitialSourceConnectionId,
+759        Self::RetrySourceConnectionId,
+760        Self::GreaseQuicBit,
+761        Self::MinAckDelayDraft07,
+762        Self::ObservedAddr,
+763        Self::InitialMaxPathId,
+764        Self::IrohNatTraversal,
+765    ];
+766}
+767
+768impl std::cmp::PartialEq<u64> for TransportParameterId {
+769    fn eq(&self, other: &u64) -> bool {
+770        *other == (*self as u64)
+771    }
+772}
+773
+774impl TryFrom<u64> for TransportParameterId {
+775    type Error = ();
+776
+777    fn try_from(value: u64) -> Result<Self, Self::Error> {
+778        let param = match value {
+779            id if Self::MaxIdleTimeout == id => Self::MaxIdleTimeout,
+780            id if Self::MaxUdpPayloadSize == id => Self::MaxUdpPayloadSize,
+781            id if Self::InitialMaxData == id => Self::InitialMaxData,
+782            id if Self::InitialMaxStreamDataBidiLocal == id => Self::InitialMaxStreamDataBidiLocal,
+783            id if Self::InitialMaxStreamDataBidiRemote == id => {
+784                Self::InitialMaxStreamDataBidiRemote
+785            }
+786            id if Self::InitialMaxStreamDataUni == id => Self::InitialMaxStreamDataUni,
+787            id if Self::InitialMaxStreamsBidi == id => Self::InitialMaxStreamsBidi,
+788            id if Self::InitialMaxStreamsUni == id => Self::InitialMaxStreamsUni,
+789            id if Self::AckDelayExponent == id => Self::AckDelayExponent,
+790            id if Self::MaxAckDelay == id => Self::MaxAckDelay,
+791            id if Self::ActiveConnectionIdLimit == id => Self::ActiveConnectionIdLimit,
+792            id if Self::ReservedTransportParameter == id => Self::ReservedTransportParameter,
+793            id if Self::StatelessResetToken == id => Self::StatelessResetToken,
+794            id if Self::DisableActiveMigration == id => Self::DisableActiveMigration,
+795            id if Self::MaxDatagramFrameSize == id => Self::MaxDatagramFrameSize,
+796            id if Self::PreferredAddress == id => Self::PreferredAddress,
+797            id if Self::OriginalDestinationConnectionId == id => {
+798                Self::OriginalDestinationConnectionId
+799            }
+800            id if Self::InitialSourceConnectionId == id => Self::InitialSourceConnectionId,
+801            id if Self::RetrySourceConnectionId == id => Self::RetrySourceConnectionId,
+802            id if Self::GreaseQuicBit == id => Self::GreaseQuicBit,
+803            id if Self::MinAckDelayDraft07 == id => Self::MinAckDelayDraft07,
+804            id if Self::ObservedAddr == id => Self::ObservedAddr,
+805            id if Self::InitialMaxPathId == id => Self::InitialMaxPathId,
+806            id if Self::IrohNatTraversal == id => Self::IrohNatTraversal,
+807            _ => return Err(()),
+808        };
+809        Ok(param)
+810    }
+811}
+812
+813fn decode_cid(len: usize, value: &mut Option<ConnectionId>, r: &mut impl Buf) -> Result<(), Error> {
+814    if len > MAX_CID_SIZE || value.is_some() || r.remaining() < len {
+815        return Err(Error::Malformed);
+816    }
+817
+818    *value = Some(ConnectionId::from_buf(r, len));
+819    Ok(())
+820}
+821
+822#[cfg(test)]
+823mod test {
+824
+825    use super::*;
+826
+827    #[test]
+828    fn coding() {
+829        let mut buf = Vec::new();
+830        let params = TransportParameters {
+831            initial_src_cid: Some(ConnectionId::new(&[])),
+832            original_dst_cid: Some(ConnectionId::new(&[])),
+833            initial_max_streams_bidi: 16u32.into(),
+834            initial_max_streams_uni: 16u32.into(),
+835            ack_delay_exponent: 2u32.into(),
+836            max_udp_payload_size: 1200u32.into(),
+837            preferred_address: Some(PreferredAddress {
+838                address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
+839                address_v6: Some(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 24, 0, 0)),
+840                connection_id: ConnectionId::new(&[0x42]),
+841                stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
+842            }),
+843            grease_quic_bit: true,
+844            min_ack_delay: Some(2_000u32.into()),
+845            address_discovery_role: address_discovery::Role::SendOnly,
+846            initial_max_path_id: Some(PathId::MAX),
+847            max_remote_nat_traversal_addresses: Some(5u8.try_into().unwrap()),
+848            ..TransportParameters::default()
+849        };
+850        params.write(&mut buf);
+851        assert_eq!(
+852            TransportParameters::read(Side::Client, &mut buf.as_slice()).unwrap(),
+853            params
+854        );
+855    }
+856
+857    #[test]
+858    fn reserved_transport_parameter_generate_reserved_id() {
+859        let mut rngs = [
+860            StepRng(0),
+861            StepRng(1),
+862            StepRng(27),
+863            StepRng(31),
+864            StepRng(u32::MAX as u64),
+865            StepRng(u32::MAX as u64 - 1),
+866            StepRng(u32::MAX as u64 + 1),
+867            StepRng(u32::MAX as u64 - 27),
+868            StepRng(u32::MAX as u64 + 27),
+869            StepRng(u32::MAX as u64 - 31),
+870            StepRng(u32::MAX as u64 + 31),
+871            StepRng(u64::MAX),
+872            StepRng(u64::MAX - 1),
+873            StepRng(u64::MAX - 27),
+874            StepRng(u64::MAX - 31),
+875            StepRng(1 << 62),
+876            StepRng((1 << 62) - 1),
+877            StepRng((1 << 62) + 1),
+878            StepRng((1 << 62) - 27),
+879            StepRng((1 << 62) + 27),
+880            StepRng((1 << 62) - 31),
+881            StepRng((1 << 62) + 31),
+882        ];
+883        for rng in &mut rngs {
+884            let id = ReservedTransportParameter::generate_reserved_id(rng);
+885            assert!(id.0 % 31 == 27)
+886        }
+887    }
+888
+889    struct StepRng(u64);
+890
+891    impl RngCore for StepRng {
+892        #[inline]
+893        fn next_u32(&mut self) -> u32 {
+894            self.next_u64() as u32
+895        }
+896
+897        #[inline]
+898        fn next_u64(&mut self) -> u64 {
+899            let res = self.0;
+900            self.0 = self.0.wrapping_add(1);
+901            res
+902        }
+903
+904        #[inline]
+905        fn fill_bytes(&mut self, dst: &mut [u8]) {
+906            let mut left = dst;
+907            while left.len() >= 8 {
+908                let (l, r) = left.split_at_mut(8);
+909                left = r;
+910                l.copy_from_slice(&self.next_u64().to_le_bytes());
+911            }
+912            let n = left.len();
+913            if n > 0 {
+914                left.copy_from_slice(&self.next_u32().to_le_bytes()[..n]);
+915            }
+916        }
+917    }
+918
+919    #[test]
+920    fn reserved_transport_parameter_ignored_when_read() {
+921        let mut buf = Vec::new();
+922        let reserved_parameter = ReservedTransportParameter::random(&mut rand::rng());
+923        assert!(reserved_parameter.payload_len < ReservedTransportParameter::MAX_PAYLOAD_LEN);
+924        assert!(reserved_parameter.id.0 % 31 == 27);
+925
+926        reserved_parameter.write(&mut buf);
+927        assert!(!buf.is_empty());
+928        let read_params = TransportParameters::read(Side::Server, &mut buf.as_slice()).unwrap();
+929        assert_eq!(read_params, TransportParameters::default());
+930    }
+931
+932    #[test]
+933    fn read_semantic_validation() {
+934        #[allow(clippy::type_complexity)]
+935        let illegal_params_builders: Vec<Box<dyn FnMut(&mut TransportParameters)>> = vec![
+936            Box::new(|t| {
+937                // This min_ack_delay is bigger than max_ack_delay!
+938                let min_ack_delay = t.max_ack_delay.0 * 1_000 + 1;
+939                t.min_ack_delay = Some(VarInt::from_u64(min_ack_delay).unwrap())
+940            }),
+941            Box::new(|t| {
+942                // Preferred address can only be sent by senders (and we are reading the transport
+943                // params as a client)
+944                t.preferred_address = Some(PreferredAddress {
+945                    address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
+946                    address_v6: None,
+947                    connection_id: ConnectionId::new(&[]),
+948                    stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
+949                })
+950            }),
+951        ];
+952
+953        for mut builder in illegal_params_builders {
+954            let mut buf = Vec::new();
+955            let mut params = TransportParameters::default();
+956            builder(&mut params);
+957            params.write(&mut buf);
+958
+959            assert_eq!(
+960                TransportParameters::read(Side::Server, &mut buf.as_slice()),
+961                Err(Error::IllegalValue)
+962            );
+963        }
+964    }
+965
+966    #[test]
+967    fn resumption_params_validation() {
+968        let high_limit = TransportParameters {
+969            initial_max_streams_uni: 32u32.into(),
+970            ..TransportParameters::default()
+971        };
+972        let low_limit = TransportParameters {
+973            initial_max_streams_uni: 16u32.into(),
+974            ..TransportParameters::default()
+975        };
+976        high_limit.validate_resumption_from(&low_limit).unwrap();
+977        low_limit.validate_resumption_from(&high_limit).unwrap_err();
+978    }
+979}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_proto/varint.rs.html b/pr/395/docs/src/iroh_quinn_proto/varint.rs.html new file mode 100644 index 000000000..192d903ae --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_proto/varint.rs.html @@ -0,0 +1,233 @@ +varint.rs - source

iroh_quinn_proto/
varint.rs

1use std::{convert::TryInto, fmt};
+2
+3use bytes::{Buf, BufMut};
+4use thiserror::Error;
+5
+6use crate::coding::{self, Decodable, Encodable, UnexpectedEnd};
+7
+8/// An integer less than 2^62
+9///
+10/// Values of this type are suitable for encoding as QUIC variable-length integer.
+11// It would be neat if we could express to Rust that the top two bits are available for use as enum
+12// discriminants
+13#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+14pub struct VarInt(pub(crate) u64);
+15
+16impl VarInt {
+17    /// The largest representable value
+18    pub const MAX: Self = Self((1 << 62) - 1);
+19    /// The largest encoded value length
+20    pub const MAX_SIZE: usize = 8;
+21
+22    /// Construct a `VarInt` infallibly
+23    pub const fn from_u32(x: u32) -> Self {
+24        Self(x as u64)
+25    }
+26
+27    /// Succeeds iff `x` < 2^62
+28    pub fn from_u64(x: u64) -> Result<Self, VarIntBoundsExceeded> {
+29        if x < 2u64.pow(62) {
+30            Ok(Self(x))
+31        } else {
+32            Err(VarIntBoundsExceeded)
+33        }
+34    }
+35
+36    /// Create a VarInt without ensuring it's in range
+37    ///
+38    /// # Safety
+39    ///
+40    /// `x` must be less than 2^62.
+41    pub const unsafe fn from_u64_unchecked(x: u64) -> Self {
+42        Self(x)
+43    }
+44
+45    /// Extract the integer value
+46    pub const fn into_inner(self) -> u64 {
+47        self.0
+48    }
+49
+50    /// Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead
+51    /// of overflowing.
+52    pub fn saturating_add(self, rhs: impl Into<Self>) -> Self {
+53        let rhs = rhs.into();
+54        let inner = self.0.saturating_add(rhs.0).min(Self::MAX.0);
+55        Self(inner)
+56    }
+57
+58    /// Compute the number of bytes needed to encode this value
+59    pub(crate) const fn size(self) -> usize {
+60        let x = self.0;
+61        if x < 2u64.pow(6) {
+62            1
+63        } else if x < 2u64.pow(14) {
+64            2
+65        } else if x < 2u64.pow(30) {
+66            4
+67        } else if x < 2u64.pow(62) {
+68            8
+69        } else {
+70            panic!("malformed VarInt");
+71        }
+72    }
+73}
+74
+75impl From<VarInt> for u64 {
+76    fn from(x: VarInt) -> Self {
+77        x.0
+78    }
+79}
+80
+81impl From<u8> for VarInt {
+82    fn from(x: u8) -> Self {
+83        Self(x.into())
+84    }
+85}
+86
+87impl From<u16> for VarInt {
+88    fn from(x: u16) -> Self {
+89        Self(x.into())
+90    }
+91}
+92
+93impl From<u32> for VarInt {
+94    fn from(x: u32) -> Self {
+95        Self(x.into())
+96    }
+97}
+98
+99impl std::convert::TryFrom<u64> for VarInt {
+100    type Error = VarIntBoundsExceeded;
+101    /// Succeeds iff `x` < 2^62
+102    fn try_from(x: u64) -> Result<Self, VarIntBoundsExceeded> {
+103        Self::from_u64(x)
+104    }
+105}
+106
+107impl std::convert::TryFrom<u128> for VarInt {
+108    type Error = VarIntBoundsExceeded;
+109    /// Succeeds iff `x` < 2^62
+110    fn try_from(x: u128) -> Result<Self, VarIntBoundsExceeded> {
+111        Self::from_u64(x.try_into().map_err(|_| VarIntBoundsExceeded)?)
+112    }
+113}
+114
+115impl std::convert::TryFrom<usize> for VarInt {
+116    type Error = VarIntBoundsExceeded;
+117    /// Succeeds iff `x` < 2^62
+118    fn try_from(x: usize) -> Result<Self, VarIntBoundsExceeded> {
+119        Self::try_from(x as u64)
+120    }
+121}
+122
+123impl fmt::Debug for VarInt {
+124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+125        self.0.fmt(f)
+126    }
+127}
+128
+129impl fmt::Display for VarInt {
+130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+131        self.0.fmt(f)
+132    }
+133}
+134
+135#[cfg(feature = "arbitrary")]
+136impl<'arbitrary> arbitrary::Arbitrary<'arbitrary> for VarInt {
+137    fn arbitrary(u: &mut arbitrary::Unstructured<'arbitrary>) -> arbitrary::Result<Self> {
+138        Ok(Self(u.int_in_range(0..=Self::MAX.0)?))
+139    }
+140}
+141
+142#[cfg(test)]
+143impl proptest::arbitrary::Arbitrary for VarInt {
+144    type Parameters = ();
+145    type Strategy = proptest::strategy::BoxedStrategy<Self>;
+146
+147    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
+148        use proptest::strategy::Strategy;
+149        (0..=Self::MAX.0).prop_map(Self).boxed()
+150    }
+151}
+152
+153/// Strategy for generating a u64 in the valid VarInt range (0..2^62)
+154#[cfg(test)]
+155pub(crate) fn varint_u64() -> impl proptest::strategy::Strategy<Value = u64> {
+156    0..=VarInt::MAX.0
+157}
+158
+159/// Error returned when constructing a `VarInt` from a value >= 2^62
+160#[derive(Debug, Copy, Clone, Eq, PartialEq, Error)]
+161#[error("value too large for varint encoding")]
+162pub struct VarIntBoundsExceeded;
+163
+164impl Decodable for VarInt {
+165    fn decode<B: Buf>(r: &mut B) -> coding::Result<Self> {
+166        if !r.has_remaining() {
+167            return Err(UnexpectedEnd);
+168        }
+169        let mut buf = [0; 8];
+170        buf[0] = r.get_u8();
+171        let tag = buf[0] >> 6;
+172        buf[0] &= 0b0011_1111;
+173        let x = match tag {
+174            0b00 => u64::from(buf[0]),
+175            0b01 => {
+176                if r.remaining() < 1 {
+177                    return Err(UnexpectedEnd);
+178                }
+179                r.copy_to_slice(&mut buf[1..2]);
+180                u64::from(u16::from_be_bytes(buf[..2].try_into().unwrap()))
+181            }
+182            0b10 => {
+183                if r.remaining() < 3 {
+184                    return Err(UnexpectedEnd);
+185                }
+186                r.copy_to_slice(&mut buf[1..4]);
+187                u64::from(u32::from_be_bytes(buf[..4].try_into().unwrap()))
+188            }
+189            0b11 => {
+190                if r.remaining() < 7 {
+191                    return Err(UnexpectedEnd);
+192                }
+193                r.copy_to_slice(&mut buf[1..8]);
+194                u64::from_be_bytes(buf)
+195            }
+196            _ => unreachable!(),
+197        };
+198        Ok(Self(x))
+199    }
+200}
+201
+202impl Encodable for VarInt {
+203    fn encode<B: BufMut>(&self, w: &mut B) {
+204        let x = self.0;
+205        if x < 2u64.pow(6) {
+206            w.put_u8(x as u8);
+207        } else if x < 2u64.pow(14) {
+208            w.put_u16((0b01 << 14) | x as u16);
+209        } else if x < 2u64.pow(30) {
+210            w.put_u32((0b10 << 30) | x as u32);
+211        } else if x < 2u64.pow(62) {
+212            w.put_u64((0b11 << 62) | x);
+213        } else {
+214            unreachable!("malformed VarInt")
+215        }
+216    }
+217}
+218
+219#[cfg(test)]
+220mod tests {
+221    use super::*;
+222
+223    #[test]
+224    fn test_saturating_add() {
+225        // add within range behaves normally
+226        let large: VarInt = u32::MAX.into();
+227        let next = u64::from(u32::MAX) + 1;
+228        assert_eq!(large.saturating_add(1u8), VarInt::from_u64(next).unwrap());
+229
+230        // outside range saturates
+231        assert_eq!(VarInt::MAX.saturating_add(1u8), VarInt::MAX)
+232    }
+233}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_udp/cmsg/mod.rs.html b/pr/395/docs/src/iroh_quinn_udp/cmsg/mod.rs.html new file mode 100644 index 000000000..c9f5e54a5 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_udp/cmsg/mod.rs.html @@ -0,0 +1,157 @@ +mod.rs - source

iroh_quinn_udp/cmsg/
mod.rs

1use std::{
+2    ffi::{c_int, c_uchar},
+3    mem, ptr,
+4};
+5
+6#[cfg(unix)]
+7#[path = "unix.rs"]
+8mod imp;
+9
+10#[cfg(windows)]
+11#[path = "windows.rs"]
+12mod imp;
+13
+14pub(crate) use imp::Aligned;
+15
+16/// Helper to encode a series of control messages (native "cmsgs") to a buffer for use in `sendmsg`
+17//  like API.
+18///
+19/// The operation must be "finished" for the native msghdr to be usable, either by calling `finish`
+20/// explicitly or by dropping the `Encoder`.
+21pub(crate) struct Encoder<'a, M: MsgHdr> {
+22    hdr: &'a mut M,
+23    cmsg: Option<&'a mut M::ControlMessage>,
+24    len: usize,
+25}
+26
+27impl<'a, M: MsgHdr> Encoder<'a, M> {
+28    /// # Safety
+29    /// - `hdr` must contain a suitably aligned pointer to a big enough buffer to hold control messages
+30    ///   bytes. All bytes of this buffer can be safely written.
+31    /// - The `Encoder` must be dropped before `hdr` is passed to a system call, and must not be leaked.
+32    pub(crate) unsafe fn new(hdr: &'a mut M) -> Self {
+33        Self {
+34            cmsg: unsafe { hdr.cmsg_first_hdr().as_mut() },
+35            hdr,
+36            len: 0,
+37        }
+38    }
+39
+40    /// Append a control message to the buffer.
+41    ///
+42    /// # Panics
+43    /// - If insufficient buffer space remains.
+44    /// - If `T` has stricter alignment requirements than `M::ControlMessage`
+45    pub(crate) fn push<T: Copy>(&mut self, level: c_int, ty: c_int, value: T) {
+46        assert!(mem::align_of::<T>() <= mem::align_of::<M::ControlMessage>());
+47        let space = M::ControlMessage::cmsg_space(mem::size_of_val(&value));
+48        assert!(
+49            self.hdr.control_len() >= self.len + space,
+50            "control message buffer too small. Required: {}, Available: {}",
+51            self.len + space,
+52            self.hdr.control_len()
+53        );
+54        let cmsg = self.cmsg.take().expect("no control buffer space remaining");
+55        cmsg.set(
+56            level,
+57            ty,
+58            M::ControlMessage::cmsg_len(mem::size_of_val(&value)),
+59        );
+60        unsafe {
+61            ptr::write(cmsg.cmsg_data() as *const T as *mut T, value);
+62        }
+63        self.len += space;
+64        self.cmsg = unsafe { self.hdr.cmsg_nxt_hdr(cmsg).as_mut() };
+65    }
+66
+67    /// Finishes appending control messages to the buffer
+68    pub(crate) fn finish(self) {
+69        // Delegates to the `Drop` impl
+70    }
+71}
+72
+73// Statically guarantees that the encoding operation is "finished" before the control buffer is read
+74// by `sendmsg` like API.
+75impl<M: MsgHdr> Drop for Encoder<'_, M> {
+76    fn drop(&mut self) {
+77        self.hdr.set_control_len(self.len as _);
+78    }
+79}
+80
+81/// # Safety
+82///
+83/// `cmsg` must refer to a native cmsg containing a payload of type `T`
+84pub(crate) unsafe fn decode<T: Copy, C: CMsgHdr>(cmsg: &impl CMsgHdr) -> T {
+85    assert!(mem::align_of::<T>() <= mem::align_of::<C>());
+86    debug_assert_eq!(cmsg.len(), C::cmsg_len(mem::size_of::<T>()));
+87    unsafe { ptr::read(cmsg.cmsg_data() as *const T) }
+88}
+89
+90pub(crate) struct Iter<'a, M: MsgHdr> {
+91    hdr: &'a M,
+92    cmsg: Option<&'a M::ControlMessage>,
+93}
+94
+95impl<'a, M: MsgHdr> Iter<'a, M> {
+96    /// # Safety
+97    ///
+98    /// `hdr` must hold a pointer to memory outliving `'a` which can be soundly read for the
+99    /// lifetime of the constructed `Iter` and contains a buffer of native cmsgs, i.e. is aligned
+100    //  for native `cmsghdr`, is fully initialized, and has correct internal links.
+101    pub(crate) unsafe fn new(hdr: &'a M) -> Self {
+102        Self {
+103            hdr,
+104            cmsg: unsafe { hdr.cmsg_first_hdr().as_ref() },
+105        }
+106    }
+107}
+108
+109impl<'a, M: MsgHdr> Iterator for Iter<'a, M> {
+110    type Item = &'a M::ControlMessage;
+111
+112    fn next(&mut self) -> Option<Self::Item> {
+113        let current = self.cmsg.take()?;
+114        self.cmsg = unsafe { self.hdr.cmsg_nxt_hdr(current).as_ref() };
+115
+116        #[cfg(apple_fast)]
+117        {
+118            // On MacOS < 14 CMSG_NXTHDR might continuously return a zeroed cmsg. In
+119            // such case, return `None` instead, thus indicating the end of
+120            // the cmsghdr chain.
+121            if current.len() < mem::size_of::<M::ControlMessage>() {
+122                return None;
+123            }
+124        }
+125
+126        Some(current)
+127    }
+128}
+129
+130// Helper traits for native types for control messages
+131pub(crate) trait MsgHdr {
+132    type ControlMessage: CMsgHdr;
+133
+134    fn cmsg_first_hdr(&self) -> *mut Self::ControlMessage;
+135
+136    fn cmsg_nxt_hdr(&self, cmsg: &Self::ControlMessage) -> *mut Self::ControlMessage;
+137
+138    /// Sets the number of control messages added to this `struct msghdr`.
+139    ///
+140    /// Note that this is a destructive operation and should only be done as a finalisation
+141    /// step.
+142    fn set_control_len(&mut self, len: usize);
+143
+144    fn control_len(&self) -> usize;
+145}
+146
+147pub(crate) trait CMsgHdr {
+148    fn cmsg_len(length: usize) -> usize;
+149
+150    fn cmsg_space(length: usize) -> usize;
+151
+152    fn cmsg_data(&self) -> *mut c_uchar;
+153
+154    fn set(&mut self, level: c_int, ty: c_int, len: usize);
+155
+156    fn len(&self) -> usize;
+157}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_udp/cmsg/unix.rs.html b/pr/395/docs/src/iroh_quinn_udp/cmsg/unix.rs.html new file mode 100644 index 000000000..337ee0895 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_udp/cmsg/unix.rs.html @@ -0,0 +1,81 @@ +unix.rs - source

iroh_quinn_udp/cmsg/
unix.rs

1use std::ffi::{c_int, c_uchar};
+2
+3use super::{CMsgHdr, MsgHdr};
+4
+5#[derive(Copy, Clone)]
+6#[repr(align(8))] // Conservative bound for align_of<libc::cmsghdr>
+7pub(crate) struct Aligned<T>(pub(crate) T);
+8
+9/// Helpers for [`libc::msghdr`]
+10impl MsgHdr for libc::msghdr {
+11    type ControlMessage = libc::cmsghdr;
+12
+13    fn cmsg_first_hdr(&self) -> *mut Self::ControlMessage {
+14        unsafe { libc::CMSG_FIRSTHDR(self) }
+15    }
+16
+17    fn cmsg_nxt_hdr(&self, cmsg: &Self::ControlMessage) -> *mut Self::ControlMessage {
+18        unsafe { libc::CMSG_NXTHDR(self, cmsg) }
+19    }
+20
+21    fn set_control_len(&mut self, len: usize) {
+22        self.msg_controllen = len as _;
+23        if len == 0 {
+24            // netbsd is particular about this being a NULL pointer if there are no control
+25            // messages.
+26            self.msg_control = std::ptr::null_mut();
+27        }
+28    }
+29
+30    fn control_len(&self) -> usize {
+31        self.msg_controllen as _
+32    }
+33}
+34
+35#[cfg(apple_fast)]
+36impl MsgHdr for crate::imp::msghdr_x {
+37    type ControlMessage = libc::cmsghdr;
+38
+39    fn cmsg_first_hdr(&self) -> *mut Self::ControlMessage {
+40        let selfp = self as *const _ as *mut libc::msghdr;
+41        unsafe { libc::CMSG_FIRSTHDR(selfp) }
+42    }
+43
+44    fn cmsg_nxt_hdr(&self, cmsg: &Self::ControlMessage) -> *mut Self::ControlMessage {
+45        let selfp = self as *const _ as *mut libc::msghdr;
+46        unsafe { libc::CMSG_NXTHDR(selfp, cmsg) }
+47    }
+48
+49    fn set_control_len(&mut self, len: usize) {
+50        self.msg_controllen = len as _;
+51    }
+52
+53    fn control_len(&self) -> usize {
+54        self.msg_controllen as _
+55    }
+56}
+57
+58/// Helpers for [`libc::cmsghdr`]
+59impl CMsgHdr for libc::cmsghdr {
+60    fn cmsg_len(length: usize) -> usize {
+61        unsafe { libc::CMSG_LEN(length as _) as usize }
+62    }
+63
+64    fn cmsg_space(length: usize) -> usize {
+65        unsafe { libc::CMSG_SPACE(length as _) as usize }
+66    }
+67
+68    fn cmsg_data(&self) -> *mut c_uchar {
+69        unsafe { libc::CMSG_DATA(self) }
+70    }
+71
+72    fn set(&mut self, level: c_int, ty: c_int, len: usize) {
+73        self.cmsg_level = level as _;
+74        self.cmsg_type = ty as _;
+75        self.cmsg_len = len as _;
+76    }
+77
+78    fn len(&self) -> usize {
+79        self.cmsg_len as _
+80    }
+81}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_udp/lib.rs.html b/pr/395/docs/src/iroh_quinn_udp/lib.rs.html new file mode 100644 index 000000000..22c12ab98 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_udp/lib.rs.html @@ -0,0 +1,300 @@ +lib.rs - source

iroh_quinn_udp/
lib.rs

1//! Uniform interface to send and receive UDP packets with advanced features useful for QUIC
+2//!
+3//! This crate exposes kernel UDP stack features available on most modern systems which are required
+4//! for an efficient and conformant QUIC implementation. As of this writing, these are not available
+5//! in std or major async runtimes, and their niche character and complexity are a barrier to adding
+6//! them. Hence, a dedicated crate.
+7//!
+8//! Exposed features include:
+9//!
+10//! - Segmentation offload for bulk send and receive operations, reducing CPU load.
+11//! - Reporting the exact destination address of received packets and specifying explicit source
+12//!   addresses for sent packets, allowing responses to be sent from the address that the peer
+13//!   expects when there are multiple possibilities. This is common when bound to a wildcard address
+14//!   in IPv6 due to [RFC 8981] temporary addresses.
+15//! - [Explicit Congestion Notification], which is required by QUIC to prevent packet loss and reduce
+16//!   latency on congested links when supported by the network path.
+17//! - Disabled IP-layer fragmentation, which allows the true physical MTU to be detected and reduces
+18//!   risk of QUIC packet loss.
+19//!
+20//! Some features are unavailable in some environments. This can be due to an outdated operating
+21//! system or drivers. Some operating systems may not implement desired features at all, or may not
+22//! yet be supported by the crate. When support is unavailable, functionality will gracefully
+23//! degrade.
+24//!
+25//! [RFC 8981]: https://www.rfc-editor.org/rfc/rfc8981.html
+26//! [Explicit Congestion Notification]: https://www.rfc-editor.org/rfc/rfc3168.html
+27#![warn(unreachable_pub)]
+28#![warn(clippy::use_self)]
+29
+30use std::net::{IpAddr, Ipv6Addr, SocketAddr};
+31#[cfg(unix)]
+32use std::os::unix::io::AsFd;
+33#[cfg(windows)]
+34use std::os::windows::io::AsSocket;
+35#[cfg(not(wasm_browser))]
+36use std::{
+37    sync::Mutex,
+38    time::{Duration, Instant},
+39};
+40
+41#[cfg(any(unix, windows))]
+42mod cmsg;
+43
+44#[cfg(unix)]
+45#[path = "unix.rs"]
+46mod imp;
+47
+48#[cfg(windows)]
+49#[path = "windows.rs"]
+50mod imp;
+51
+52// No ECN support
+53#[cfg(not(any(wasm_browser, unix, windows)))]
+54#[path = "fallback.rs"]
+55mod imp;
+56
+57#[allow(unused_imports, unused_macros)]
+58mod log {
+59    #[cfg(all(feature = "log", not(feature = "tracing-log")))]
+60    pub(crate) use log::{debug, error, info, trace, warn};
+61
+62    #[cfg(feature = "tracing-log")]
+63    pub(crate) use tracing::{debug, error, info, trace, warn};
+64
+65    #[cfg(not(any(feature = "log", feature = "tracing-log")))]
+66    mod no_op {
+67        macro_rules! trace    ( ($($tt:tt)*) => {{}} );
+68        macro_rules! debug    ( ($($tt:tt)*) => {{}} );
+69        macro_rules! info     ( ($($tt:tt)*) => {{}} );
+70        macro_rules! log_warn ( ($($tt:tt)*) => {{}} );
+71        macro_rules! error    ( ($($tt:tt)*) => {{}} );
+72
+73        pub(crate) use {debug, error, info, log_warn as warn, trace};
+74    }
+75
+76    #[cfg(not(any(feature = "log", feature = "tracing-log")))]
+77    pub(crate) use no_op::*;
+78}
+79
+80#[cfg(not(wasm_browser))]
+81pub use imp::UdpSocketState;
+82
+83/// Number of UDP packets to send/receive at a time
+84#[cfg(not(wasm_browser))]
+85pub const BATCH_SIZE: usize = imp::BATCH_SIZE;
+86/// Number of UDP packets to send/receive at a time
+87#[cfg(wasm_browser)]
+88pub const BATCH_SIZE: usize = 1;
+89
+90/// Metadata for a single buffer filled with bytes received from the network
+91///
+92/// This associated buffer can contain one or more datagrams, see [`stride`].
+93///
+94/// [`stride`]: RecvMeta::stride
+95#[derive(Debug, Copy, Clone)]
+96#[non_exhaustive]
+97pub struct RecvMeta {
+98    /// The source address of the datagram(s) contained in the buffer
+99    pub addr: SocketAddr,
+100    /// The number of bytes the associated buffer has
+101    pub len: usize,
+102    /// The size of a single datagram in the associated buffer
+103    ///
+104    /// When GRO (Generic Receive Offload) is used this indicates the size of a single
+105    /// datagram inside the buffer. If the buffer is larger, that is if [`len`] is greater
+106    /// then this value, then the individual datagrams contained have their boundaries at
+107    /// `stride` increments from the start. The last datagram could be smaller than
+108    /// `stride`.
+109    ///
+110    /// [`len`]: RecvMeta::len
+111    pub stride: usize,
+112    /// The Explicit Congestion Notification bits for the datagram(s) in the buffer
+113    pub ecn: Option<EcnCodepoint>,
+114    /// The destination IP address which was encoded in this datagram
+115    ///
+116    /// Populated on platforms: Windows, Linux, Android (API level > 25),
+117    /// FreeBSD, OpenBSD, NetBSD, macOS, and iOS.
+118    pub dst_ip: Option<IpAddr>,
+119    /// The interface index of the interface on which the datagram was received
+120    pub interface_index: Option<u32>,
+121}
+122
+123impl Default for RecvMeta {
+124    /// Constructs a value with arbitrary fields, intended to be overwritten
+125    fn default() -> Self {
+126        Self {
+127            addr: SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0),
+128            len: 0,
+129            stride: 0,
+130            ecn: None,
+131            dst_ip: None,
+132            interface_index: None,
+133        }
+134    }
+135}
+136
+137/// An outgoing packet
+138#[derive(Debug, Clone)]
+139pub struct Transmit<'a> {
+140    /// The socket this datagram should be sent to
+141    pub destination: SocketAddr,
+142    /// Explicit congestion notification bits to set on the packet
+143    pub ecn: Option<EcnCodepoint>,
+144    /// Contents of the datagram
+145    pub contents: &'a [u8],
+146    /// The segment size if this transmission contains multiple datagrams.
+147    /// This is `None` if the transmit only contains a single datagram
+148    pub segment_size: Option<usize>,
+149    /// Optional source IP address for the datagram
+150    pub src_ip: Option<IpAddr>,
+151}
+152
+153impl Transmit<'_> {
+154    /// Computes the effective segment-size of the packet.
+155    ///
+156    /// Some (older) network drivers don't like being told to do GSO even if
+157    /// there is effectively only a single segment.
+158    /// (i.e. `segment_size == contents.len()`)
+159    /// Additionally, a `segment_size` that is greater than the content also
+160    /// means there is effectively only a single segment.
+161    /// This case is actually quite common when splitting up a prepared GSO batch
+162    /// again after GSO has been disabled because the last datagram in a GSO
+163    /// batch is allowed to be smaller than the segment size.
+164    fn effective_segment_size(&self) -> Option<usize> {
+165        match self.segment_size? {
+166            size if size >= self.contents.len() => None,
+167            size => Some(size),
+168        }
+169    }
+170}
+171
+172/// Log at most 1 IO error per minute
+173#[cfg(not(wasm_browser))]
+174const IO_ERROR_LOG_INTERVAL: Duration = std::time::Duration::from_secs(60);
+175
+176/// Logs a warning message when sendmsg fails
+177///
+178/// Logging will only be performed if at least [`IO_ERROR_LOG_INTERVAL`]
+179/// has elapsed since the last error was logged.
+180#[cfg(all(not(wasm_browser), any(feature = "tracing-log", feature = "log")))]
+181fn log_sendmsg_error(
+182    last_send_error: &Mutex<Instant>,
+183    err: impl core::fmt::Debug,
+184    transmit: &Transmit<'_>,
+185) {
+186    let now = Instant::now();
+187    let last_send_error = &mut *last_send_error.lock().expect("poisend lock");
+188    if now.saturating_duration_since(*last_send_error) > IO_ERROR_LOG_INTERVAL {
+189        *last_send_error = now;
+190        log::warn!(
+191            "sendmsg error: {:?}, Transmit: {{ destination: {:?}, src_ip: {:?}, ecn: {:?}, len: {:?}, segment_size: {:?} }}",
+192            err,
+193            transmit.destination,
+194            transmit.src_ip,
+195            transmit.ecn,
+196            transmit.contents.len(),
+197            transmit.segment_size
+198        );
+199    }
+200}
+201
+202// No-op
+203#[cfg(not(any(wasm_browser, feature = "tracing-log", feature = "log")))]
+204fn log_sendmsg_error(_: &Mutex<Instant>, _: impl core::fmt::Debug, _: &Transmit<'_>) {}
+205
+206/// A borrowed UDP socket
+207///
+208/// On Unix, constructible via `From<T: AsFd>`. On Windows, constructible via `From<T:
+209/// AsSocket>`.
+210// Wrapper around socket2 to avoid making it a public dependency and incurring stability risk
+211#[cfg(not(wasm_browser))]
+212pub struct UdpSockRef<'a>(socket2::SockRef<'a>);
+213
+214#[cfg(unix)]
+215impl<'s, S> From<&'s S> for UdpSockRef<'s>
+216where
+217    S: AsFd,
+218{
+219    fn from(socket: &'s S) -> Self {
+220        Self(socket.into())
+221    }
+222}
+223
+224#[cfg(windows)]
+225impl<'s, S> From<&'s S> for UdpSockRef<'s>
+226where
+227    S: AsSocket,
+228{
+229    fn from(socket: &'s S) -> Self {
+230        Self(socket.into())
+231    }
+232}
+233
+234/// Explicit congestion notification codepoint
+235#[repr(u8)]
+236#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+237pub enum EcnCodepoint {
+238    /// The ECT(0) codepoint, indicating that an endpoint is ECN-capable
+239    Ect0 = 0b10,
+240    /// The ECT(1) codepoint, indicating that an endpoint is ECN-capable
+241    Ect1 = 0b01,
+242    /// The CE codepoint, signalling that congestion was experienced
+243    Ce = 0b11,
+244}
+245
+246impl EcnCodepoint {
+247    /// Create new object from the given bits
+248    pub fn from_bits(x: u8) -> Option<Self> {
+249        use EcnCodepoint::*;
+250        Some(match x & 0b11 {
+251            0b10 => Ect0,
+252            0b01 => Ect1,
+253            0b11 => Ce,
+254            _ => {
+255                return None;
+256            }
+257        })
+258    }
+259}
+260
+261#[cfg(test)]
+262mod tests {
+263    use std::net::Ipv4Addr;
+264
+265    use super::*;
+266
+267    #[test]
+268    fn effective_segment_size() {
+269        assert_eq!(
+270            make_transmit(&[0u8; 10], Some(15)).effective_segment_size(),
+271            None,
+272            "segment_size > content_len should yield no effective segment_size"
+273        );
+274        assert_eq!(
+275            make_transmit(&[0u8; 10], Some(10)).effective_segment_size(),
+276            None,
+277            "segment_size == content_len should yield no effective segment_size"
+278        );
+279        assert_eq!(
+280            make_transmit(&[0u8; 10], None).effective_segment_size(),
+281            None,
+282            "no segment_size should yield no effective segment_size"
+283        );
+284        assert_eq!(
+285            make_transmit(&[0u8; 10], Some(5)).effective_segment_size(),
+286            Some(5),
+287            "segment_size < content_len should yield effective segment_size"
+288        );
+289    }
+290
+291    fn make_transmit(contents: &[u8], segment_size: Option<usize>) -> Transmit<'_> {
+292        Transmit {
+293            destination: SocketAddr::from((Ipv4Addr::UNSPECIFIED, 1)),
+294            ecn: None,
+295            contents,
+296            segment_size,
+297            src_ip: None,
+298        }
+299    }
+300}
\ No newline at end of file diff --git a/pr/395/docs/src/iroh_quinn_udp/unix.rs.html b/pr/395/docs/src/iroh_quinn_udp/unix.rs.html new file mode 100644 index 000000000..9ff99fbc9 --- /dev/null +++ b/pr/395/docs/src/iroh_quinn_udp/unix.rs.html @@ -0,0 +1,1079 @@ +unix.rs - source

iroh_quinn_udp/
unix.rs

1#[cfg(not(any(apple, target_os = "openbsd", solarish)))]
+2use std::ptr;
+3use std::{
+4    io::{self, IoSliceMut},
+5    mem::{self, MaybeUninit},
+6    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
+7    num::NonZeroUsize,
+8    os::unix::io::AsRawFd,
+9    sync::{
+10        Mutex,
+11        atomic::{AtomicBool, AtomicUsize, Ordering},
+12    },
+13    time::Instant,
+14};
+15
+16use socket2::SockRef;
+17
+18use super::{
+19    EcnCodepoint, IO_ERROR_LOG_INTERVAL, RecvMeta, Transmit, UdpSockRef, cmsg, log_sendmsg_error,
+20};
+21
+22// Adapted from https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/bsd/sys/socket_private.h
+23#[cfg(apple_fast)]
+24#[repr(C)]
+25#[allow(non_camel_case_types)]
+26pub(crate) struct msghdr_x {
+27    pub msg_name: *mut libc::c_void,
+28    pub msg_namelen: libc::socklen_t,
+29    pub msg_iov: *mut libc::iovec,
+30    pub msg_iovlen: libc::c_int,
+31    pub msg_control: *mut libc::c_void,
+32    pub msg_controllen: libc::socklen_t,
+33    pub msg_flags: libc::c_int,
+34    pub msg_datalen: usize,
+35}
+36
+37#[cfg(apple_fast)]
+38unsafe extern "C" {
+39    fn recvmsg_x(
+40        s: libc::c_int,
+41        msgp: *const msghdr_x,
+42        cnt: libc::c_uint,
+43        flags: libc::c_int,
+44    ) -> isize;
+45
+46    fn sendmsg_x(
+47        s: libc::c_int,
+48        msgp: *const msghdr_x,
+49        cnt: libc::c_uint,
+50        flags: libc::c_int,
+51    ) -> isize;
+52}
+53
+54#[cfg(target_os = "freebsd")]
+55type IpTosTy = libc::c_uchar;
+56#[cfg(not(any(target_os = "freebsd", target_os = "netbsd")))]
+57type IpTosTy = libc::c_int;
+58
+59/// Tokio-compatible UDP socket with some useful specializations.
+60///
+61/// Unlike a standard tokio UDP socket, this allows ECN bits to be read and written on some
+62/// platforms.
+63#[derive(Debug)]
+64pub struct UdpSocketState {
+65    last_send_error: Mutex<Instant>,
+66    max_gso_segments: AtomicUsize,
+67    gro_segments: NonZeroUsize,
+68    may_fragment: bool,
+69
+70    /// True if we have received EINVAL error from `sendmsg` system call at least once.
+71    ///
+72    /// If enabled, we assume that old kernel is used and switch to fallback mode.
+73    /// In particular, we do not use IP_TOS cmsg_type in this case,
+74    /// which is not supported on Linux <3.13 and results in not sending the UDP packet at all.
+75    sendmsg_einval: AtomicBool,
+76}
+77
+78impl UdpSocketState {
+79    pub fn new(sock: UdpSockRef<'_>) -> io::Result<Self> {
+80        let io = sock.0;
+81        let mut cmsg_platform_space = 0;
+82        if cfg!(target_os = "linux")
+83            || cfg!(bsd)
+84            || cfg!(apple)
+85            || cfg!(target_os = "android")
+86            || cfg!(solarish)
+87        {
+88            cmsg_platform_space +=
+89                unsafe { libc::CMSG_SPACE(mem::size_of::<libc::in6_pktinfo>() as _) as usize };
+90        }
+91
+92        assert!(
+93            CMSG_LEN
+94                >= unsafe { libc::CMSG_SPACE(mem::size_of::<libc::c_int>() as _) as usize }
+95                    + cmsg_platform_space
+96        );
+97        assert!(
+98            mem::align_of::<libc::cmsghdr>() <= mem::align_of::<cmsg::Aligned<[u8; 0]>>(),
+99            "control message buffers will be misaligned"
+100        );
+101
+102        io.set_nonblocking(true)?;
+103
+104        let addr = io.local_addr()?;
+105        let is_ipv4 = addr.family() == libc::AF_INET as libc::sa_family_t;
+106
+107        // mac and ios do not support IP_RECVTOS on dual-stack sockets :(
+108        // older macos versions also don't have the flag and will error out if we don't ignore it
+109        #[cfg(not(any(
+110            target_os = "openbsd",
+111            target_os = "netbsd",
+112            target_os = "dragonfly",
+113            solarish
+114        )))]
+115        if (is_ipv4 || !io.only_v6()?)
+116            && let Err(_err) =
+117                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVTOS, OPTION_ON)
+118        {
+119            crate::log::debug!("Ignoring error setting IP_RECVTOS on socket: {_err:?}");
+120        }
+121
+122        let mut may_fragment = false;
+123        #[cfg(any(target_os = "linux", target_os = "android"))]
+124        {
+125            // opportunistically try to enable GRO. See gro::gro_segments().
+126            let _ = set_socket_option(&*io, libc::SOL_UDP, libc::UDP_GRO, OPTION_ON);
+127
+128            // Forbid IPv4 fragmentation. Set even for IPv6 to account for IPv6 mapped IPv4 addresses.
+129            // Set `may_fragment` to `true` if this option is not supported on the platform.
+130            may_fragment |= !set_socket_option_supported(
+131                &*io,
+132                libc::IPPROTO_IP,
+133                libc::IP_MTU_DISCOVER,
+134                libc::IP_PMTUDISC_PROBE,
+135            )?;
+136
+137            if is_ipv4 {
+138                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_PKTINFO, OPTION_ON)?;
+139            } else {
+140                // Set `may_fragment` to `true` if this option is not supported on the platform.
+141                may_fragment |= !set_socket_option_supported(
+142                    &*io,
+143                    libc::IPPROTO_IPV6,
+144                    libc::IPV6_MTU_DISCOVER,
+145                    libc::IPV6_PMTUDISC_PROBE,
+146                )?;
+147            }
+148        }
+149        #[cfg(any(target_os = "freebsd", apple))]
+150        {
+151            if is_ipv4 {
+152                // Set `may_fragment` to `true` if this option is not supported on the platform.
+153                may_fragment |= !set_socket_option_supported(
+154                    &*io,
+155                    libc::IPPROTO_IP,
+156                    libc::IP_DONTFRAG,
+157                    OPTION_ON,
+158                )?;
+159            }
+160        }
+161        #[cfg(any(bsd, apple, solarish))]
+162        // IP_RECVDSTADDR == IP_SENDSRCADDR on FreeBSD
+163        // macOS uses only IP_RECVDSTADDR, no IP_SENDSRCADDR on macOS (the same on Solaris)
+164        // macOS also supports IP_PKTINFO
+165        {
+166            if is_ipv4 {
+167                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, OPTION_ON)?;
+168            }
+169        }
+170
+171        // Options standardized in RFC 3542
+172        if !is_ipv4 {
+173            set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, OPTION_ON)?;
+174            set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVTCLASS, OPTION_ON)?;
+175            // Linux's IP_PMTUDISC_PROBE allows us to operate under interface MTU rather than the
+176            // kernel's path MTU guess, but actually disabling fragmentation requires this too. See
+177            // __ip6_append_data in ip6_output.c.
+178            // Set `may_fragment` to `true` if this option is not supported on the platform.
+179            may_fragment |= !set_socket_option_supported(
+180                &*io,
+181                libc::IPPROTO_IPV6,
+182                libc::IPV6_DONTFRAG,
+183                OPTION_ON,
+184            )?;
+185        }
+186
+187        let now = Instant::now();
+188        Ok(Self {
+189            last_send_error: Mutex::new(now.checked_sub(2 * IO_ERROR_LOG_INTERVAL).unwrap_or(now)),
+190            max_gso_segments: AtomicUsize::new(gso::max_gso_segments()),
+191            gro_segments: gro::gro_segments(),
+192            may_fragment,
+193            sendmsg_einval: AtomicBool::new(false),
+194        })
+195    }
+196
+197    /// Sends a [`Transmit`] on the given socket.
+198    ///
+199    /// This function will only ever return errors of kind [`io::ErrorKind::WouldBlock`].
+200    /// All other errors will be logged and converted to `Ok`.
+201    ///
+202    /// UDP transmission errors are considered non-fatal because higher-level protocols must
+203    /// employ retransmits and timeouts anyway in order to deal with UDP's unreliable nature.
+204    /// Thus, logging is most likely the only thing you can do with these errors.
+205    ///
+206    /// If you would like to handle these errors yourself, use [`UdpSocketState::try_send`]
+207    /// instead.
+208    pub fn send(&self, socket: UdpSockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+209        match send(self, socket.0, transmit) {
+210            Ok(()) => Ok(()),
+211            Err(e) if e.kind() == io::ErrorKind::WouldBlock => Err(e),
+212            // - EMSGSIZE is expected for MTU probes. Future work might be able to avoid
+213            //   these by automatically clamping the MTUD upper bound to the interface MTU.
+214            Err(e) if e.raw_os_error() == Some(libc::EMSGSIZE) => Ok(()),
+215            Err(e) => {
+216                log_sendmsg_error(&self.last_send_error, e, transmit);
+217
+218                Ok(())
+219            }
+220        }
+221    }
+222
+223    /// Sends a [`Transmit`] on the given socket without any additional error handling.
+224    pub fn try_send(&self, socket: UdpSockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+225        send(self, socket.0, transmit)
+226    }
+227
+228    pub fn recv(
+229        &self,
+230        socket: UdpSockRef<'_>,
+231        bufs: &mut [IoSliceMut<'_>],
+232        meta: &mut [RecvMeta],
+233    ) -> io::Result<usize> {
+234        recv(socket.0, bufs, meta)
+235    }
+236
+237    /// The maximum amount of segments which can be transmitted if a platform
+238    /// supports Generic Send Offload (GSO).
+239    ///
+240    /// This is 1 if the platform doesn't support GSO. Subject to change if errors are detected
+241    /// while using GSO.
+242    #[inline]
+243    pub fn max_gso_segments(&self) -> NonZeroUsize {
+244        self.max_gso_segments
+245            .load(Ordering::Relaxed)
+246            .try_into()
+247            .expect("must have non zero GSO segments")
+248    }
+249
+250    /// The number of segments to read when GRO is enabled. Used as a factor to
+251    /// compute the receive buffer size.
+252    ///
+253    /// Returns 1 if the platform doesn't support GRO.
+254    #[inline]
+255    pub fn gro_segments(&self) -> NonZeroUsize {
+256        self.gro_segments
+257    }
+258
+259    /// Resize the send buffer of `socket` to `bytes`
+260    #[inline]
+261    pub fn set_send_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
+262        socket.0.set_send_buffer_size(bytes)
+263    }
+264
+265    /// Resize the receive buffer of `socket` to `bytes`
+266    #[inline]
+267    pub fn set_recv_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
+268        socket.0.set_recv_buffer_size(bytes)
+269    }
+270
+271    /// Get the size of the `socket` send buffer
+272    #[inline]
+273    pub fn send_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
+274        socket.0.send_buffer_size()
+275    }
+276
+277    /// Get the size of the `socket` receive buffer
+278    #[inline]
+279    pub fn recv_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
+280        socket.0.recv_buffer_size()
+281    }
+282
+283    /// Whether transmitted datagrams might get fragmented by the IP layer
+284    ///
+285    /// Returns `false` on targets which employ e.g. the `IPV6_DONTFRAG` socket option.
+286    #[inline]
+287    pub fn may_fragment(&self) -> bool {
+288        self.may_fragment
+289    }
+290
+291    /// Returns true if we previously got an EINVAL error from `sendmsg` syscall.
+292    fn sendmsg_einval(&self) -> bool {
+293        self.sendmsg_einval.load(Ordering::Relaxed)
+294    }
+295
+296    /// Sets the flag indicating we got EINVAL error from `sendmsg` syscall.
+297    #[cfg(not(any(apple, target_os = "openbsd", target_os = "netbsd")))]
+298    fn set_sendmsg_einval(&self) {
+299        self.sendmsg_einval.store(true, Ordering::Relaxed)
+300    }
+301}
+302
+303#[cfg(not(any(apple, target_os = "openbsd", target_os = "netbsd")))]
+304fn send(
+305    #[allow(unused_variables)] // only used on Linux
+306    state: &UdpSocketState,
+307    io: SockRef<'_>,
+308    transmit: &Transmit<'_>,
+309) -> io::Result<()> {
+310    #[allow(unused_mut)] // only mutable on FreeBSD
+311    let mut encode_src_ip = true;
+312    #[cfg(target_os = "freebsd")]
+313    {
+314        let addr = io.local_addr()?;
+315        let is_ipv4 = addr.family() == libc::AF_INET as libc::sa_family_t;
+316        if is_ipv4 {
+317            if let Some(socket) = addr.as_socket_ipv4() {
+318                encode_src_ip = socket.ip() == &Ipv4Addr::UNSPECIFIED;
+319            }
+320        }
+321    }
+322    let mut msg_hdr: libc::msghdr = unsafe { mem::zeroed() };
+323    let mut iovec: libc::iovec = unsafe { mem::zeroed() };
+324    let mut cmsgs = cmsg::Aligned([0u8; CMSG_LEN]);
+325    let dst_addr = socket2::SockAddr::from(transmit.destination);
+326    prepare_msg(
+327        transmit,
+328        &dst_addr,
+329        &mut msg_hdr,
+330        &mut iovec,
+331        &mut cmsgs,
+332        encode_src_ip,
+333        state.sendmsg_einval(),
+334    );
+335
+336    loop {
+337        let n = unsafe { libc::sendmsg(io.as_raw_fd(), &msg_hdr, 0) };
+338
+339        if n >= 0 {
+340            return Ok(());
+341        }
+342
+343        let e = io::Error::last_os_error();
+344        match e.kind() {
+345            // Retry the transmission
+346            io::ErrorKind::Interrupted => continue,
+347            io::ErrorKind::WouldBlock => return Err(e),
+348            _ => {
+349                // Some network adapters and drivers do not support GSO. Unfortunately, Linux
+350                // offers no easy way for us to detect this short of an EIO or sometimes EINVAL
+351                // when we try to actually send datagrams using it.
+352                #[cfg(any(target_os = "linux", target_os = "android"))]
+353                if let Some(libc::EIO) | Some(libc::EINVAL) = e.raw_os_error() {
+354                    // Prevent new transmits from being scheduled using GSO. Existing GSO transmits
+355                    // may already be in the pipeline, so we need to tolerate additional failures.
+356                    if state.max_gso_segments().get() > 1 {
+357                        crate::log::info!(
+358                            "`libc::sendmsg` failed with {e}; halting segmentation offload"
+359                        );
+360                        state
+361                            .max_gso_segments
+362                            .store(1, std::sync::atomic::Ordering::Relaxed);
+363                    }
+364                }
+365
+366                // Some arguments to `sendmsg` are not supported. Switch to
+367                // fallback mode and retry if we haven't already.
+368                if e.raw_os_error() == Some(libc::EINVAL) && !state.sendmsg_einval() {
+369                    state.set_sendmsg_einval();
+370                    prepare_msg(
+371                        transmit,
+372                        &dst_addr,
+373                        &mut msg_hdr,
+374                        &mut iovec,
+375                        &mut cmsgs,
+376                        encode_src_ip,
+377                        state.sendmsg_einval(),
+378                    );
+379                    continue;
+380                }
+381
+382                return Err(e);
+383            }
+384        }
+385    }
+386}
+387
+388#[cfg(apple_fast)]
+389fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+390    let mut hdrs = unsafe { mem::zeroed::<[msghdr_x; BATCH_SIZE]>() };
+391    let mut iovs = unsafe { mem::zeroed::<[libc::iovec; BATCH_SIZE]>() };
+392    let mut ctrls = [cmsg::Aligned([0u8; CMSG_LEN]); BATCH_SIZE];
+393    let addr = socket2::SockAddr::from(transmit.destination);
+394    let segment_size = transmit.segment_size.unwrap_or(transmit.contents.len());
+395    let mut cnt = 0;
+396    debug_assert!(transmit.contents.len().div_ceil(segment_size) <= BATCH_SIZE);
+397    for (i, chunk) in transmit
+398        .contents
+399        .chunks(segment_size)
+400        .enumerate()
+401        .take(BATCH_SIZE)
+402    {
+403        prepare_msg(
+404            &Transmit {
+405                destination: transmit.destination,
+406                ecn: transmit.ecn,
+407                contents: chunk,
+408                segment_size: Some(chunk.len()),
+409                src_ip: transmit.src_ip,
+410            },
+411            &addr,
+412            &mut hdrs[i],
+413            &mut iovs[i],
+414            &mut ctrls[i],
+415            true,
+416            state.sendmsg_einval(),
+417        );
+418        hdrs[i].msg_datalen = chunk.len();
+419        cnt += 1;
+420    }
+421    loop {
+422        let n = unsafe { sendmsg_x(io.as_raw_fd(), hdrs.as_ptr(), cnt as u32, 0) };
+423
+424        if n >= 0 {
+425            return Ok(());
+426        }
+427
+428        let e = io::Error::last_os_error();
+429        match e.kind() {
+430            // Retry the transmission
+431            io::ErrorKind::Interrupted => continue,
+432            _ => return Err(e),
+433        }
+434    }
+435}
+436
+437#[cfg(any(target_os = "openbsd", target_os = "netbsd", apple_slow))]
+438fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+439    let mut hdr: libc::msghdr = unsafe { mem::zeroed() };
+440    let mut iov: libc::iovec = unsafe { mem::zeroed() };
+441    let mut ctrl = cmsg::Aligned([0u8; CMSG_LEN]);
+442    let addr = socket2::SockAddr::from(transmit.destination);
+443    prepare_msg(
+444        transmit,
+445        &addr,
+446        &mut hdr,
+447        &mut iov,
+448        &mut ctrl,
+449        cfg!(apple) || cfg!(target_os = "openbsd") || cfg!(target_os = "netbsd"),
+450        state.sendmsg_einval(),
+451    );
+452    loop {
+453        let n = unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) };
+454
+455        if n >= 0 {
+456            return Ok(());
+457        }
+458
+459        let e = io::Error::last_os_error();
+460        match e.kind() {
+461            // Retry the transmission
+462            io::ErrorKind::Interrupted => continue,
+463            _ => return Err(e),
+464        }
+465    }
+466}
+467
+468#[cfg(not(any(
+469    apple,
+470    target_os = "openbsd",
+471    target_os = "netbsd",
+472    target_os = "dragonfly",
+473    solarish
+474)))]
+475fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize> {
+476    let mut names = [MaybeUninit::<libc::sockaddr_storage>::uninit(); BATCH_SIZE];
+477    let mut ctrls = [cmsg::Aligned(MaybeUninit::<[u8; CMSG_LEN]>::uninit()); BATCH_SIZE];
+478    let mut hdrs = unsafe { mem::zeroed::<[libc::mmsghdr; BATCH_SIZE]>() };
+479    let max_msg_count = bufs.len().min(BATCH_SIZE);
+480    for i in 0..max_msg_count {
+481        prepare_recv(
+482            &mut bufs[i],
+483            &mut names[i],
+484            &mut ctrls[i],
+485            &mut hdrs[i].msg_hdr,
+486        );
+487    }
+488    let msg_count = loop {
+489        let n = unsafe {
+490            libc::recvmmsg(
+491                io.as_raw_fd(),
+492                hdrs.as_mut_ptr(),
+493                bufs.len().min(BATCH_SIZE) as _,
+494                0,
+495                ptr::null_mut::<libc::timespec>(),
+496            )
+497        };
+498
+499        if n >= 0 {
+500            break n;
+501        }
+502
+503        let e = io::Error::last_os_error();
+504        match e.kind() {
+505            // Retry receiving
+506            io::ErrorKind::Interrupted => continue,
+507            _ => return Err(e),
+508        }
+509    };
+510    for i in 0..(msg_count as usize) {
+511        meta[i] = decode_recv(&names[i], &hdrs[i].msg_hdr, hdrs[i].msg_len as usize)?;
+512    }
+513    Ok(msg_count as usize)
+514}
+515
+516#[cfg(apple_fast)]
+517fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize> {
+518    let mut names = [MaybeUninit::<libc::sockaddr_storage>::uninit(); BATCH_SIZE];
+519    // MacOS 10.15 `recvmsg_x` does not override the `msghdr_x`
+520    // `msg_controllen`. Thus, after the call to `recvmsg_x`, one does not know
+521    // which control messages have been written to. To prevent reading
+522    // uninitialized memory, do not use `MaybeUninit` for `ctrls`, instead
+523    // initialize `ctrls` with `0`s. A control message of all `0`s is
+524    // automatically skipped by `libc::CMSG_NXTHDR`.
+525    let mut ctrls = [cmsg::Aligned([0u8; CMSG_LEN]); BATCH_SIZE];
+526    let mut hdrs = unsafe { mem::zeroed::<[msghdr_x; BATCH_SIZE]>() };
+527    let max_msg_count = bufs.len().min(BATCH_SIZE);
+528    for i in 0..max_msg_count {
+529        prepare_recv(&mut bufs[i], &mut names[i], &mut ctrls[i], &mut hdrs[i]);
+530    }
+531    let msg_count = loop {
+532        let n = unsafe { recvmsg_x(io.as_raw_fd(), hdrs.as_mut_ptr(), max_msg_count as _, 0) };
+533
+534        if n >= 0 {
+535            break n;
+536        }
+537
+538        let e = io::Error::last_os_error();
+539        match e.kind() {
+540            // Retry receiving
+541            io::ErrorKind::Interrupted => continue,
+542            _ => return Err(e),
+543        }
+544    };
+545    for i in 0..(msg_count as usize) {
+546        meta[i] = decode_recv(&names[i], &hdrs[i], hdrs[i].msg_datalen as usize)?;
+547    }
+548    Ok(msg_count as usize)
+549}
+550
+551#[cfg(any(
+552    target_os = "openbsd",
+553    target_os = "netbsd",
+554    target_os = "dragonfly",
+555    solarish,
+556    apple_slow
+557))]
+558fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize> {
+559    let mut name = MaybeUninit::<libc::sockaddr_storage>::uninit();
+560    let mut ctrl = cmsg::Aligned(MaybeUninit::<[u8; CMSG_LEN]>::uninit());
+561    let mut hdr = unsafe { mem::zeroed::<libc::msghdr>() };
+562    prepare_recv(&mut bufs[0], &mut name, &mut ctrl, &mut hdr);
+563    let n = loop {
+564        let n = unsafe { libc::recvmsg(io.as_raw_fd(), &mut hdr, 0) };
+565
+566        if hdr.msg_flags & libc::MSG_TRUNC != 0 {
+567            continue;
+568        }
+569
+570        if n >= 0 {
+571            break n;
+572        }
+573
+574        let e = io::Error::last_os_error();
+575        match e.kind() {
+576            // Retry receiving
+577            io::ErrorKind::Interrupted => continue,
+578            _ => return Err(e),
+579        }
+580    };
+581    meta[0] = decode_recv(&name, &hdr, n as usize)?;
+582    Ok(1)
+583}
+584
+585const CMSG_LEN: usize = 88;
+586
+587fn prepare_msg(
+588    transmit: &Transmit<'_>,
+589    dst_addr: &socket2::SockAddr,
+590    #[cfg(not(apple_fast))] hdr: &mut libc::msghdr,
+591    #[cfg(apple_fast)] hdr: &mut msghdr_x,
+592    iov: &mut libc::iovec,
+593    ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
+594    #[allow(unused_variables)] // only used on FreeBSD & macOS
+595    encode_src_ip: bool,
+596    sendmsg_einval: bool,
+597) {
+598    iov.iov_base = transmit.contents.as_ptr() as *const _ as *mut _;
+599    iov.iov_len = transmit.contents.len();
+600
+601    // SAFETY: Casting the pointer to a mutable one is legal,
+602    // as sendmsg is guaranteed to not alter the mutable pointer
+603    // as per the POSIX spec. See the section on the sys/socket.h
+604    // header for details. The type is only mutable in the first
+605    // place because it is reused by recvmsg as well.
+606    let name = dst_addr.as_ptr() as *mut libc::c_void;
+607    let namelen = dst_addr.len();
+608    hdr.msg_name = name as *mut _;
+609    hdr.msg_namelen = namelen;
+610    hdr.msg_iov = iov;
+611    hdr.msg_iovlen = 1;
+612
+613    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+614    hdr.msg_controllen = CMSG_LEN as _;
+615    let mut encoder = unsafe { cmsg::Encoder::new(hdr) };
+616    let ecn = transmit.ecn.map_or(0, |x| x as libc::c_int);
+617    // True for IPv4 or IPv4-Mapped IPv6
+618    let is_ipv4 = transmit.destination.is_ipv4()
+619        || matches!(transmit.destination.ip(), IpAddr::V6(addr) if addr.to_ipv4_mapped().is_some());
+620    if is_ipv4 {
+621        if !sendmsg_einval {
+622            #[cfg(not(target_os = "netbsd"))]
+623            {
+624                encoder.push(libc::IPPROTO_IP, libc::IP_TOS, ecn as IpTosTy);
+625            }
+626        }
+627    } else {
+628        encoder.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
+629    }
+630
+631    if let Some(segment_size) = transmit.effective_segment_size() {
+632        gso::set_segment_size(&mut encoder, segment_size as u16);
+633    }
+634
+635    if let Some(ip) = &transmit.src_ip {
+636        match ip {
+637            IpAddr::V4(v4) => {
+638                #[cfg(any(target_os = "linux", target_os = "android"))]
+639                {
+640                    let pktinfo = libc::in_pktinfo {
+641                        ipi_ifindex: 0,
+642                        ipi_spec_dst: libc::in_addr {
+643                            s_addr: u32::from_ne_bytes(v4.octets()),
+644                        },
+645                        ipi_addr: libc::in_addr { s_addr: 0 },
+646                    };
+647                    encoder.push(libc::IPPROTO_IP, libc::IP_PKTINFO, pktinfo);
+648                }
+649                #[cfg(any(bsd, apple, solarish))]
+650                {
+651                    if encode_src_ip {
+652                        let addr = libc::in_addr {
+653                            s_addr: u32::from_ne_bytes(v4.octets()),
+654                        };
+655                        encoder.push(libc::IPPROTO_IP, libc::IP_RECVDSTADDR, addr);
+656                    }
+657                }
+658            }
+659            IpAddr::V6(v6) => {
+660                let pktinfo = libc::in6_pktinfo {
+661                    ipi6_ifindex: 0,
+662                    ipi6_addr: libc::in6_addr {
+663                        s6_addr: v6.octets(),
+664                    },
+665                };
+666                encoder.push(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO, pktinfo);
+667            }
+668        }
+669    }
+670
+671    encoder.finish();
+672}
+673
+674#[cfg(not(apple_fast))]
+675fn prepare_recv(
+676    buf: &mut IoSliceMut<'_>,
+677    name: &mut MaybeUninit<libc::sockaddr_storage>,
+678    ctrl: &mut cmsg::Aligned<MaybeUninit<[u8; CMSG_LEN]>>,
+679    hdr: &mut libc::msghdr,
+680) {
+681    hdr.msg_name = name.as_mut_ptr() as _;
+682    hdr.msg_namelen = mem::size_of::<libc::sockaddr_storage>() as _;
+683    hdr.msg_iov = buf as *mut IoSliceMut<'_> as *mut libc::iovec;
+684    hdr.msg_iovlen = 1;
+685    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+686    hdr.msg_controllen = CMSG_LEN as _;
+687    hdr.msg_flags = 0;
+688}
+689
+690#[cfg(apple_fast)]
+691fn prepare_recv(
+692    buf: &mut IoSliceMut<'_>,
+693    name: &mut MaybeUninit<libc::sockaddr_storage>,
+694    ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
+695    hdr: &mut msghdr_x,
+696) {
+697    hdr.msg_name = name.as_mut_ptr() as _;
+698    hdr.msg_namelen = mem::size_of::<libc::sockaddr_storage>() as _;
+699    hdr.msg_iov = buf as *mut IoSliceMut<'_> as *mut libc::iovec;
+700    hdr.msg_iovlen = 1;
+701    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+702    hdr.msg_controllen = CMSG_LEN as _;
+703    hdr.msg_flags = 0;
+704    hdr.msg_datalen = buf.len();
+705}
+706
+707fn decode_recv<M: cmsg::MsgHdr<ControlMessage = libc::cmsghdr>>(
+708    name: &MaybeUninit<libc::sockaddr_storage>,
+709    hdr: &M,
+710    len: usize,
+711) -> io::Result<RecvMeta> {
+712    let name = unsafe { name.assume_init() };
+713    let mut ecn_bits = 0;
+714    let mut dst_ip = None;
+715    let mut interface_index = None;
+716    #[allow(unused_mut)] // only mutable on Linux
+717    let mut stride = len;
+718
+719    let cmsg_iter = unsafe { cmsg::Iter::new(hdr) };
+720    for cmsg in cmsg_iter {
+721        match (cmsg.cmsg_level, cmsg.cmsg_type) {
+722            (libc::IPPROTO_IP, libc::IP_TOS) => unsafe {
+723                ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+724            },
+725            // FreeBSD uses IP_RECVTOS here, and we can be liberal because cmsgs are opt-in.
+726            #[cfg(not(any(
+727                target_os = "openbsd",
+728                target_os = "netbsd",
+729                target_os = "dragonfly",
+730                solarish
+731            )))]
+732            (libc::IPPROTO_IP, libc::IP_RECVTOS) => unsafe {
+733                ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+734            },
+735            (libc::IPPROTO_IPV6, libc::IPV6_TCLASS) => unsafe {
+736                // Temporary hack around broken macos ABI. Remove once upstream fixes it.
+737                // https://bugreport.apple.com/web/?problemID=48761855
+738                #[allow(clippy::unnecessary_cast)] // cmsg.cmsg_len defined as size_t
+739                if cfg!(apple)
+740                    && cmsg.cmsg_len as usize == libc::CMSG_LEN(mem::size_of::<u8>() as _) as usize
+741                {
+742                    ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+743                } else {
+744                    ecn_bits = cmsg::decode::<libc::c_int, libc::cmsghdr>(cmsg) as u8;
+745                }
+746            },
+747            #[cfg(any(target_os = "linux", target_os = "android"))]
+748            (libc::IPPROTO_IP, libc::IP_PKTINFO) => {
+749                let pktinfo = unsafe { cmsg::decode::<libc::in_pktinfo, libc::cmsghdr>(cmsg) };
+750                dst_ip = Some(IpAddr::V4(Ipv4Addr::from(
+751                    pktinfo.ipi_addr.s_addr.to_ne_bytes(),
+752                )));
+753                interface_index = Some(pktinfo.ipi_ifindex as u32);
+754            }
+755            #[cfg(any(bsd, apple))]
+756            (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
+757                let in_addr = unsafe { cmsg::decode::<libc::in_addr, libc::cmsghdr>(cmsg) };
+758                dst_ip = Some(IpAddr::V4(Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())));
+759            }
+760            (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
+761                let pktinfo = unsafe { cmsg::decode::<libc::in6_pktinfo, libc::cmsghdr>(cmsg) };
+762                dst_ip = Some(IpAddr::V6(Ipv6Addr::from(pktinfo.ipi6_addr.s6_addr)));
+763                interface_index = Some(pktinfo.ipi6_ifindex as u32);
+764            }
+765            #[cfg(any(target_os = "linux", target_os = "android"))]
+766            (libc::SOL_UDP, libc::UDP_GRO) => unsafe {
+767                stride = cmsg::decode::<libc::c_int, libc::cmsghdr>(cmsg) as usize;
+768            },
+769            _ => {}
+770        }
+771    }
+772
+773    let addr = match libc::c_int::from(name.ss_family) {
+774        libc::AF_INET => {
+775            // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
+776            let addr: &libc::sockaddr_in =
+777                unsafe { &*(&name as *const _ as *const libc::sockaddr_in) };
+778            SocketAddr::V4(SocketAddrV4::new(
+779                Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()),
+780                u16::from_be(addr.sin_port),
+781            ))
+782        }
+783        libc::AF_INET6 => {
+784            // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
+785            let addr: &libc::sockaddr_in6 =
+786                unsafe { &*(&name as *const _ as *const libc::sockaddr_in6) };
+787            SocketAddr::V6(SocketAddrV6::new(
+788                Ipv6Addr::from(addr.sin6_addr.s6_addr),
+789                u16::from_be(addr.sin6_port),
+790                addr.sin6_flowinfo,
+791                addr.sin6_scope_id,
+792            ))
+793        }
+794        f => {
+795            return Err(io::Error::other(format!(
+796                "expected AF_INET or AF_INET6, got {f} in decode_recv"
+797            )));
+798        }
+799    };
+800
+801    Ok(RecvMeta {
+802        len,
+803        stride,
+804        addr,
+805        ecn: EcnCodepoint::from_bits(ecn_bits),
+806        dst_ip,
+807        interface_index,
+808    })
+809}
+810
+811#[cfg(not(apple_slow))]
+812// Chosen somewhat arbitrarily; might benefit from additional tuning.
+813pub(crate) const BATCH_SIZE: usize = 32;
+814
+815#[cfg(apple_slow)]
+816pub(crate) const BATCH_SIZE: usize = 1;
+817
+818#[cfg(any(target_os = "linux", target_os = "android"))]
+819mod gso {
+820    use super::*;
+821    use std::{ffi::CStr, mem, str::FromStr, sync::OnceLock};
+822
+823    // Support for UDP GSO has been added to linux kernel in version 4.18
+824    // https://github.com/torvalds/linux/commit/cb586c63e3fc5b227c51fd8c4cb40b34d3750645
+825    const SUPPORTED_SINCE: KernelVersion = KernelVersion {
+826        version: 4,
+827        major_revision: 18,
+828    };
+829
+830    /// Checks whether GSO support is available by checking the kernel version followed by setting
+831    /// the UDP_SEGMENT option on a socket
+832    pub(crate) fn max_gso_segments() -> usize {
+833        const GSO_SIZE: libc::c_int = 1500;
+834
+835        if !SUPPORTED_BY_CURRENT_KERNEL.get_or_init(supported_by_current_kernel) {
+836            return 1;
+837        }
+838
+839        let socket = match std::net::UdpSocket::bind("[::]:0")
+840            .or_else(|_| std::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)))
+841        {
+842            Ok(socket) => socket,
+843            Err(_) => return 1,
+844        };
+845
+846        // As defined in linux/udp.h
+847        // #define UDP_MAX_SEGMENTS        (1 << 6UL)
+848        match set_socket_option(&socket, libc::SOL_UDP, libc::UDP_SEGMENT, GSO_SIZE) {
+849            Ok(()) => 64,
+850            Err(_e) => {
+851                crate::log::debug!(
+852                    "failed to set `UDP_SEGMENT` socket option ({_e}); setting `max_gso_segments = 1`"
+853                );
+854
+855                1
+856            }
+857        }
+858    }
+859
+860    pub(crate) fn set_segment_size(
+861        encoder: &mut cmsg::Encoder<'_, libc::msghdr>,
+862        segment_size: u16,
+863    ) {
+864        encoder.push(libc::SOL_UDP, libc::UDP_SEGMENT, segment_size);
+865    }
+866
+867    // Avoid calling `supported_by_current_kernel` for each socket by using `OnceLock`.
+868    static SUPPORTED_BY_CURRENT_KERNEL: OnceLock<bool> = OnceLock::new();
+869
+870    fn supported_by_current_kernel() -> bool {
+871        let kernel_version_string = match kernel_version_string() {
+872            Ok(kernel_version_string) => kernel_version_string,
+873            Err(_e) => {
+874                crate::log::warn!("GSO disabled: uname returned {_e}");
+875                return false;
+876            }
+877        };
+878
+879        let Some(kernel_version) = KernelVersion::from_str(&kernel_version_string) else {
+880            crate::log::warn!(
+881                "GSO disabled: failed to parse kernel version ({kernel_version_string})"
+882            );
+883            return false;
+884        };
+885
+886        if kernel_version < SUPPORTED_SINCE {
+887            crate::log::info!("GSO disabled: kernel too old ({kernel_version_string}); need 4.18+",);
+888            return false;
+889        }
+890
+891        true
+892    }
+893
+894    fn kernel_version_string() -> io::Result<String> {
+895        let mut n = unsafe { mem::zeroed() };
+896        let r = unsafe { libc::uname(&mut n) };
+897        if r != 0 {
+898            return Err(io::Error::last_os_error());
+899        }
+900        Ok(unsafe {
+901            CStr::from_ptr(n.release[..].as_ptr())
+902                .to_string_lossy()
+903                .into_owned()
+904        })
+905    }
+906
+907    // https://www.linfo.org/kernel_version_numbering.html
+908    #[derive(Eq, PartialEq, Ord, PartialOrd, Debug)]
+909    struct KernelVersion {
+910        version: u8,
+911        major_revision: u8,
+912    }
+913
+914    impl KernelVersion {
+915        fn from_str(release: &str) -> Option<Self> {
+916            let mut split = release
+917                .split_once('-')
+918                .map(|pair| pair.0)
+919                .unwrap_or(release)
+920                .split('.');
+921
+922            let version = u8::from_str(split.next()?).ok()?;
+923            let major_revision = u8::from_str(split.next()?).ok()?;
+924
+925            Some(Self {
+926                version,
+927                major_revision,
+928            })
+929        }
+930    }
+931
+932    #[cfg(test)]
+933    mod test {
+934        use super::*;
+935
+936        #[test]
+937        fn parse_current_kernel_version_release_string() {
+938            let release = kernel_version_string().unwrap();
+939            KernelVersion::from_str(&release).unwrap();
+940        }
+941
+942        #[test]
+943        fn parse_kernel_version_release_string() {
+944            // These are made up for the test
+945            assert_eq!(
+946                KernelVersion::from_str("4.14"),
+947                Some(KernelVersion {
+948                    version: 4,
+949                    major_revision: 14
+950                })
+951            );
+952            assert_eq!(
+953                KernelVersion::from_str("4.18"),
+954                Some(KernelVersion {
+955                    version: 4,
+956                    major_revision: 18
+957                })
+958            );
+959            // These were seen in the wild
+960            assert_eq!(
+961                KernelVersion::from_str("4.14.186-27095505"),
+962                Some(KernelVersion {
+963                    version: 4,
+964                    major_revision: 14
+965                })
+966            );
+967            assert_eq!(
+968                KernelVersion::from_str("6.8.0-59-generic"),
+969                Some(KernelVersion {
+970                    version: 6,
+971                    major_revision: 8
+972                })
+973            );
+974        }
+975    }
+976}
+977
+978// On Apple platforms using the `sendmsg_x` call, UDP datagram segmentation is not
+979// offloaded to the NIC or even the kernel, but instead done here in user space in
+980// [`send`]) and then passed to the OS as individual `iovec`s (up to `BATCH_SIZE`).
+981#[cfg(not(any(target_os = "linux", target_os = "android")))]
+982mod gso {
+983    use super::*;
+984
+985    pub(super) fn max_gso_segments() -> usize {
+986        #[cfg(apple_fast)]
+987        {
+988            BATCH_SIZE
+989        }
+990        #[cfg(not(apple_fast))]
+991        {
+992            1
+993        }
+994    }
+995
+996    pub(super) fn set_segment_size(
+997        #[cfg(not(apple_fast))] _encoder: &mut cmsg::Encoder<'_, libc::msghdr>,
+998        #[cfg(apple_fast)] _encoder: &mut cmsg::Encoder<'_, msghdr_x>,
+999        _segment_size: u16,
+1000    ) {
+1001    }
+1002}
+1003
+1004#[cfg(any(target_os = "linux", target_os = "android"))]
+1005mod gro {
+1006    use super::*;
+1007
+1008    pub(crate) fn gro_segments() -> NonZeroUsize {
+1009        let socket = match std::net::UdpSocket::bind("[::]:0")
+1010            .or_else(|_| std::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)))
+1011        {
+1012            Ok(socket) => socket,
+1013            Err(_) => return NonZeroUsize::MIN,
+1014        };
+1015
+1016        // As defined in net/ipv4/udp_offload.c
+1017        // #define UDP_GRO_CNT_MAX 64
+1018        //
+1019        // NOTE: this MUST be set to UDP_GRO_CNT_MAX to ensure that the receive buffer size
+1020        // (get_max_udp_payload_size() * gro_segments()) is large enough to hold the largest GRO
+1021        // list the kernel might potentially produce. See
+1022        // https://github.com/quinn-rs/quinn/pull/1354.
+1023        match set_socket_option(&socket, libc::SOL_UDP, libc::UDP_GRO, OPTION_ON) {
+1024            Ok(()) => NonZeroUsize::new(64).expect("known"),
+1025            Err(_) => NonZeroUsize::MIN,
+1026        }
+1027    }
+1028}
+1029
+1030/// Returns whether the given socket option is supported on the current platform
+1031///
+1032/// Yields `Ok(true)` if the option was set successfully, `Ok(false)` if setting
+1033/// the option raised an `ENOPROTOOPT` or `EOPNOTSUPP` error, and `Err` for any other error.
+1034fn set_socket_option_supported(
+1035    socket: &impl AsRawFd,
+1036    level: libc::c_int,
+1037    name: libc::c_int,
+1038    value: libc::c_int,
+1039) -> io::Result<bool> {
+1040    match set_socket_option(socket, level, name, value) {
+1041        Ok(()) => Ok(true),
+1042        Err(err) if err.raw_os_error() == Some(libc::ENOPROTOOPT) => Ok(false),
+1043        Err(err) if err.raw_os_error() == Some(libc::EOPNOTSUPP) => Ok(false),
+1044        Err(err) => Err(err),
+1045    }
+1046}
+1047
+1048fn set_socket_option(
+1049    socket: &impl AsRawFd,
+1050    level: libc::c_int,
+1051    name: libc::c_int,
+1052    value: libc::c_int,
+1053) -> io::Result<()> {
+1054    let rc = unsafe {
+1055        libc::setsockopt(
+1056            socket.as_raw_fd(),
+1057            level,
+1058            name,
+1059            &value as *const _ as _,
+1060            mem::size_of_val(&value) as _,
+1061        )
+1062    };
+1063
+1064    match rc == 0 {
+1065        true => Ok(()),
+1066        false => Err(io::Error::last_os_error()),
+1067    }
+1068}
+1069
+1070const OPTION_ON: libc::c_int = 1;
+1071
+1072#[cfg(not(any(target_os = "linux", target_os = "android")))]
+1073mod gro {
+1074    use std::num::NonZeroUsize;
+1075
+1076    pub(super) fn gro_segments() -> NonZeroUsize {
+1077        NonZeroUsize::MIN
+1078    }
+1079}
\ No newline at end of file diff --git a/pr/395/docs/src/perf/client.rs.html b/pr/395/docs/src/perf/client.rs.html new file mode 100644 index 000000000..37a94ceca --- /dev/null +++ b/pr/395/docs/src/perf/client.rs.html @@ -0,0 +1,419 @@ +client.rs - source

perf/
client.rs

1#[cfg(feature = "json-output")]
+2use std::path::PathBuf;
+3use std::{
+4    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
+5    sync::Arc,
+6    time::{Duration, Instant},
+7};
+8
+9use anyhow::{Context, Result};
+10use bytes::Bytes;
+11use clap::Parser;
+12use quinn::{TokioRuntime, crypto::rustls::QuicClientConfig};
+13use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
+14use tokio::sync::Semaphore;
+15use tracing::{debug, error, info};
+16
+17use crate::{
+18    CommonOpt, PERF_CIPHER_SUITES,
+19    noprotection::NoProtectionClientConfig,
+20    parse_byte_size,
+21    stats::{OpenStreamStats, Stats},
+22};
+23
+24/// Connects to a QUIC perf server and maintains a specified pattern of requests until interrupted
+25#[derive(Parser)]
+26#[clap(name = "client")]
+27pub struct Opt {
+28    /// Host to connect to
+29    #[clap(default_value = "localhost:4433")]
+30    host: String,
+31    /// Override DNS resolution for host
+32    #[clap(long)]
+33    ip: Option<IpAddr>,
+34    /// Specify the local socket address
+35    #[clap(long)]
+36    local_addr: Option<SocketAddr>,
+37    /// Number of unidirectional requests to maintain concurrently
+38    #[clap(long, default_value = "0")]
+39    uni_requests: u64,
+40    /// Number of bidirectional requests to maintain concurrently
+41    #[clap(long, default_value = "1")]
+42    bi_requests: u64,
+43    /// Number of bytes to request
+44    ///
+45    /// This can use SI suffixes for sizes. For example, 1M will transfer
+46    /// 1MiB, 10G will transfer 10GiB.
+47    #[clap(long, default_value = "1M", value_parser = parse_byte_size)]
+48    download_size: u64,
+49    /// Number of bytes to transmit, in addition to the request header
+50    ///
+51    /// This can use SI suffixes for sizes. For example, 1M will transfer
+52    /// 1MiB, 10G will transfer 10GiB.
+53    #[clap(long, default_value = "1M", value_parser = parse_byte_size)]
+54    upload_size: u64,
+55    /// The time to run in seconds
+56    #[clap(long, default_value = "60")]
+57    duration: u64,
+58    /// The interval in seconds at which stats are reported
+59    #[clap(long, default_value = "1")]
+60    interval: u64,
+61    /// File path to output JSON statistics to. If the file is '-', stdout will be used
+62    #[cfg(feature = "json-output")]
+63    #[clap(long)]
+64    json: Option<PathBuf>,
+65    /// Common options
+66    #[command(flatten)]
+67    common: CommonOpt,
+68}
+69
+70pub async fn run(opt: Opt) -> Result<()> {
+71    let mut host_parts = opt.host.split(':');
+72    let host_name = host_parts.next().unwrap();
+73    let host_port = host_parts
+74        .next()
+75        .map_or(Ok(443), |x| x.parse())
+76        .context("parsing port")?;
+77    let addr = match opt.ip {
+78        None => tokio::net::lookup_host(&opt.host)
+79            .await
+80            .context("resolving host")?
+81            .next()
+82            .unwrap(),
+83        Some(ip) => SocketAddr::new(ip, host_port),
+84    };
+85
+86    info!("connecting to {} at {}", host_name, addr);
+87
+88    let bind_addr = opt.local_addr.unwrap_or_else(|| {
+89        let unspec = if addr.is_ipv4() {
+90            Ipv4Addr::UNSPECIFIED.into()
+91        } else {
+92            Ipv6Addr::UNSPECIFIED.into()
+93        };
+94        SocketAddr::new(unspec, 0)
+95    });
+96
+97    info!("local addr {:?}", bind_addr);
+98
+99    let socket = opt.common.bind_socket(bind_addr)?;
+100
+101    let mut endpoint_cfg = quinn::EndpointConfig::default();
+102    endpoint_cfg.max_udp_payload_size(opt.common.max_udp_payload_size)?;
+103
+104    let endpoint = quinn::Endpoint::new(endpoint_cfg, None, socket, Arc::new(TokioRuntime))?;
+105
+106    let default_provider = rustls::crypto::ring::default_provider();
+107    let provider = Arc::new(rustls::crypto::CryptoProvider {
+108        cipher_suites: PERF_CIPHER_SUITES.into(),
+109        ..default_provider
+110    });
+111
+112    let mut crypto = rustls::ClientConfig::builder_with_provider(provider.clone())
+113        .with_protocol_versions(&[&rustls::version::TLS13])
+114        .unwrap()
+115        .dangerous()
+116        .with_custom_certificate_verifier(SkipServerVerification::new(provider))
+117        .with_no_client_auth();
+118    crypto.alpn_protocols = vec![b"perf".to_vec()];
+119
+120    if opt.common.keylog {
+121        crypto.key_log = Arc::new(rustls::KeyLogFile::new());
+122    }
+123
+124    let transport = opt.common.build_transport_config(
+125        #[cfg(feature = "qlog")]
+126        "perf-client",
+127    )?;
+128
+129    let crypto = Arc::new(QuicClientConfig::try_from(crypto)?);
+130    let mut config = quinn::ClientConfig::new(match opt.common.no_protection {
+131        true => Arc::new(NoProtectionClientConfig::new(crypto)),
+132        false => crypto,
+133    });
+134    config.transport_config(Arc::new(transport));
+135
+136    let stream_stats = OpenStreamStats::default();
+137
+138    let connection = endpoint
+139        .connect_with(config, addr, host_name)?
+140        .await
+141        .context("connecting")?;
+142
+143    info!("established");
+144
+145    let drive_fut = async {
+146        tokio::try_join!(
+147            drive_uni(
+148                connection.clone(),
+149                stream_stats.clone(),
+150                opt.uni_requests,
+151                opt.upload_size,
+152                opt.download_size
+153            ),
+154            drive_bi(
+155                connection.clone(),
+156                stream_stats.clone(),
+157                opt.bi_requests,
+158                opt.upload_size,
+159                opt.download_size
+160            )
+161        )
+162    };
+163
+164    let mut stats = Stats::default();
+165
+166    let stats_fut = async {
+167        let interval_duration = Duration::from_secs(opt.interval);
+168
+169        loop {
+170            let start = Instant::now();
+171            tokio::time::sleep(interval_duration).await;
+172            {
+173                stats.on_interval(start, &stream_stats);
+174
+175                stats.print();
+176                if opt.common.conn_stats {
+177                    println!("{:?}\n", connection.stats());
+178                }
+179            }
+180        }
+181    };
+182
+183    tokio::select! {
+184        _ = drive_fut => {}
+185        _ = stats_fut => {}
+186        _ = tokio::signal::ctrl_c() => {
+187            info!("shutting down");
+188            connection.close(0u32.into(), b"interrupted");
+189        }
+190        // Add a small duration so the final interval can be reported
+191        _ = tokio::time::sleep(Duration::from_secs(opt.duration) + Duration::from_millis(200)) => {
+192            info!("shutting down");
+193            connection.close(0u32.into(), b"done");
+194        }
+195    }
+196
+197    endpoint.wait_idle().await;
+198
+199    #[cfg(feature = "json-output")]
+200    if let Some(path) = opt.json {
+201        stats.print_json(path.as_path())?;
+202    }
+203
+204    Ok(())
+205}
+206
+207async fn drain_stream(
+208    mut stream: quinn::RecvStream,
+209    download: u64,
+210    stream_stats: OpenStreamStats,
+211) -> Result<()> {
+212    if download == 0 {
+213        return Ok(());
+214    }
+215
+216    #[rustfmt::skip]
+217    let mut bufs = [
+218        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+219        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+220        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+221        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+222        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+223        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+224        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+225        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+226    ];
+227    let download_start = Instant::now();
+228    let recv_stream_stats = stream_stats.new_receiver(&stream, download);
+229
+230    let mut first_byte = true;
+231
+232    while let Some(size) = stream.read_chunks(&mut bufs[..]).await? {
+233        if first_byte {
+234            recv_stream_stats.on_first_byte(download_start.elapsed());
+235            first_byte = false;
+236        }
+237        let bytes_received = bufs[..size].iter().map(|b| b.len()).sum();
+238        recv_stream_stats.on_bytes(bytes_received);
+239    }
+240
+241    if first_byte {
+242        recv_stream_stats.on_first_byte(download_start.elapsed());
+243    }
+244    recv_stream_stats.finish(download_start.elapsed());
+245
+246    debug!("response finished on {}", stream.id());
+247    Ok(())
+248}
+249
+250async fn drive_uni(
+251    connection: quinn::Connection,
+252    stream_stats: OpenStreamStats,
+253    concurrency: u64,
+254    upload: u64,
+255    download: u64,
+256) -> Result<()> {
+257    if concurrency == 0 {
+258        return Ok(());
+259    }
+260
+261    let sem = Arc::new(Semaphore::new(concurrency as usize));
+262
+263    loop {
+264        let permit = sem.clone().acquire_owned().await.unwrap();
+265        let send = connection.open_uni().await?;
+266        let stream_stats = stream_stats.clone();
+267
+268        debug!("sending request on {}", send.id());
+269        let connection = connection.clone();
+270        tokio::spawn(async move {
+271            if let Err(e) = request_uni(send, connection, upload, download, stream_stats).await {
+272                error!("sending request failed: {:#}", e);
+273            }
+274
+275            drop(permit);
+276        });
+277    }
+278}
+279
+280async fn request_uni(
+281    send: quinn::SendStream,
+282    conn: quinn::Connection,
+283    upload: u64,
+284    download: u64,
+285    stream_stats: OpenStreamStats,
+286) -> Result<()> {
+287    request(send, upload, download, stream_stats.clone()).await?;
+288    let recv = conn.accept_uni().await?;
+289    drain_stream(recv, download, stream_stats).await?;
+290    Ok(())
+291}
+292
+293async fn request(
+294    mut send: quinn::SendStream,
+295    mut upload: u64,
+296    download: u64,
+297    stream_stats: OpenStreamStats,
+298) -> Result<()> {
+299    let upload_start = Instant::now();
+300    send.write_all(&download.to_be_bytes()).await?;
+301    if upload == 0 {
+302        send.finish().unwrap();
+303        return Ok(());
+304    }
+305
+306    let send_stream_stats = stream_stats.new_sender(&send, upload);
+307
+308    static DATA: [u8; 1024 * 1024] = [42; 1024 * 1024];
+309    while upload > 0 {
+310        let chunk_len = upload.min(DATA.len() as u64);
+311        send.write_chunk(Bytes::from_static(&DATA[..chunk_len as usize]))
+312            .await
+313            .context("sending response")?;
+314        send_stream_stats.on_bytes(chunk_len as usize);
+315        upload -= chunk_len;
+316    }
+317    send.finish().unwrap();
+318    // Wait for stream to close
+319    _ = send.stopped().await;
+320    send_stream_stats.finish(upload_start.elapsed());
+321
+322    debug!("upload finished on {}", send.id());
+323    Ok(())
+324}
+325
+326async fn drive_bi(
+327    connection: quinn::Connection,
+328    stream_stats: OpenStreamStats,
+329    concurrency: u64,
+330    upload: u64,
+331    download: u64,
+332) -> Result<()> {
+333    if concurrency == 0 {
+334        return Ok(());
+335    }
+336
+337    let sem = Arc::new(Semaphore::new(concurrency as usize));
+338
+339    loop {
+340        let permit = sem.clone().acquire_owned().await.unwrap();
+341        let (send, recv) = connection.open_bi().await?;
+342        let stream_stats = stream_stats.clone();
+343
+344        debug!("sending request on {}", send.id());
+345        tokio::spawn(async move {
+346            if let Err(e) = request_bi(send, recv, upload, download, stream_stats).await {
+347                error!("request failed: {:#}", e);
+348            }
+349
+350            drop(permit);
+351        });
+352    }
+353}
+354
+355async fn request_bi(
+356    send: quinn::SendStream,
+357    recv: quinn::RecvStream,
+358    upload: u64,
+359    download: u64,
+360    stream_stats: OpenStreamStats,
+361) -> Result<()> {
+362    request(send, upload, download, stream_stats.clone()).await?;
+363    drain_stream(recv, download, stream_stats).await?;
+364    Ok(())
+365}
+366
+367#[derive(Debug)]
+368struct SkipServerVerification(Arc<rustls::crypto::CryptoProvider>);
+369
+370impl SkipServerVerification {
+371    fn new(provider: Arc<rustls::crypto::CryptoProvider>) -> Arc<Self> {
+372        Arc::new(Self(provider))
+373    }
+374}
+375
+376impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
+377    fn verify_server_cert(
+378        &self,
+379        _end_entity: &CertificateDer<'_>,
+380        _intermediates: &[CertificateDer<'_>],
+381        _server_name: &ServerName<'_>,
+382        _ocsp: &[u8],
+383        _now: UnixTime,
+384    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
+385        Ok(rustls::client::danger::ServerCertVerified::assertion())
+386    }
+387
+388    fn verify_tls12_signature(
+389        &self,
+390        message: &[u8],
+391        cert: &CertificateDer<'_>,
+392        dss: &rustls::DigitallySignedStruct,
+393    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
+394        rustls::crypto::verify_tls12_signature(
+395            message,
+396            cert,
+397            dss,
+398            &self.0.signature_verification_algorithms,
+399        )
+400    }
+401
+402    fn verify_tls13_signature(
+403        &self,
+404        message: &[u8],
+405        cert: &CertificateDer<'_>,
+406        dss: &rustls::DigitallySignedStruct,
+407    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
+408        rustls::crypto::verify_tls13_signature(
+409            message,
+410            cert,
+411            dss,
+412            &self.0.signature_verification_algorithms,
+413        )
+414    }
+415
+416    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
+417        self.0.signature_verification_algorithms.supported_schemes()
+418    }
+419}
\ No newline at end of file diff --git a/pr/395/docs/src/perf/lib.rs.html b/pr/395/docs/src/perf/lib.rs.html new file mode 100644 index 000000000..59df4814f --- /dev/null +++ b/pr/395/docs/src/perf/lib.rs.html @@ -0,0 +1,219 @@ +lib.rs - source

perf/
lib.rs

1#[cfg(feature = "qlog")]
+2use std::path::PathBuf;
+3use std::{io, net::SocketAddr, num::ParseIntError, str::FromStr, sync::Arc, time::Duration};
+4
+5use anyhow::{Context, Result};
+6use clap::{Parser, ValueEnum};
+7use quinn::{
+8    AckFrequencyConfig, TransportConfig, VarInt,
+9    congestion::{self, ControllerFactory},
+10    udp::UdpSocketState,
+11};
+12use rustls::crypto::ring::cipher_suite;
+13use socket2::{Domain, Protocol, Socket, Type};
+14use tracing::warn;
+15
+16#[cfg_attr(not(feature = "json-output"), allow(dead_code))]
+17pub mod stats;
+18
+19pub mod noprotection;
+20
+21pub mod client;
+22pub mod server;
+23
+24// Common options between client and server binary
+25#[derive(Parser)]
+26pub struct CommonOpt {
+27    /// Send buffer size in bytes
+28    ///
+29    /// This can use SI suffixes for sizes. For example, 1M will request
+30    /// 1MiB, 10G will request 10GiB.
+31    #[clap(long, default_value = "2M", value_parser = parse_byte_size)]
+32    pub send_buffer_size: u64,
+33    /// Receive buffer size in bytes
+34    ///
+35    /// This can use SI suffixes for sizes. For example, 1M will request
+36    /// 1MiB, 10G will request 10GiB.
+37    #[clap(long, default_value = "2M", value_parser = parse_byte_size)]
+38    pub recv_buffer_size: u64,
+39    /// Whether to print connection statistics
+40    #[clap(long)]
+41    pub conn_stats: bool,
+42    /// Perform NSS-compatible TLS key logging to the file specified in `SSLKEYLOGFILE`.
+43    #[clap(long = "keylog")]
+44    pub keylog: bool,
+45    /// UDP payload size that the network must be capable of carrying
+46    #[clap(long, default_value = "1200")]
+47    pub initial_mtu: u16,
+48    /// Disable packet encryption/decryption (for debugging purpose)
+49    #[clap(long = "no-protection")]
+50    pub no_protection: bool,
+51    /// The initial round-trip-time (in msecs)
+52    #[clap(long, group = "common")]
+53    pub initial_rtt: Option<u64>,
+54    /// Ack Frequency mode
+55    #[clap(long = "ack-frequency")]
+56    pub ack_frequency: bool,
+57    /// Congestion algorithm to use
+58    #[clap(long = "congestion")]
+59    pub cong_alg: Option<CongestionAlgorithm>,
+60    /// Maximum number of bytes the peer may transmit without acknowledgement on any one stream
+61    /// before becoming blocked.
+62    ///
+63    /// This can use SI suffixes for sizes. For example, 1M will limit to
+64    /// 1MiB, 10G will limit to 10GiB.
+65    #[clap(long, value_parser = parse_byte_size)]
+66    pub stream_receive_window: Option<u64>,
+67    /// Maximum number of bytes the peer may transmit across all streams of a connection before
+68    /// becoming blocked.
+69    ///
+70    /// This can use SI suffixes for sizes. For example, 1M will limit to
+71    /// 1MiB, 10G will limit to 10GiB.
+72    #[clap(long, value_parser = parse_byte_size)]
+73    pub receive_window: Option<u64>,
+74    /// Maximum number of bytes to transmit to a peer without acknowledgment
+75    ///
+76    /// This can use SI suffixes for sizes. For example, 1M will limit to
+77    /// 1MiB, 10G will limit to 10GiB.
+78    #[clap(long, value_parser = parse_byte_size)]
+79    pub send_window: Option<u64>,
+80    /// Max UDP payload size in bytes
+81    #[clap(long, default_value = "1472")]
+82    pub max_udp_payload_size: u16,
+83    /// qlog output directory
+84    ///
+85    /// Alternatively you can set the `QLOGDIR` environment variable.
+86    #[cfg(feature = "qlog")]
+87    #[clap(long = "qlog")]
+88    pub qlog_dir: Option<PathBuf>,
+89}
+90
+91impl CommonOpt {
+92    pub fn build_transport_config(
+93        &self,
+94        #[cfg(feature = "qlog")] name: &str,
+95    ) -> io::Result<TransportConfig> {
+96        let mut transport = TransportConfig::default();
+97        transport.initial_mtu(self.initial_mtu);
+98
+99        if let Some(initial_rtt) = self.initial_rtt {
+100            transport.initial_rtt(Duration::from_millis(initial_rtt));
+101        }
+102
+103        if self.ack_frequency {
+104            transport.ack_frequency_config(Some(AckFrequencyConfig::default()));
+105        }
+106
+107        if let Some(cong_alg) = self.cong_alg {
+108            transport.congestion_controller_factory(cong_alg.build());
+109        }
+110
+111        if let Some(stream_receive_window) = self.stream_receive_window {
+112            transport.stream_receive_window(
+113                VarInt::from_u64(stream_receive_window).unwrap_or(VarInt::MAX),
+114            );
+115        }
+116
+117        if let Some(receive_window) = self.receive_window {
+118            transport.receive_window(VarInt::from_u64(receive_window).unwrap_or(VarInt::MAX));
+119        }
+120
+121        if let Some(send_window) = self.send_window {
+122            transport.send_window(send_window);
+123        }
+124
+125        #[cfg(feature = "qlog")]
+126        if let Some(qlog_dir) = &self.qlog_dir {
+127            transport.qlog_from_path(qlog_dir, name);
+128        } else {
+129            transport.qlog_from_env(name);
+130        }
+131
+132        Ok(transport)
+133    }
+134
+135    pub fn bind_socket(&self, addr: SocketAddr) -> Result<std::net::UdpSocket> {
+136        let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))
+137            .context("create socket")?;
+138
+139        if addr.is_ipv6() {
+140            socket.set_only_v6(false).context("set_only_v6")?;
+141        }
+142
+143        socket
+144            .bind(&socket2::SockAddr::from(addr))
+145            .context("binding endpoint")?;
+146
+147        let socket_state = UdpSocketState::new((&socket).into())?;
+148        socket_state
+149            .set_send_buffer_size((&socket).into(), self.send_buffer_size as usize)
+150            .context("send buffer size")?;
+151        socket_state
+152            .set_recv_buffer_size((&socket).into(), self.recv_buffer_size as usize)
+153            .context("recv buffer size")?;
+154
+155        let buf_size = socket_state
+156            .send_buffer_size((&socket).into())
+157            .context("send buffer size")?;
+158        if buf_size < self.send_buffer_size as usize {
+159            warn!(
+160                "Unable to set desired send buffer size. Desired: {}, Actual: {}",
+161                self.send_buffer_size, buf_size
+162            );
+163        }
+164
+165        let buf_size = socket_state
+166            .recv_buffer_size((&socket).into())
+167            .context("recv buffer size")?;
+168        if buf_size < self.recv_buffer_size as usize {
+169            warn!(
+170                "Unable to set desired recv buffer size. Desired: {}, Actual: {}",
+171                self.recv_buffer_size, buf_size
+172            );
+173        }
+174
+175        Ok(socket.into())
+176    }
+177}
+178
+179pub fn parse_byte_size(s: &str) -> Result<u64, ParseIntError> {
+180    let s = s.trim();
+181
+182    let multiplier = match s.chars().last() {
+183        Some('T') => 1024 * 1024 * 1024 * 1024,
+184        Some('G') => 1024 * 1024 * 1024,
+185        Some('M') => 1024 * 1024,
+186        Some('k') => 1024,
+187        _ => 1,
+188    };
+189
+190    let s = match multiplier {
+191        1 => s,
+192        _ => &s[..s.len() - 1],
+193    };
+194
+195    Ok(u64::from_str(s)? * multiplier)
+196}
+197
+198#[derive(Clone, Copy, ValueEnum)]
+199pub enum CongestionAlgorithm {
+200    Cubic,
+201    Bbr,
+202    NewReno,
+203}
+204
+205impl CongestionAlgorithm {
+206    pub fn build(self) -> Arc<dyn ControllerFactory + Send + Sync + 'static> {
+207        match self {
+208            Self::Cubic => Arc::new(congestion::CubicConfig::default()),
+209            Self::Bbr => Arc::new(congestion::BbrConfig::default()),
+210            Self::NewReno => Arc::new(congestion::NewRenoConfig::default()),
+211        }
+212    }
+213}
+214
+215pub static PERF_CIPHER_SUITES: &[rustls::SupportedCipherSuite] = &[
+216    cipher_suite::TLS13_AES_128_GCM_SHA256,
+217    cipher_suite::TLS13_AES_256_GCM_SHA384,
+218    cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
+219];
\ No newline at end of file diff --git a/pr/395/docs/src/perf/noprotection.rs.html b/pr/395/docs/src/perf/noprotection.rs.html new file mode 100644 index 000000000..a3028bc01 --- /dev/null +++ b/pr/395/docs/src/perf/noprotection.rs.html @@ -0,0 +1,205 @@ +noprotection.rs - source

perf/
noprotection.rs

1use std::sync::Arc;
+2
+3use bytes::BytesMut;
+4
+5use quinn_proto::{
+6    ConnectionId, PathId, Side, TransportError,
+7    crypto::{
+8        self, CryptoError,
+9        rustls::{QuicClientConfig, QuicServerConfig},
+10    },
+11    transport_parameters,
+12};
+13
+14/// A rustls TLS session which does not perform packet encryption/decryption (for debugging purpose)
+15struct NoProtectionSession {
+16    inner: Box<dyn crypto::Session>,
+17}
+18
+19impl NoProtectionSession {
+20    fn new(tls: Box<dyn crypto::Session>) -> Self {
+21        Self { inner: tls }
+22    }
+23
+24    /// Wraps the provided keys in `NoProtectionPacketKey` to disable packet encryption / decryption
+25    fn wrap_packet_keys(
+26        keys: crypto::KeyPair<Box<dyn crypto::PacketKey>>,
+27    ) -> crypto::KeyPair<Box<dyn crypto::PacketKey>> {
+28        crypto::KeyPair {
+29            local: Box::new(NoProtectionPacketKey::new(keys.local)),
+30            remote: Box::new(NoProtectionPacketKey::new(keys.remote)),
+31        }
+32    }
+33}
+34
+35struct NoProtectionPacketKey {
+36    inner: Box<dyn crypto::PacketKey>,
+37}
+38
+39impl NoProtectionPacketKey {
+40    fn new(key: Box<dyn crypto::PacketKey>) -> Self {
+41        Self { inner: key }
+42    }
+43}
+44
+45pub struct NoProtectionClientConfig {
+46    inner: Arc<QuicClientConfig>,
+47}
+48
+49impl NoProtectionClientConfig {
+50    pub fn new(config: Arc<QuicClientConfig>) -> Self {
+51        Self { inner: config }
+52    }
+53}
+54
+55pub struct NoProtectionServerConfig {
+56    inner: Arc<QuicServerConfig>,
+57}
+58
+59impl NoProtectionServerConfig {
+60    pub fn new(config: Arc<QuicServerConfig>) -> Self {
+61        Self { inner: config }
+62    }
+63}
+64
+65// forward all calls to inner except those related to packet encryption/decryption
+66impl crypto::Session for NoProtectionSession {
+67    fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> crypto::Keys {
+68        self.inner.initial_keys(dst_cid, side)
+69    }
+70
+71    fn handshake_data(&self) -> Option<Box<dyn std::any::Any>> {
+72        self.inner.handshake_data()
+73    }
+74
+75    fn peer_identity(&self) -> Option<Box<dyn std::any::Any>> {
+76        self.inner.peer_identity()
+77    }
+78
+79    fn early_crypto(&self) -> Option<(Box<dyn crypto::HeaderKey>, Box<dyn crypto::PacketKey>)> {
+80        let (hkey, pkey) = self.inner.early_crypto()?;
+81
+82        // use wrapper type to disable packet encryption/decryption
+83        Some((hkey, Box::new(NoProtectionPacketKey::new(pkey))))
+84    }
+85
+86    fn early_data_accepted(&self) -> Option<bool> {
+87        self.inner.early_data_accepted()
+88    }
+89
+90    fn is_handshaking(&self) -> bool {
+91        self.inner.is_handshaking()
+92    }
+93
+94    fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError> {
+95        self.inner.read_handshake(buf)
+96    }
+97
+98    fn transport_parameters(
+99        &self,
+100    ) -> Result<Option<transport_parameters::TransportParameters>, TransportError> {
+101        self.inner.transport_parameters()
+102    }
+103
+104    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<crypto::Keys> {
+105        let keys = self.inner.write_handshake(buf)?;
+106
+107        Some(crypto::Keys {
+108            header: keys.header,
+109            packet: Self::wrap_packet_keys(keys.packet),
+110        })
+111    }
+112
+113    fn next_1rtt_keys(&mut self) -> Option<crypto::KeyPair<Box<dyn crypto::PacketKey>>> {
+114        let keys = self.inner.next_1rtt_keys()?;
+115        Some(Self::wrap_packet_keys(keys))
+116    }
+117
+118    fn is_valid_retry(&self, orig_dst_cid: ConnectionId, header: &[u8], payload: &[u8]) -> bool {
+119        self.inner.is_valid_retry(orig_dst_cid, header, payload)
+120    }
+121
+122    fn export_keying_material(
+123        &self,
+124        output: &mut [u8],
+125        label: &[u8],
+126        context: &[u8],
+127    ) -> Result<(), crypto::ExportKeyingMaterialError> {
+128        self.inner.export_keying_material(output, label, context)
+129    }
+130}
+131
+132impl crypto::ClientConfig for NoProtectionClientConfig {
+133    fn start_session(
+134        self: std::sync::Arc<Self>,
+135        version: u32,
+136        server_name: &str,
+137        params: &transport_parameters::TransportParameters,
+138    ) -> Result<Box<dyn crypto::Session>, quinn::ConnectError> {
+139        let tls = self
+140            .inner
+141            .clone()
+142            .start_session(version, server_name, params)?;
+143
+144        Ok(Box::new(NoProtectionSession::new(tls)))
+145    }
+146}
+147
+148impl crypto::ServerConfig for NoProtectionServerConfig {
+149    fn initial_keys(
+150        &self,
+151        version: u32,
+152        dst_cid: ConnectionId,
+153    ) -> Result<crypto::Keys, crypto::UnsupportedVersion> {
+154        self.inner.initial_keys(version, dst_cid)
+155    }
+156
+157    fn retry_tag(&self, version: u32, orig_dst_cid: ConnectionId, packet: &[u8]) -> [u8; 16] {
+158        self.inner.retry_tag(version, orig_dst_cid, packet)
+159    }
+160
+161    fn start_session(
+162        self: Arc<Self>,
+163        version: u32,
+164        params: &transport_parameters::TransportParameters,
+165    ) -> Box<dyn crypto::Session> {
+166        let tls = self.inner.clone().start_session(version, params);
+167
+168        Box::new(NoProtectionSession::new(tls))
+169    }
+170}
+171
+172// forward all calls to inner except those related to packet encryption/decryption
+173impl crypto::PacketKey for NoProtectionPacketKey {
+174    fn encrypt(&self, _path_id: PathId, _packet: u64, buf: &mut [u8], header_len: usize) {
+175        let (_header, payload_tag) = buf.split_at_mut(header_len);
+176        let (_payload, tag_storage) =
+177            payload_tag.split_at_mut(payload_tag.len() - self.inner.tag_len());
+178        // packet = identity(packet)
+179        tag_storage.fill(42);
+180    }
+181
+182    fn decrypt(
+183        &self,
+184        _path_id: PathId,
+185        _packet: u64,
+186        _header: &[u8],
+187        payload: &mut BytesMut,
+188    ) -> Result<(), CryptoError> {
+189        let plain_len = payload.len() - self.inner.tag_len();
+190        payload.truncate(plain_len);
+191        Ok(())
+192    }
+193
+194    fn tag_len(&self) -> usize {
+195        self.inner.tag_len()
+196    }
+197
+198    fn confidentiality_limit(&self) -> u64 {
+199        self.inner.confidentiality_limit()
+200    }
+201
+202    fn integrity_limit(&self) -> u64 {
+203        self.inner.integrity_limit()
+204    }
+205}
\ No newline at end of file diff --git a/pr/395/docs/src/perf/server.rs.html b/pr/395/docs/src/perf/server.rs.html new file mode 100644 index 000000000..e1b75cd59 --- /dev/null +++ b/pr/395/docs/src/perf/server.rs.html @@ -0,0 +1,202 @@ +server.rs - source

perf/
server.rs

1use std::{net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};
+2
+3use anyhow::{Context, Result};
+4use bytes::Bytes;
+5use clap::Parser;
+6use quinn::{TokioRuntime, crypto::rustls::QuicServerConfig};
+7use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer, pem::PemObject};
+8use tracing::{debug, error, info};
+9
+10use crate::{CommonOpt, PERF_CIPHER_SUITES, noprotection::NoProtectionServerConfig};
+11
+12#[derive(Parser)]
+13#[clap(name = "server")]
+14pub struct Opt {
+15    /// Address to listen on
+16    #[clap(long = "listen", default_value = "[::]:4433")]
+17    listen: SocketAddr,
+18    /// TLS private key in DER format
+19    #[clap(short = 'k', long = "key", requires = "cert")]
+20    key: Option<PathBuf>,
+21    /// TLS certificate in PEM format
+22    #[clap(short = 'c', long = "cert", requires = "key")]
+23    cert: Option<PathBuf>,
+24    /// Common options
+25    #[command(flatten)]
+26    common: CommonOpt,
+27}
+28
+29pub async fn run(opt: Opt) -> Result<()> {
+30    let (key, cert) = match (&opt.key, &opt.cert) {
+31        (Some(key), Some(cert)) => (
+32            PrivateKeyDer::from_pem_file(key).context("reading private key")?,
+33            CertificateDer::pem_file_iter(cert)
+34                .context("reading certificate chain file")?
+35                .collect::<Result<_, _>>()
+36                .context("reading certificate chain")?,
+37        ),
+38        _ => {
+39            let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()]).unwrap();
+40            (
+41                PrivatePkcs8KeyDer::from(cert.signing_key.serialize_der()).into(),
+42                vec![CertificateDer::from(cert.cert)],
+43            )
+44        }
+45    };
+46
+47    let default_provider = rustls::crypto::ring::default_provider();
+48    let provider = rustls::crypto::CryptoProvider {
+49        cipher_suites: PERF_CIPHER_SUITES.into(),
+50        ..default_provider
+51    };
+52
+53    let mut crypto = rustls::ServerConfig::builder_with_provider(provider.into())
+54        .with_protocol_versions(&[&rustls::version::TLS13])
+55        .unwrap()
+56        .with_no_client_auth()
+57        .with_single_cert(cert, key)
+58        .unwrap();
+59    crypto.alpn_protocols = vec![b"perf".to_vec()];
+60
+61    if opt.common.keylog {
+62        crypto.key_log = Arc::new(rustls::KeyLogFile::new());
+63    }
+64
+65    let transport = opt.common.build_transport_config(
+66        #[cfg(feature = "qlog")]
+67        "perf-server",
+68    )?;
+69
+70    let crypto = Arc::new(QuicServerConfig::try_from(crypto)?);
+71    let mut config = quinn::ServerConfig::with_crypto(match opt.common.no_protection {
+72        true => Arc::new(NoProtectionServerConfig::new(crypto)),
+73        false => crypto,
+74    });
+75    config.transport_config(Arc::new(transport));
+76
+77    let socket = opt.common.bind_socket(opt.listen)?;
+78
+79    let mut endpoint_cfg = quinn::EndpointConfig::default();
+80    endpoint_cfg.max_udp_payload_size(opt.common.max_udp_payload_size)?;
+81
+82    let endpoint = quinn::Endpoint::new(endpoint_cfg, Some(config), socket, Arc::new(TokioRuntime))
+83        .context("creating endpoint")?;
+84
+85    info!("listening on {}", endpoint.local_addr().unwrap());
+86
+87    let opt = Arc::new(opt);
+88
+89    while let Some(handshake) = endpoint.accept().await {
+90        let opt = opt.clone();
+91        tokio::spawn(async move {
+92            if let Err(e) = handle(handshake, opt).await {
+93                error!("connection lost: {:#}", e);
+94            }
+95        });
+96    }
+97
+98    Ok(())
+99}
+100
+101async fn handle(handshake: quinn::Incoming, opt: Arc<Opt>) -> Result<()> {
+102    let connection = handshake.await.context("handshake failed")?;
+103
+104    debug!("{} connected", connection.remote_address());
+105    tokio::try_join!(
+106        drive_uni(connection.clone()),
+107        drive_bi(connection.clone()),
+108        conn_stats(connection, opt)
+109    )?;
+110    Ok(())
+111}
+112
+113async fn conn_stats(connection: quinn::Connection, opt: Arc<Opt>) -> Result<()> {
+114    if opt.common.conn_stats {
+115        loop {
+116            tokio::time::sleep(Duration::from_secs(2)).await;
+117            println!("{:?}\n", connection.stats());
+118        }
+119    }
+120
+121    Ok(())
+122}
+123
+124async fn drive_uni(connection: quinn::Connection) -> Result<()> {
+125    while let Ok(stream) = connection.accept_uni().await {
+126        let connection = connection.clone();
+127        tokio::spawn(async move {
+128            if let Err(e) = handle_uni(connection, stream).await {
+129                error!("request failed: {:#}", e);
+130            }
+131        });
+132    }
+133    Ok(())
+134}
+135
+136async fn handle_uni(connection: quinn::Connection, stream: quinn::RecvStream) -> Result<()> {
+137    let bytes = read_req(stream).await?;
+138    let response = connection.open_uni().await?;
+139    respond(bytes, response).await?;
+140    Ok(())
+141}
+142
+143async fn drive_bi(connection: quinn::Connection) -> Result<()> {
+144    while let Ok((send, recv)) = connection.accept_bi().await {
+145        tokio::spawn(async move {
+146            if let Err(e) = handle_bi(send, recv).await {
+147                error!("request failed: {:#}", e);
+148            }
+149        });
+150    }
+151    Ok(())
+152}
+153
+154async fn handle_bi(send: quinn::SendStream, recv: quinn::RecvStream) -> Result<()> {
+155    let bytes = read_req(recv).await?;
+156    respond(bytes, send).await?;
+157    Ok(())
+158}
+159
+160async fn read_req(mut stream: quinn::RecvStream) -> Result<u64> {
+161    let mut buf = [0; 8];
+162    stream
+163        .read_exact(&mut buf)
+164        .await
+165        .context("reading request")?;
+166    let n = u64::from_be_bytes(buf);
+167    debug!("got req for {} bytes on {}", n, stream.id());
+168    drain_stream(stream).await?;
+169    Ok(n)
+170}
+171
+172async fn drain_stream(mut stream: quinn::RecvStream) -> Result<()> {
+173    #[rustfmt::skip]
+174    let mut bufs = [
+175        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+176        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+177        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+178        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+179        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+180        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+181        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+182        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+183    ];
+184    while stream.read_chunks(&mut bufs[..]).await?.is_some() {}
+185    debug!("finished reading {}", stream.id());
+186    Ok(())
+187}
+188
+189async fn respond(mut bytes: u64, mut stream: quinn::SendStream) -> Result<()> {
+190    static DATA: [u8; 1024 * 1024] = [42; 1024 * 1024];
+191
+192    while bytes > 0 {
+193        let chunk_len = bytes.min(DATA.len() as u64);
+194        stream
+195            .write_chunk(Bytes::from_static(&DATA[..chunk_len as usize]))
+196            .await
+197            .context("sending response")?;
+198        bytes -= chunk_len;
+199    }
+200    debug!("finished responding on {}", stream.id());
+201    Ok(())
+202}
\ No newline at end of file diff --git a/pr/395/docs/src/perf/stats.rs.html b/pr/395/docs/src/perf/stats.rs.html new file mode 100644 index 000000000..8d8d86e96 --- /dev/null +++ b/pr/395/docs/src/perf/stats.rs.html @@ -0,0 +1,413 @@ +stats.rs - source

perf/
stats.rs

1use hdrhistogram::Histogram;
+2use quinn::StreamId;
+3use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
+4use std::sync::{Arc, Mutex};
+5use std::time::{Duration, Instant, SystemTime};
+6#[cfg(feature = "json-output")]
+7use {std::fs::File, std::io, std::path::Path};
+8
+9pub struct Stats {
+10    /// Test start time
+11    start_instant: Instant,
+12    /// Test start system time
+13    start: SystemTime,
+14    /// Durations of uploads
+15    upload_duration: Histogram<u64>,
+16    /// Durations of downloads
+17    download_duration: Histogram<u64>,
+18    /// Time from finishing the upload until receiving the first byte of the response
+19    fbl: Histogram<u64>,
+20    /// Throughput for uploads
+21    upload_throughput: Histogram<u64>,
+22    /// Throughput for downloads
+23    download_throughput: Histogram<u64>,
+24    /// The total amount of requests executed
+25    requests: usize,
+26    /// Stats accumulated over each interval
+27    intervals: Vec<Interval>,
+28}
+29
+30impl Default for Stats {
+31    fn default() -> Self {
+32        Self {
+33            start_instant: Instant::now(),
+34            start: SystemTime::now(),
+35            upload_duration: Histogram::new(3).unwrap(),
+36            download_duration: Histogram::new(3).unwrap(),
+37            fbl: Histogram::new(3).unwrap(),
+38            upload_throughput: Histogram::new(3).unwrap(),
+39            download_throughput: Histogram::new(3).unwrap(),
+40            requests: 0,
+41            intervals: vec![],
+42        }
+43    }
+44}
+45
+46impl Stats {
+47    pub fn on_interval(&mut self, start: Instant, stream_stats: &OpenStreamStats) {
+48        let mut interval = Interval::new(start - self.start_instant, self.start_instant.elapsed());
+49        let mut guard = stream_stats.0.lock().unwrap();
+50
+51        guard.retain(|stream_stats| {
+52            self.record(stream_stats.clone());
+53            interval.record_stream_stats(stream_stats.clone());
+54            // Retain if not finished yet
+55            !stream_stats.finished.load(Ordering::SeqCst)
+56        });
+57
+58        self.intervals.push(interval);
+59    }
+60
+61    fn record(&mut self, stream_stats: Arc<StreamStats>) {
+62        if stream_stats.finished.load(Ordering::SeqCst) {
+63            let duration = stream_stats.duration.load(Ordering::SeqCst);
+64            let bps = throughput_bytes_per_second(duration, stream_stats.request_size);
+65
+66            if stream_stats.sender {
+67                self.upload_throughput.record(bps as u64).unwrap();
+68                self.upload_duration.record(duration).unwrap();
+69            } else {
+70                self.download_throughput.record(bps as u64).unwrap();
+71                self.download_duration.record(duration).unwrap();
+72                self.fbl
+73                    .record(stream_stats.first_byte_latency.load(Ordering::SeqCst))
+74                    .unwrap();
+75                self.requests += 1;
+76            }
+77        }
+78    }
+79
+80    pub fn print(&self) {
+81        let dt = self.start_instant.elapsed();
+82        let rps = self.requests as f64 / dt.as_secs_f64();
+83
+84        println!("Overall stats:");
+85        println!(
+86            "RPS: {:.2} ({} requests in {:4.2?})",
+87            rps, self.requests, dt,
+88        );
+89        println!();
+90
+91        println!("Stream metrics:\n");
+92
+93        println!(
+94            "      │ Upload Duration │ Download Duration | FBL        | Upload Throughput | Download Throughput"
+95        );
+96        println!(
+97            "──────┼─────────────────┼───────────────────┼────────────┼───────────────────┼────────────────────"
+98        );
+99
+100        let print_metric = |label: &'static str, get_metric: fn(&Histogram<u64>) -> u64| {
+101            println!(
+102                " {} │ {:>15.2?} │ {:>17.2?} │  {:>9.2?} │ {:12.2} Mb/s │ {:13.2} Mb/s",
+103                label,
+104                Duration::from_micros(get_metric(&self.upload_duration)),
+105                Duration::from_micros(get_metric(&self.download_duration)),
+106                Duration::from_micros(get_metric(&self.fbl)),
+107                get_metric(&self.upload_throughput) as f64 * 8.0 / 1000.0 / 1000.0,
+108                get_metric(&self.download_throughput) as f64 * 8.0 / 1000.0 / 1000.0,
+109            );
+110        };
+111
+112        print_metric("AVG ", |hist| hist.mean() as u64);
+113        print_metric("P0  ", |hist| hist.value_at_quantile(0.00));
+114        print_metric("P10 ", |hist| hist.value_at_quantile(0.10));
+115        print_metric("P50 ", |hist| hist.value_at_quantile(0.50));
+116        print_metric("P90 ", |hist| hist.value_at_quantile(0.90));
+117        print_metric("P100", |hist| hist.value_at_quantile(1.00));
+118        println!();
+119    }
+120
+121    #[cfg(feature = "json-output")]
+122    pub fn print_json(&self, path: &Path) -> io::Result<()> {
+123        match path {
+124            path if path == Path::new("-") => json::print(self, std::io::stdout()),
+125            _ => {
+126                let file = File::create(path)?;
+127                json::print(self, file)
+128            }
+129        }
+130        Ok(())
+131    }
+132}
+133
+134/// Statistics for the currently open streams
+135#[derive(Clone, Default)]
+136pub struct OpenStreamStats(Arc<Mutex<Vec<Arc<StreamStats>>>>);
+137
+138impl OpenStreamStats {
+139    pub fn new_sender(&self, stream: &quinn::SendStream, upload_size: u64) -> Arc<StreamStats> {
+140        let send_stream_stats = StreamStats {
+141            id: stream.id(),
+142            request_size: upload_size,
+143            bytes: Default::default(),
+144            sender: true,
+145            finished: Default::default(),
+146            duration: Default::default(),
+147            first_byte_latency: Default::default(),
+148        };
+149        let send_stream_stats = Arc::new(send_stream_stats);
+150        self.push(send_stream_stats.clone());
+151        send_stream_stats
+152    }
+153
+154    pub fn new_receiver(&self, stream: &quinn::RecvStream, download_size: u64) -> Arc<StreamStats> {
+155        let recv_stream_stats = StreamStats {
+156            id: stream.id(),
+157            request_size: download_size,
+158            bytes: Default::default(),
+159            sender: false,
+160            finished: Default::default(),
+161            duration: Default::default(),
+162            first_byte_latency: Default::default(),
+163        };
+164        let recv_stream_stats = Arc::new(recv_stream_stats);
+165        self.push(recv_stream_stats.clone());
+166        recv_stream_stats
+167    }
+168
+169    fn push(&self, stream_stats: Arc<StreamStats>) {
+170        self.0.lock().unwrap().push(stream_stats);
+171    }
+172}
+173
+174pub struct StreamStats {
+175    id: StreamId,
+176    request_size: u64,
+177    bytes: AtomicUsize,
+178    sender: bool,
+179    finished: AtomicBool,
+180    duration: AtomicU64,
+181    first_byte_latency: AtomicU64,
+182}
+183
+184impl StreamStats {
+185    pub fn on_first_byte(&self, latency: Duration) {
+186        self.first_byte_latency
+187            .store(latency.as_micros() as u64, Ordering::SeqCst);
+188    }
+189
+190    pub fn on_bytes(&self, bytes: usize) {
+191        self.bytes.fetch_add(bytes, Ordering::SeqCst);
+192    }
+193
+194    pub fn finish(&self, duration: Duration) {
+195        self.duration
+196            .store(duration.as_micros() as u64, Ordering::SeqCst);
+197        self.finished.store(true, Ordering::SeqCst);
+198    }
+199}
+200
+201struct Interval {
+202    streams: Vec<StreamIntervalStats>,
+203    period: IntervalPeriod,
+204}
+205
+206impl Interval {
+207    fn new(start: Duration, end: Duration) -> Self {
+208        let period = IntervalPeriod {
+209            start: start.as_secs_f64(),
+210            end: end.as_secs_f64(),
+211            seconds: (end - start).as_secs_f64(),
+212        };
+213
+214        Self {
+215            streams: vec![],
+216            period,
+217        }
+218    }
+219
+220    fn record_stream_stats(&mut self, stream_stats: Arc<StreamStats>) {
+221        let bytes = stream_stats.bytes.swap(0, Ordering::SeqCst);
+222        self.streams.push(StreamIntervalStats {
+223            id: stream_stats.id,
+224            bytes,
+225            sender: stream_stats.sender,
+226        })
+227    }
+228}
+229
+230struct IntervalPeriod {
+231    start: f64,
+232    end: f64,
+233    seconds: f64,
+234}
+235
+236struct StreamIntervalStats {
+237    id: StreamId,
+238    bytes: usize,
+239    sender: bool,
+240}
+241
+242fn throughput_bytes_per_second(duration_in_micros: u64, size: u64) -> f64 {
+243    (size as f64) / (duration_in_micros as f64 / 1000000.0)
+244}
+245
+246#[cfg(feature = "json-output")]
+247mod json {
+248    use crate::stats;
+249    use crate::stats::{Stats, StreamIntervalStats};
+250    use quinn::StreamId;
+251    use serde::{self, Serialize, Serializer, ser::SerializeStruct};
+252    use std::io::Write;
+253    use std::time::{SystemTime, UNIX_EPOCH};
+254
+255    pub(crate) fn print<W: Write>(stats: &Stats, out: W) {
+256        let report = Report {
+257            start: Start {
+258                timestamp: stats.start,
+259            },
+260            intervals: &stats
+261                .intervals
+262                .iter()
+263                .map(Interval::from_stats_interval)
+264                .collect(),
+265        };
+266
+267        serde_json::to_writer(out, &report).unwrap();
+268    }
+269
+270    #[derive(Serialize)]
+271    struct Report<'a> {
+272        start: Start,
+273        intervals: &'a Vec<Interval>,
+274        // TODO: add end stats
+275    }
+276
+277    #[derive(Serialize)]
+278    struct Start {
+279        #[serde(serialize_with = "serialize_timestamp")]
+280        timestamp: SystemTime,
+281    }
+282
+283    fn serialize_timestamp<S>(time: &SystemTime, s: S) -> Result<S::Ok, S::Error>
+284    where
+285        S: serde::Serializer,
+286    {
+287        use serde::ser::SerializeMap;
+288        let mut state = s.serialize_map(Some(1))?;
+289        state.serialize_entry(
+290            "timesecs",
+291            &time.duration_since(UNIX_EPOCH).unwrap().as_secs(),
+292        )?;
+293        state.end()
+294    }
+295
+296    struct Interval {
+297        streams: Vec<Stream>,
+298        recv_sum: Sum,
+299        send_sum: Sum,
+300    }
+301
+302    impl Interval {
+303        fn from_stats_interval(interval: &stats::Interval) -> Self {
+304            Self {
+305                streams: interval
+306                    .streams
+307                    .iter()
+308                    .map(|stats| Stream::from_stream_interval_stats(stats, &interval.period))
+309                    .collect(),
+310                recv_sum: Sum::from_stream_interval_stats(
+311                    &interval.streams,
+312                    &interval.period,
+313                    false,
+314                ),
+315                send_sum: Sum::from_stream_interval_stats(
+316                    &interval.streams,
+317                    &interval.period,
+318                    true,
+319                ),
+320            }
+321        }
+322    }
+323
+324    impl Serialize for Interval {
+325        fn serialize<S>(
+326            &self,
+327            serializer: S,
+328        ) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
+329        where
+330            S: Serializer,
+331        {
+332            let mut state = serializer.serialize_struct("Interval", 2)?;
+333            state.serialize_field("streams", &self.streams)?;
+334            // iperf3 outputs duplicate "sum" entries when run in bidirectional mode
+335            // serde does not support duplicate keys, so only output one of the sums
+336            if self.send_sum.bytes > 0 {
+337                state.serialize_field("sum", &self.send_sum)?;
+338            } else {
+339                state.serialize_field("sum", &self.recv_sum)?;
+340            }
+341            state.end()
+342        }
+343    }
+344
+345    #[derive(Serialize)]
+346    struct Stream {
+347        #[serde(serialize_with = "serialize_stream_id")]
+348        id: StreamId,
+349        start: f64,
+350        end: f64,
+351        seconds: f64,
+352        bytes: usize,
+353        bits_per_second: f64,
+354        sender: bool,
+355    }
+356
+357    impl Stream {
+358        fn from_stream_interval_stats(
+359            stats: &stats::StreamIntervalStats,
+360            period: &stats::IntervalPeriod,
+361        ) -> Self {
+362            let bits_per_second = stats.bytes as f64 * 8.0 / period.seconds;
+363
+364            Self {
+365                id: stats.id,
+366                start: period.start,
+367                end: period.end,
+368                seconds: period.seconds,
+369                bytes: stats.bytes,
+370                bits_per_second,
+371                sender: stats.sender,
+372            }
+373        }
+374    }
+375
+376    fn serialize_stream_id<S: Serializer>(id: &StreamId, serializer: S) -> Result<S::Ok, S::Error> {
+377        serializer.serialize_u64(u64::from(*id))
+378    }
+379
+380    #[derive(Serialize)]
+381    struct Sum {
+382        start: f64,
+383        end: f64,
+384        seconds: f64,
+385        bytes: usize,
+386        bits_per_second: f64,
+387        sender: bool,
+388    }
+389
+390    impl Sum {
+391        fn from_stream_interval_stats(
+392            stats: &[StreamIntervalStats],
+393            period: &stats::IntervalPeriod,
+394            sender: bool,
+395        ) -> Self {
+396            let bytes = stats
+397                .iter()
+398                .filter(|stat| stat.sender == sender)
+399                .map(|stat| stat.bytes)
+400                .sum();
+401            let bits_per_second = bytes as f64 * 8.0 / period.seconds;
+402
+403            Self {
+404                start: period.start,
+405                end: period.end,
+406                seconds: period.seconds,
+407                bytes,
+408                bits_per_second,
+409                sender,
+410            }
+411        }
+412    }
+413}
\ No newline at end of file diff --git a/pr/395/docs/src/quinn_perf/perf.rs.html b/pr/395/docs/src/quinn_perf/perf.rs.html new file mode 100644 index 000000000..34363d0d1 --- /dev/null +++ b/pr/395/docs/src/quinn_perf/perf.rs.html @@ -0,0 +1,46 @@ +perf.rs - source

quinn_perf/
perf.rs

1use clap::{Parser, Subcommand};
+2use tracing::error;
+3use tracing_subscriber::{EnvFilter, Layer, fmt, layer::SubscriberExt, util::SubscriberInitExt};
+4
+5use perf::{client, server};
+6
+7#[tokio::main(flavor = "current_thread")]
+8async fn main() {
+9    let opt = Cli::parse();
+10
+11    let registry = tracing_subscriber::registry();
+12    #[cfg(feature = "tokio-console")]
+13    let registry = registry.with(console_subscriber::spawn());
+14    registry
+15        .with(
+16            fmt::layer().with_filter(
+17                EnvFilter::try_from_default_env()
+18                    .or_else(|_| EnvFilter::try_new("warn"))
+19                    .unwrap(),
+20            ),
+21        )
+22        .init();
+23
+24    let r = match opt.command {
+25        Commands::Server(opt) => server::run(opt).await,
+26        Commands::Client(opt) => client::run(opt).await,
+27    };
+28    if let Err(e) = r {
+29        error!("{:#}", e);
+30    }
+31}
+32
+33#[derive(Parser)]
+34#[clap(long_about = None)]
+35struct Cli {
+36    #[command(subcommand)]
+37    command: Commands,
+38}
+39
+40#[derive(Subcommand)]
+41enum Commands {
+42    /// Run as a perf server
+43    Server(server::Opt),
+44    /// Run as a perf client
+45    Client(client::Opt),
+46}
\ No newline at end of file diff --git a/pr/395/docs/src/set_up_connection/set-up-connection.rs.html b/pr/395/docs/src/set_up_connection/set-up-connection.rs.html new file mode 100644 index 000000000..a601750b4 --- /dev/null +++ b/pr/395/docs/src/set_up_connection/set-up-connection.rs.html @@ -0,0 +1,38 @@ +set-up-connection.rs - source

set_up_connection/
set-up-connection.rs

1use quinn::{Endpoint, ServerConfig};
+2use std::error::Error;
+3use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+4
+5fn main() {}
+6
+7#[allow(dead_code, unused_variables)] // Included in `set-up-connection.md`
+8async fn server(config: ServerConfig) -> Result<(), Box<dyn Error>> {
+9    // Bind this endpoint to a UDP socket on the given server address.
+10    let endpoint = Endpoint::server(config, SERVER_ADDR)?;
+11
+12    // Start iterating over incoming connections.
+13    while let Some(conn) = endpoint.accept().await {
+14        let connection = conn.await?;
+15
+16        // Save connection somewhere, start transferring, receiving data, see DataTransfer tutorial.
+17    }
+18
+19    Ok(())
+20}
+21
+22#[allow(dead_code, unused_variables)] // Included in `set-up-connection.md`
+23async fn client() -> Result<(), Box<dyn Error>> {
+24    // Bind this endpoint to a UDP socket on the given client address.
+25    let endpoint = Endpoint::client(CLIENT_ADDR)?;
+26
+27    // Connect to the server passing in the server name which is supposed to be in the server certificate.
+28    let connection = endpoint.connect(SERVER_ADDR, SERVER_NAME)?.await?;
+29
+30    // Start transferring, receiving data, see data transfer page.
+31
+32    Ok(())
+33}
+34
+35const SERVER_NAME: &str = "localhost";
+36const LOCALHOST_V4: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST);
+37const CLIENT_ADDR: SocketAddr = SocketAddr::new(LOCALHOST_V4, 5000);
+38const SERVER_ADDR: SocketAddr = SocketAddr::new(LOCALHOST_V4, 5001);
\ No newline at end of file diff --git a/pr/395/docs/static.files/COPYRIGHT-7fb11f4e.txt b/pr/395/docs/static.files/COPYRIGHT-7fb11f4e.txt new file mode 100644 index 000000000..752dab0a3 --- /dev/null +++ b/pr/395/docs/static.files/COPYRIGHT-7fb11f4e.txt @@ -0,0 +1,71 @@ +# REUSE-IgnoreStart + +These documentation pages include resources by third parties. This copyright +file applies only to those resources. The following third party resources are +included, and carry their own copyright notices and license terms: + +* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2): + + Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ + with Reserved Font Name Fira Sans. + + Copyright (c) 2014, Telefonica S.A. + + Licensed under the SIL Open Font License, Version 1.1. + See FiraSans-LICENSE.txt. + +* rustdoc.css, main.js, and playpen.js: + + Copyright 2015 The Rust Developers. + Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or + the MIT license (LICENSE-MIT.txt) at your option. + +* normalize.css: + + Copyright (c) Nicolas Gallagher and Jonathan Neal. + Licensed under the MIT license (see LICENSE-MIT.txt). + +* Source Code Pro (SourceCodePro-Regular.ttf.woff2, + SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2): + + Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), + with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark + of Adobe Systems Incorporated in the United States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceCodePro-LICENSE.txt. + +* Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2, + SourceSerif4-It.ttf.woff2, SourceSerif4-Semibold.ttf.woff2): + + Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name + 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United + States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceSerif4-LICENSE.md. + +* Nanum Barun Gothic Font (NanumBarunGothic.woff2) + + Copyright 2010, NAVER Corporation (http://www.nhncorp.com) + with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, + NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, + Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, + Naver NanumMyeongjoEco, NanumMyeongjoEco, Naver NanumGothicLight, + NanumGothicLight, NanumBarunGothic, Naver NanumBarunGothic. + + https://hangeul.naver.com/2017/nanum + https://github.com/hiun/NanumBarunGothic + + Licensed under the SIL Open Font License, Version 1.1. + See NanumBarunGothic-LICENSE.txt. + +* Rust logos (rust-logo.svg, favicon.svg, favicon-32x32.png) + + Copyright 2025 Rust Foundation. + Licensed under the Creative Commons Attribution license (CC-BY). + https://rustfoundation.org/policy/rust-trademark-policy/ + +This copyright file is intended to be distributed with rustdoc output. + +# REUSE-IgnoreEnd diff --git a/pr/395/docs/static.files/FiraMono-Medium-86f75c8c.woff2 b/pr/395/docs/static.files/FiraMono-Medium-86f75c8c.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..610e9b2071ec1d6c47a7815010acb20f0abbdd98 GIT binary patch literal 64572 zcmV)QK(xPiPew8T0RR910Q@`v5dZ)H0;RA30Q=bh12nt<00000000000000000000 z0000QfdCt|ARMZC24Dd9WC(#036~cU2nvUn0EUYU0X7081Fkd+mmB~DAO(#V2kLta zfgoG@QnCfU_8h&L-}+=hkQDB&>THhDR%QSv!r}kQKLKP8*!@ri8(DgYcm$Qvz555( z;Hee(t~XQf7S6(lDEt5a|NsC0|NsBJNoBZ73rmr~{Om;))JPZ@hzKUe6omz(97Paq zX(QOyF7(MthavS*R8hOJt&=jg=t+v~FjFjo-bbv8vYt=|c4FhN=ycu&jA%PdHAPSa zgOMmZLm&u}NZi{uF;LX7j%bt8f<1OyO9%Ts)`z8+M@V8)=<(QnHD%Z_OdF$V)|tA{ zJyA*G#B3v1r##z}0XEpV=y=g81ta#9K87GJJwt4nN_dTLK*Q=hqUmwl|8@q|O;*O$4z#?+E!ZaY)lw>wxO0b^O7WXM0mY6C!K8)I4 zvH?@T@qxcekB=&uun~K6JVITl+j>hY`BcHVE*FJjT?bN2L$d!A7JgTVrIQlYj5UFJ{cH<87oVWyht zjoA0}e2cYMP=BWG9oMJr`Ti2udELHX%9r>!T}}p>!#;vM>TyoYqFd$YHXpR27t{k) z5=Lbqj3Oix+3(og?Crw)d-+4EVwL5;AQF-^n0}(zHJv})icUYT4cMrZ{etW>xJUQ@ zr>F8?L~h=UU!~sEiygv_%k32yH;n2A&?r zdfqd0lYO5R$~treZ)2GlB`MSDx?jBsI${BB`Kl6P8qfbUY~#B?^*YskubnS zqd?0fT|i%djsM@=SN9bKu#&3q2kK5UP6qfdkpn({g&|UA?Fus##!1^m&?-`VgrCWQ z0OP2w-Zhc0Y*#3~qZLi>%qrP|+iLCJjy&$tU3 ze*pBOKdq#l?!7I8h?@iN2%TN2kmK-5QtKN0TpZ9X`@Gs z*y~wGfktXcaN?fyj-j->N)WMJGLSeIqUW|X{q^0wU#cJABG;G!gh~YlEX($xhO2BwU(MK*Rm`7Qd9vrYsjH0_IlI47*<%O+*@P z#L8c~e!JjF`{b(_@MU^xiayuohHiIrhUapInRIwb(E(1?PB_nRm>-)yB{H8G-~p2a z5<=>hR+V3&)|L}SUI7$DfvOt{V=#(cSdw)!b%wBz3L(L`POS5mSkud%&?N;U1iOTw z0N`V)_uXOl`lymf23V0U|I60$9>Faf=q2<_?SVJ=3Jxig%IWjo)21OAt+6 zy2StbSJieO0Bf>~M|>ssRZ9D8a;`O(eQ!ZR5-CF#lA|-c3ulh@@rS%mY4TT_tJ13M zWbCfA0uo7=QW~!o|G%}@yi9N=lW!|bE7fj*COoGNlDd`eo(Nl^(6JG;8=@(XC{Yaj z|GujKwf6#~C`vC-dPy(KKg!Rq@LT_{UJTOYRQ=EtIA@=8E^Y+xy&x$hQUFNG07yvy z7zBc{&%FTkEl$CO4 z?!32ov_Q#*p@{pVJ`^44`U(jSRjcY$Pa&eEv&l{h@ah~0iN(u%>JI&AA4=1uSsV%l z5Ac_&dD;K$G?WlVEk8B~A7xlDp*7lS#S2ouewxzE?S2KvD`#R! zZo0ElDnBD%TG2e=G=1~!*rSLBB?LhW2n=8VGygixRdTMa zQas?P$ivi*I4qsIj9&_VDMYf8-09fyffaUq$RH{1Gq=*Ab!aHq$G$S)+q-vMkcVmO zFt_tk`)^I{O~UeCgrW(m6)rMT0{-y2AiGGa7p((YS3p^w=iO)1aSJE{yR#?*kt4sX1fpm6gwo&xkoC0$FTmXHWB2Zt>9V!xR;nhMN$KyFU0GG5 z9r~0MNJv5<{K>Os-`W%IN)}}x%4JFStnE)pj!*Q-LcI=GqDNB0;^2t#UuYlJbb5W)zdrrG2Eei;x{;A#8% zw|lB)*|Mxo=$MCTL_~^;h#05Svwr-{&wj1{IoP(FL#Ct%s3;(sWi>Uk-{t$hcdhyF zXz-U(Er?WUi?HfTayYd?=KTJ!&_%svyPpb}fb4{kH9-E~gG{w5jN`;{lJ@R$9m@j@ zn6bnX651e{{|!;qc?yWh)`WgRFLhlZ1uzGXAB>YwNPs1B2$9cRlqi;jQ0o?=)M4f! ztj_L5xD|T|hO69FwnMPnaTW^PH-K=fd=->iz1JYz+DM|@x;i3Q-Gh+5mM7fJ7Dh3FFe)FeM$VxR6Et4r$BqwSvj z=X)pZne)Pk(8#ivgvR$2y(}VZN7XALTV4_E>5WAa1kz9ZMIloRWQ&DdagZ+_6iN(= zB@U&MfO1Jfw&Qw%e+w3K9$QhU1@ytm4I7r0wcTN{C9TWx?kAjIug#hLM zuhsz=pg{Sri2(~v-5C!uP@&D36$dOp!TNX)FX=8>2RyP}`9VIaf9d>ceg{$cgi0$Y z_(-CNuwEtstem1NoT#g{#Lr#0(#yGi>;{l&M6jNBU_OFTfMW?d4yuSDg}5|MS~=Zk z=38V~EDya77QP_FzK_M>q_$ey^d2n^hDKux0n|gB-^WgBarLqHY_>C<^iiKvrWVky z{(+&b0u^d`0sW}7mes*4+XXhIo?yAc&-T58PQP>|`t?J-bM-j$4RfkVg*!M}t-DlBbKr({jgiRVg`+-Ed|Rco zNDjZ2>-6if*uWTRV=%@>%8PTU$<)zFwcBeg6(&@bIGd(fM^DlNTHdolgx@FEu0A8S zwiY1&3yjBAVMvU)0cJlZ+OGA0F-PPgJU%Deu;Icj5at}=>~*luF?aE*Ii*`u^_dzc zy3Q!Dc^-g24B&_x5aGAL4hBz@Z)L`l@l_LvNxbt!#vf}|I<*X8-q4QmA$;|wcnwbj z4-?a18WvdSfVyD2(CTD`KHD^{25G2QF+BYZLogRBj5N&9*e?JhYNz&jvA^Y}=q1Kb5*n=z2WA;HDQEMHzE<>FLt1*nrwPKB zdCo#Zr@mF$!#Isw=}4YDtzl8EWoqpOs@BYgPU4~Wf|7en>S$ODpgSxw31!#1LR?#0 z7C-l)D*a|Eqmqh&jfYCdN>v^BS1YE{;g}Bfook0fOd^Y5bRHKcLbg(lRCPQYQQ1oE zZh{9eJIt~p27Y@x%*?;8He)eTo6Bzc*ykR4=EvU4R?aB=sZ5O~9Rwu$>I%zAZ#2M^ zI2lSk^`6?Kfv_%PUgT5asva0B%e3RB5;1gXk`ZGl7?fbKCVgY z)sB~Plp*z{Hl1ILrMT12@22qk` zg;r?*g@i-Kz++(hkIn7qu(#VcWLLw;n+-L`@%#$i(S^SaG-0q}rKM~RQ>|=2${U(O zM#OUhYsl`|jJJ=X{pyiHmi9S+$Z%?Yax(zk5b21$y zeTB}cHpV?xIyz6u;)ebamC0ah8qoO?(@oA%@@=SEZF^wn{qpv)Gw9?oo?*~WgsXim&&TlbZ09$@dd__?W}^k;_PE*$Ti*P&oNU!))4LvhgL z0O!tx@|hk7&4A`XOOA6DvPIOd(74xZ6O1k zk~$UW>e8Ru7rOB$=Rv~HTTPcV?<>(qj@o9(w~6$LTOpx(#YAQ18~3xS-t!*;o?=i` zd=vl(=Vt~kFN~YdOX|+?pJALtT}=Tl!y{kS^>oZ<0n_H%y*S8>bcD{C*HR#?t!C8y z+8mtgv!x~lR!>6TUgxa6jheqCSzdqfmbLm6^;mst74zM%9e>MMk6O5Y>P9<1&gn2F z!@hpyf~-QU%Jde3S*{(ctG8qdK!6{L4ux7rv%!jFCFyHu^McL@x&rHhX{iIRv*eZu zu1pAZR@BadT*e4BTg(#O8H6P{-dSvQW-;vTLt{4ZLc3TE^3<6!_^lOx^zTM@^c3>s zg2@7$V#Kq=3+i1Ks(($|bH&1x6+159NK5$n+K%GfP**IkEKPlDYGRi**1ODHY&inC z9~SkG@wNO}X^q#rM2lC6H`Kc=RR5l|-~IS-;=xz2NO301ly8Q4mUN>2Fidr;nE=^7 z+@A)-0!0!Vk37|SD0|IEGe?Yqxp(oyFDuGlO+A^(f>H}VzN3g|luV+ImUZ-TvW+`F z9w|!$-vrVuGU0G&Qri3zL|i)3Cq4tIm6~;FG(S7jZgo1-V}1H^R&@p8b|?Jo3&BBQf(71$XnL;?7BB zZkzm^Dt*tix(oMTmSM7G0*cb(yatP`6Z#u&D?xQGoVsl9}#Q_2! zp!X-ByQ#L1Av%9n8CttVhjOi1|MZQObgMNw8*dIa7e7asGt7k!R+c8;tBw9~j|u17 z^MRQ+THXwgL1S#=5vW88oyFj6w1$HOv?jKmL29(vT@KBu8Ua#GQ#&t8iEjFG4eHlvHG#sRsGh2uolOjd{6u_aaG0;4TS@FJYsGg;>A8)7YX62Dc_7a zZ`FT`w*-ZXRH#;`L8E2@J@d%SYgxQgIPaQ!il|s0RZQma3|MsA`>%U6F1*a~uV9hl zq{y&%J5Z_zTrS!zx}&7o}tN}H(`pRF_E~O!B6pG!_Z3(Z(&Pb ze(!%}*u@?-+Y49{h!ilPcE?3#?G2N}V0{6X-jvAOP}2Gte$+QKCz3g{Z4DMe$u)&>tCC8^i12@mvV87WS4O35#AC!E6x+? zaV#?B;lfWSFZUEcjgD?Ul@!)kYn>v+;S*^hLu85EKA=SF2m^e)FYZ!=^dtmexxXv? zqG}C~5gx7i3wJ>w2_*z9hEQC1{x^y849Rps{jkPb>-@o0tVF3YJ$ff5GhdE^auq67 zsaB&_oq7!vloJq@l{=X+?^J4yR!`r+(8%AWW?{woxNR}@L?9H2B~qDO;ZGBjnI|vW za*lK|zHx`#VRXURQ7=zy>s%u~?OZ2bZYzn=ue@S@D^bsv~<=5Pmw$fKW<0_VsnU$TBo0nfuSX5lurixIjK)C%x zZcf1ipU9)=hysc;H!IhmOzmvKfN<5Zwu_O@`-uwWKJOhUi#=WKonC`-UAu9PQEtbZ z`k}&GnRbc}#!{|!lx8gEX2nrVBKjgtl%$Kry{X9M1JCEhoN&fD7hId$b3ZvHm#)qn z__>~3=<3>^?8e+a;11ky`dcgKsXvjExjbqZb1ghqDJ(p(iJRP~hqp1A)}1d!M8xO)od(!+zN69f{3Cn8Z)pycd8Izijt`S}BtFt;$D|rD)=hYM-~I_@H5UZS zx~`(-Ugap8vs^efXlYjQ@~k=>8?~&e-_{DeqVsMbZ-y^20p15?g;U@Su5?p`is9xqc+u*~Wy z2Y;Ahl5C*&;IomJB@GHLA9Mz}3J}F0X-cM9$){*2BqR;0({|1gxMnm>$H|jnQL-|@ zt|1_Omtb}rwE4^ZYi8}No1!V6k|~|CDW7{Ypz1o68!UKvA<7RGI!xH;6~@FO%ukRo zQL^MIP_2nJUHS|eGiAw^J!cnIu$ncVO2jMg!F6BX_20k^651p;bJ~D_3$YLniI5D{ z8y+=!%-C_`Crq3)dFr$kTR-l~aig?K6QqbeQGtS^5jBBgYD4MNF2K@#ZKc;k?PUfn zKs`~gke#eS45eE{gHejwwNrbYSX$1pPD-yQ!+^ewcTCP&ba4W|n-sx9Fuuh(q&p9} zE`~xEM~O?I)+N#5Qn|4G| zVgKA3wS-j*fzP%3W2eq_%DJu)wpF^vxK^WQl&<%q`lf$StMv{ICwF>Y?ePDIww>MO z&JDQH3c{knle?9@giEzd`e-TK=M4%8Zp~?zAxd}~D6TmfHeli4Db0hiJDHB533&|% zsH_RV>1)$UWfLAqiYM_8v%z|y#f8(`6fXxj{uRDHI6+z@+I`otpkHAm(%~5Z$#n`? zjR8I^ejUgVXkh-E+>wo#zi{pY3?$2W#FN+yXlq(zWCvr?~?lAK&PhMFsr)z%k|Ry zRjhie$M?Q0P@gbM@&sX1rq7w}`9;A8&g~csd)9rOMP?5fohV=^-$l&rMJ54h#G2&c zXg$_^R+^PzWm(5{AuyKDx@BNH`0Fb_2s<$Mmf1>RBZxr<2VMHC*|23VL>O?k0#i-b zx$E|hT`zoj%Or0ysMpZ2Yq9(hs9A}?fLWw5fwL9y5B|!Cs79GR*&{vI%61RLh%b(TKtHf2T9C5*u+4NAa44 zeSG|%{9=%>#yT@QHynm$-`-ac^vB4LdP3!;x zaCG)X;j0-2mIO42WGy@lX0!nqMCbSe zzc50dy9f6?5gb%k!m_~r+s5XM%^O>=U*4V02hWDH>1;U!=)%Lo3nG>)0bm?SES4on z0ZgGKk~nN0=D_Tj?YTxt*e2{f_8n`)x-mJX!7hmv*hBFY){fZ$I`R2d8@<1jp_#-K z0G`)kqXGRl5+|$yOT*TEg3eDc(9oN(7OSL4;*{7WatT))E!K!R0PyU(_%e1#94nSP zNEdyo*j8*M8UO&ZQlvPlM~ij?kNC&1)<9??suoLer~;Lue8D6Dk9dF?DI8lBrJ@d~ ziQp@0AozhYQCoBtYAu)$j0r{sBYe8xPniDI$ZY2#K+S+Es{;7qG=XnPjuB4J3`~lK z3V#9MPqvXi9tF+ABe|(gwL;gO zc2g^79t_R&f-UR||H1V%V4kIs5&cA9%ymyU+8cwtw#JOEVDCie>6&%QVbG z65t>P>ESKQypi?ajO);4Kl*a2f<%nJyT=M&TG4n2zL{y3iZ|?ErOItkp-Qznd+iwX za{n8Qyo~_zbpl{&ZmqxE`$T?u^9>pnj{`#z>+w)V3Y8SpQns0jEfj5{rk=V62KLdi zlb$^c?Wb)QQwNx6VXl>#gDkZhPr%7h4i5WAH%CX<=`_J1E>3WB%4r_{=j9wvXL%do zW00SV0$k?rk|BYvh;mn`8zSAA99jM>8F1E+Yp!b%Ib@$* zF1X~fD+Zl+RhQfvn}uo>rcJnZ5jsTb6on^Rml%Apx=kVw2Nh2!fryzYdJoy!!*cER zDX}D&WD=7}rI1M_m!?O$UK#qbj-uRvY=d$P$u%sG!bXXxdL2!j5zo|^MmDAxw_9V9 zm{LtsGa9XfZUT1u8_sk33*fp!U4dSMyhZRH>?5Mj^n7)p?=JGw#eTcQpDy*c%lzwd z|GUCyS6bLr7In47U1N!WQm}?(&>U94OGJgNQ4PAqbl4te@Rs0+Nb=-BIa3GP#aF}W zN8RJD?@`zFh#Puro#bv}qZ>1ZQJA#JnDoh<)X8mY+uG7_+xM|4+}->1{ylSsHibhS zLFc!)Ro&3)-M0_zBNH@+?&#j`ZsYFXhxgHmAJ_P&rpMabu6DO=AKSdKiaM)q{^mWp5DlEe$Id9B@9-QBfaSy5ALUimZ0>X|>Za#;RSOQHGCEQ|LCxQWdF zCP}PcvJWEvpllHqZwWE~qU~H0^QDaZjj?x6l;BHn7T922709@1oCe?H=P70~a|nzZ z4J<_xG$Vo#s?!S^EpN4`OB5iYVJJkE0}Lb`9TQM)mSZt!6cPc)vDh4#O$wRP)ds1C zE0aidt=93H_yBDD4UarN49qnja4Ue-8v`7I?*J3{5ip7G0aLUdFinR8W0DE1S>eaP zT3{Xc1F&A=zqHsU>~^nc23=^@p6OaJ1Z`-=sMk5GY$nhK8=>tByH;}Hy}f^tasfY$ z4%l-#f(*ZvPG=&W+cpGrjh<7dL}L^l_BF?-TT+Kj#>r_O+Kh z!}lWwF_DX9R%l1McnDP!!;PC~<%X@H^l^k_J44hk=J_d*E4G z4R{XU1)isMfEQ>};6+*&c!@RxUMA_lDO(qab%GW= z?)x|YZuJqmD#+ukT=%z_N?Sx>#kYZh`oHgckqeNGuJP4=~@R@ z`Q|p?UFV0J{Pcid>lVL9;K(2M_{%T;KHq3k{=G+jU--*#2>e_3`Ogmk#~^?J0DED| z{;4$1`+zW^P6Yx19KB#F3lIb+VcPDhKq#ESOhO2QbC^W}f*}dhNkJr}VJ;bnf-KA< z7m{~TrD_y_SSZ3mN{|R;SV{#_pb9IfK^)X!5fNlS1J=-lbZEh9+K>euSVtE!p$BW} zLoN(pBSXl65o}-#6)=IlOrZ>Bu$ws)!UDFkgmPHH9@bC-8`!*41%5R0`X{gp{DLa* z8xDX!6KcR;@NZ|ve~JTjpaYJAPG|sKa2#|)J?MdBpcfiJADjUFTbe*P3Bu3TfZkvbUVVuFRV0luL9q()}YscpV)vAU=#jh z>jWdg_U2Kr1Eaw13C4oG&8uMl1mnTM=3Q`jf^pzz^Co!1Jhvg#D9i_H44!}z!ue1l z_ywd2*MZdFKae`y4AOxAL5}btsGs}?Iz%>rj%;%lMCw52lG+=C3aJL&Asaz=$rjK( zvK@4vG=LtE9iWF~8yKV%Ovnl_C9A;_m?`;m6=ER+DAjXr|sphWQ875W4`4?G{T z47@<0B=Eu$`V73JwlZG|UJm*KyaHMUyb?Kp*OCtKI?@F$LJ{EN6|D>|0hfUy!4>LO zG2niGtpvq_tC;8&cmsInk6Yp0;HHa#;AU{k#W?T*aO;N=a2x0aSTN`Mrf3+EJB-N%Cgcy(N&mqNma|-te*6;UM>xo{R@jo*C}p118tPSRbRV(vIiPB=r4ah5W1j>I@m^KpSNT%-lKL=s%4g}6deT%|?0MlxKd z#kfIo+@vMAMLoDpS-3;JxJ%i%x1v7W#{=erhl+CWhz9VO^6-QP@s#rMjE3->3h;u4 z@sbMhiWGQF9K0bV-ck|Xtw@FU_`v+|QPEO-q7i(iW%xpBe5K|1MjCvlmH0tp_(`kq zYtlG=|!4S|+po|D1?ShEuK}@@W zF?~pA52VZhGTI9{Gei&VLoYKzAMHm!GsXZNz#uch5FNxYGlhZ&T;z8o-?5HoFQ}IjOYSq%p5rry2zO_C(ev6apsJ|SgO1=f^()(&V|UFEA!^uXcp(rJUI`t<~-Z{?giO!-p@z2uFO#gjLfm>B0557tVT18 z#u_yL(^!jU1dVm*2xxpkGoHp*u13TJw89!lT(WMermQ1aGgq5o)suxGhLr%Y4%Dz zXpZKt&V}Y_{zYpo(87xWTBOAnCuxb6{+gj>TD~F|Dpc|HIQ4j4>2f|)Y17)hLUr2i z{?iWa(|^`}wYk&k>(_QuP=~>k)rl#o3sX@KCa>NDO-Fs|ck^{fhuvHqQO_OGQJT;1 z$G{489N(=I_|rPsyx&iO+37T9t23Cr&SEw?cc6J_K!dJG=XKFlX-GBScE3dP^vg8Q z{EDtyux^0`>GpvZt~@ zMsHn@-f7r%X+*7XZF;W{{+K>0{7HSm=IbjqPv5Ww`hm^W&jW3xe(AS6s6QHYCp4z; zr!99H!xgRE0So-kD83d6l~-x9k3E4v#xy@vuWdwbvhBKA{!864PM z_;SWYVCG^_b4dY*OLv#Oe9sR6!Gn;;X+ZNNxm%Os-z@xN0pZu2!uVZ7&;Y z-}Sw0Y6)52G3eG6(5fq;T~`%!>gwG!k6s;z9$gDNGJuwMxsPyibf$GYO7wSK^f@mmO?^f5C@Z#SR}?KJ`Uk< z34+IN;1h!gl2WN!v95*$G=!(6JUSxN(_99k zFj5*5d&)f7XDqDiX4qhl*x4r>>^>*^oQwU)&3@ov4|x$o-M5hLSv2Xc#g?q;k;SBE zvgPawB9}?GSmQ3O4)ls;HS?=7bn*6<0|ax3e&rS;s_2JT=Zx3`Jg z*v!>z;hMH`4coYq?cB}|?r0}hw~Oo8y`oBc9In_AMVlO@TE}Rk<5c4W{pTcAJ4G9u zzOBxAe{Po-=cN^^CHJ>>jDy?m?Ii|JJ=-}9o_n#UikF5?dsT_IkkD6-`A#AwwRxZND_?S z{QNE4B*XX}Jbx!2Qee#|p^IR{E_xkoxO}{cK(F~XvhN2#24Qd57q1KDz~O<`M!qii z4P>T=963k6{|k;Vx0ABAug&iYALsijEaO`zU!b1@dgO@ zY3Q>)CyU9^=jhBe{iovHgXkDiBQE{1%X?B?B+CW;AtfT>uxD{7B)pNOK*?Vq{Bz6^ zWlC3k-*+yY_vWO{G?1la;N5tcl7~YwB0`HYD@qCiQsxvxPRVv2qZ_#xoO*_nuaX)477IKLM^*#`!NKNNcjp(16w~$3zIRF|Th3YI#4p#$4KUo(PrY ze7-JYPYkXCiI8v7qFq)(A|k~1#i-bmK&1A_=%2{MWqywlGpqkx#1hPt;_XSSA{nQ3 zqSStPG~OP@x5qJNqB86u>>B&G}Le_M$Mp+39bjRS}udo&^IkE zGIDg5h6P%~I%~;oOi(#$72Czi{&_=f7w6upVdauICi$G&A*A6L?%6dd|?^ z>^$dmNr(&DbuLg@X0)MJGq6bN`(F2dP0f*_SkgC{APK{HmWJ1MJ(mClLhqn|MLh0Z z_WL4ry%^6`pRM*dD};m*QSo`MpEw-GpKP-_ZF&Q@+N2OnxKzIw`6O+c5nM)NS&$X_ z6&Uk{!C5Z)l8mK!S2F&%jxQ}HA)HO)C!6r(6sAw0;Ivspu}Zd1IX@?nTJBvbkT7hG zsv4k2?Ev98HL2vewm&E(kgh7S?xE+VEr~0kJ2(YP!jV#M-G#NM-Ymt3u~lcT`NmEW zC(hlb(oZ$7H(nB?ipfi(g3L>J?vxu0i;~x7*ZHw9X76EYJ@!)5qo^*mbw6wNJP+DB z9n={{Rvv!hhx=R_$Gk`TTv;y_0|gmcp}qlvE&JwqoLtPa7+4uor*?r5UVnE#*{^Ov z0}TJYm?a+89b=2xS^3~&-RF{d%2P)$+kMaaoHf)2X};of%`Qx9Kn^}DYb~co&x@}6 zUhY@WVRA;2ulBik5?+_0n3!5kdjsf^wcdxK$?NEUgLXsaECxXTUN{SW4x|Y21fl~* z5*BiTNFjjgfRKiTo**&^U^<{=VPPkT0t5&;U=_k5oFIx2AnJfr42yVzC_!+Y1dcCp zpZaJA&xa;myqTG$PKpoNEoWw*Bgp&*k_Gr2#c5YxdJ$wpLoy+)>HL>4fZIiqVkIiZ`>ravgXKQ zj|l}kI;FpK%vfx^%^>Yji*>m+D+KX4VVDS(im27ZPR-07CnAv<;K&)63#w?cQ^EQ| zo)vdVNu2Ar7DF3R8~hv~jDam`UYzL3KpH}dOulmbz;R$9f$JoQ=zw&!(=J(Q&#eSt zcLy?|>^&J1OIm?R>>RuVo6*?y9rN(B=r-Qv0P7C_%0Fc&>a*URvAUTb@ch+>EX^C7 z0}8S>1sO7FzDg{+q&&GeB&=8P)BR5$=JRR3H~r2{Zo7YailAJL*_iSa#8?$fPZGwU zfat(H0t!twFP>zwgWdNzYp}=Dk?*6+-C)dj1mEd>4Do8;`cjgfc*X67BtZfk>=-QTb0R-d-k+sT$k_eNIiA-U2E?99_c?VB@(MUvJKa{n_dtRs7a zZ5a{m{lVTF+;zW4g#XJXa6R&mVAEJzuJ_G5v!gw=7V+O+*M$emx$euOqgH>hE06Dw zlC>GLOFfjts45+Giy0s=5upZTTN|6JcLWKidQcwQ(y(Ciufe}l?uLi=7ceVq_HThX zh6jo{SLpg6#IjbbsW$VtOw*&uh0A4QmfZGG?Vch)U9DjygV+SDRd}{@>ewnA(ue#umzDCi&+zjw0+%yP3{eiK{$?=QCf!j_=Fa9_L&dY{T#oFV|)_4K2G9P*M35-U)8k!}*Y zKs~fqsL8YBeIwl{lL7L&9D`aET6m&cjZ~n>3ONQB3%-dzORy0yD>9HPBSl^;f(2f_ zqP4Z#1kGwzDxi`K6jDz+DG~Q08ZR|K#I-(me|;56aSFY$b;&=#poE!Y3v9^C*K%R2 zfei>b;8(6;B*Rq6ymxCMj;zETpwf_NdYWh;Bxf9|342CG_Is@UzS~Hi&BJCSff6qVgc{>bw5HIwfrND_G`XH}b3}$&o*i3MS+)E{{!xPN>ArVJ zDm2#}{RDyc*z)Ifb?U< z<78MV_%6XhtfF8|n-<_f&ck3v&4kaAjg%>OnNZQ3qVJN`so{L}qOdE_VSkps0y2s~ zaSJkGQ`qwSYICHB*weth@S58c%TmOWLTxyqVa8v)R#599N|z2h1L@JYVWV}_a4+Ud zl!<89jQg9ld4}eA)>0#VZkBPq$xWx6*1r{G4NE~ItV$aC>lZOvSa`#26Kmv_=p`Kv zi^LxDPRl{54LrSA6vwd4;4@ljVfp01ZSyy!HvS#f#Uq6b8_rcX@&~Ip=rOVSuM2k> z!SB-{ghz~B0lKdLMN+^|Vu#-+yK=aBp@ z(<8Tn=Z{cNb36gz@S2V`b?cUqYOcUsiK=JJ#lO{()tAj+Gp>e-X$U%KIY1oO+1J*Y+}lz){hckNhEwWzOq-s6Fu+NeepvQB5_-x zWl=ZqXD(tW!b$wdIDDO~_4c|nSHJwGL-71T_w%@!=)0REouHQ z0tiybzpua)X}wMj<=36;it_<-+I`K@G@s@zQn!MQsKF0;rYafKN>6{&70Se z!0xDj!mmAz{F*)m59D;7S`(ZuledBRU}wv2!)WT~`!6GkA~3soCs4 zWL9=Ay@$WkvWD{Yy|?6tc-g;5wF|Clyg^GG)$!XP8mQIsBoZP1tZ(_0zR*5Bn;+-U z2YLc|k|t!^rI*EM|h zgDw(AovYE5H}|w&FHVt~S_uUxg2v533fhh`3DI?5l931aMukk#N>97r9_;do)D7^4 zp$h0U-x&`0OcVGy5O9%tEW_=v4EJw;tX(&>QA=}0UbU-$Sij?pXE^ihl%-By=1a-^ zS}EPy7s|JS^DJ_JDA+p$XR_8X{89WxTkivn;7%SlH5|B&dFSpIMPTsACa0l>gC#x{ z1O3>@r5LPVGH2kdDq~VsfjCkW2EHRM2@nn+gPzv`1pGm74i|uYEQDN%u|At6iA&+li3Wc}3YuW0k6+OfSDe7f79J;Z)BfnV+`9%P zFuZ?64(fi7hs*o5;9tvUN~T;#0xO+&P_b%g2qRHpV@<6lA=gX?O;#g3s@#W2zS0#L z-Ld`;A_KkafF8%NpkysOW8?~@aMN_j@b6uTfI9AhSi|OMOpe}Z_-DD}LX9Py3f?t??rmgjE}A6c-wF_t79Ss0|6l+a|ozkMKeQX zlY_M1jVY+iVXeoyT~b&QI@-<9Q9&e%MUaZ6*%?sEOq zsVH+WOGg|^b(ML%7hw538CGC0(<9PJO*E&>;4Nc*xB!YjMt#1h5HfN}48mLAtxd~k zUutDCAU0=T9)vua!4A`5(yQ4zhZxJfPpI5bc;(o=BW{ z;RQas0}C6^B<#%gb83Qw)LZBP(Q#7* zR8#}>(-LlEr_BUvyRM{`aHtzoh&cW1x(xLyQ3RxWp=pt6mL5we*a;g-ysfuR$lplO zNGUbw3KF&nC2P5p3P_|X%M84NhV=+8g;qr$Y{N-U$k8PbJVxI3MUT(og%r|j1S=k( z%We+2%S(7dLqun1jDh+Wj6K%N;1eAjX*kfWUWR|+0|N#R#gfC+J%Q+DMr$96a1>F# zAT8jHW$6Y*hAE_Fmkm=)8gs_zGc5i>rqrE53PK)(?)XHVqukB5D4PihNl5(_DJ+qU zl0*T62SO#qNVc`pOVQC};X6Hm+RBTiu(8!u;|096WhK<~*aShXRHI$rBlA6pa zymu;9^*p4M9Bm7!RqB7((s zii8N+oI43;kp!5a_*W>U6_+6!Eh}ct-_>rORgMQh?Qr2G;3$5U8<#8|>iL;9x0eA8 zZpvYCA?Z{p^GKQ&s}xt^hSZ7|5Js9}{{bL51A*vPb}pBPKDGd6Jn4r!aVutSPkUem z2ro{R$KQ^u8TgLl&6iK8xn03>JIC?1KhqhFV>@U>9(RNT4r+<>u>kw0tIbvC9c1H) z#Eq}r5#`CjS!>L+3z-1~D1JQkwwmRN6@Zcb8M@;cv!~9DwMKo*vU>?OT@Z{ECM~O* z?M^;kz<3PB=xIP?Iu;=tw}29nQ^_18FQXcDoE!}+AwMM;NSiSR{BS5*HXAsN2I*9o z^W(is2z-eKkCPkj){DD}7o9ORq6MuGNVsOn_|EXy7}Gh8K4ca=+zMrzJ_xurZ5 z8tc+sb!uanF`w-c!e4!sxw%2 zT6d#6U=b9I3+O7*PEW#xFwcF8ENVaAp}Cpz zGVK^`dJyYX<*T&1DEHQyMD_SS;9%WsPHr$mm*ZqJuq*w8L6z4aj&1@W&uS<`=16y6A(2`jVAf+q0tC!0$L#Fiyp+ro@} z<7Mj+p6N56Qq%MKor`W)x9rv`b~#Tt@dYVlQnnd!j%O0vuJ@b|CB~>@8NPw zb$ML?Fu_{xinYo`{9}qU3P;4)0>*NTgG58jhF9G2H$dDIxRKsL!ge{N7#;tG z>odyCCgv7~C7eInec>D}_C>^_ska6^&>IuQt(;SFYmaeprZPMU=awR)ib}6+*`$NQ z<|8WV%9}vp2~CX4JZ>*owG-s9t#~;{l+Pp>a=qkL{ZTDZaFnp1FTzY1FAb<~FXfr8 zxI-7t_Oare|FM+o<(%8srmQs`&4kr%$=KqlZ&rV~-O;F;|)PN(aEp{>hfD3pK9z z)ygcIll}v@m!!VXX;@Ck<5^ITC^;`E6La+ z`SMMcu?5<#NC7|hkahnzS+AqQNRf558kA&%!df$3a;s-3#NM%qeDQT#^41{pqqvx7 zjW66#&2sFT0nQUOc4#=xIheN_^;wJ=asjV$r|5eEVaA25(@DciUWW&pUsfwn?D z1a(D~r2S1V%F*`nr`{yL_~z-$x8bt?fB$jmdr9yc_>zdVtW#}>S%HiV9@se4Iu@Xp@3Hd&s)|l#D zSk!z5@KtLEHzd4zef!i@WCbRgBNHEJEfdcDliy?OnPK~+QaF`k?>{W6e2j~6$ zyd%CE#G}eaYTci4vUb$a%a+LtDQjz+7Ho7F5+xJRgQdZWL)2t}I9KF!YyMDjrY;YA zQ0c1ZKcpZ}%`O>deDfi|@4Jnj0Y7(Q?OA|LB|1`nZ=IM+4QKD6RZq8+?!)oU=3Q^- zq(Wc|Bt!=3gq*8v=nbFTU>piT!DG+-0tR!*;mFv%(Mk(?e;GxFFCDfY$7K*sR<(b- zLQhDjPVi>|)uG&m@_Ea(i;#uj{%ehbvZKyL(vBkMw4AxZ4|oIURYD8t8|Sqhr4aiA z(qz4N;|D%aya#<7<%1j6A_+>p~)aQw~md5x!4DE;u(0}&xO zNIj^807Y=^!bOeN-1-T3e&r_)CBU5W%2EnLGQMT98JpnS=KH+|MM?L4!bSHmiqY?cJBSFW$Hk@YHgliSEUJ{J-vy`<;Z@YN00rY@m9Oqo4beRagm0{ z@pSvr$6#%R!I{ynnhh@Vn!LqoH6OcWXH#3{I2kOsE&gH9E!n0gD3(#<@RG}?4VvW@ z4kquBIaOvERvEcjDvGn!4N_+D?G%c0;|2bq^{W1>9v)E~g6UvOzB1kjB|BHj*|n5qS@Y+o<2sOKhl(QevGy}7Ncwq>;nh0y3q3Z z4lUm0^Uvt^kY(1$9r8pTE>*b*R?(w(rJ$BWcP4qL_8-Yn9|^$i&l8%tNn7KZv6F#L z1{zL8XAR{yHZ#F}qRQ{Jw8o9ryzIcdc*onfqB-GP@xYWzpEumn|Lird2y%;3@hd;F zWCT6(6fD`+g6D;P!3TvOFA0+kxZ~!S>kCu#T?o9b2pi}9qFu^g!KWO^&$9q;lOROl z{^#;TIKThW0T|o$wa)+2y}Li&3m7j0ZVSXB|2X1gNUl*oV(z@`+1+Pb^>nPkG>h&VO zmIZ!Sm}$%Fr zu-DaimOJ^x(!@@dmujsprmGGL$`9}CwZ?f}(y$GBWCkv3YjQci3h=aBk(YcnN*|664cH)RQ>%?I!(#`gg4tW~c-GZpK zey&f*&h2&oA}w0Ce?=^VIwHZ(W6MAX;9M)l^`?`V#EBH&cNi)y%j66MloOgmDF-H5 zBD#G4$Qp+KvwpIxrc_6KsS!yJz_07|mNiWUV5OH*&53DNC~K~lwdHjCDoOg5W?PFZ z9XnsiHbzHH&8%f^Glu;qiW}4_cJ)t~$TwP0!kX^f0V?(dp@X>88nS%oO^}j^XK;(v z1_!YI-fz^RWPK-;6Pq_Hq`&M?kd!YrPC0DU0xrN+0Teg%b^?Y8pOZU)X%WY9E>-NB zKp56ZTyz&?7E8HZ_1$#E#YjX>^!2PZj%;U}#N8HHOg}G%gKoTurfgv$4tOU`{SUB+ z_||Rm>k)UaCfWDZ@}Uo<(q0Xdo#}Ywa8LOKLiT$ghN|Bpw8f@~3v0V6$|ijndD*+X ziJJil)Pyn3+cO3O-i1D5cb0)Lan4R9A;!I9fx<=VCAYh$h6!fr;^s@Lp8rD1!9eV( z>49o4xJ_R0rqH3Lh(nrX(k0CiWgkx(^~Mx;(NiZ4GIr`z3}JA8$S+Hf<43iO4P>k# z52b``8Uvq;Dz$Gq|?q!Y@A4x=KWGN;6I4xDjmgQob3{8yBa=Lxx> zkwTOOcdF2r7HgBGDOPKk$O)2%CTAXCOuf}^W6vfNfy*&~lJ(rv7n z{-}M=zKj?{OUNa|Q@2SvwWUBe@5y6pdR+ZgzujqP%i{Ll!nQ)UVIH&H!vUid8Z@_P zy9-kX(~4KsPcsMzBkJf&GSXXU07Vkx>DxpY+^3kpGWwW5FxS*ODPF~_U`{!{&1>W%Y4L+wq^ma>=wbs`oi6}nC1#Y%9oJ1N&f4b}g zGOCZe;M%>%eBR&(+ih1bjB4Jy7jxjzccu1jcCe>;1RAR*Z8OWO^Pt5Rx*_aMcNQl- zIxFsG2D;krX1r?*0PpLSk-Su(g^;f^{JaZKB9}`p!xf0dyTYi@&fL!X+`}_jao_Nd z8_$vG#QJhht@*^Kd}~<1TE9gZG&|tX#maii*U>hm#iTH6uK&OX2Bquyyes58+e)qV zm5_r@M7+rFO2cVCxF;rN#yc~2Qsg$sN(%gdB146ntA~huLngm zzsGQxL?oT*nGQvHi6nA*b6#0>4iJPc#3Nl!Suw_6AZ!ZCky0Xy6xGe3eU2yemyJX7 z8U0(zbg7`+DL!wCxlFO#ljU9V9~!pmBFAoEvLGiJ5LyYNd*Zdotm^<}G04h<*syFZ zzGG`~ym-oquL0J@Y();KNdjS?-xpi?!soPkBM!5x2j~PSJADlkNKs`{7Q7}fUqS>A*y2s-(1H^DDr_ld1TFE(0UoCOs;J$+%li14vP;84UXuqqW`O4hb<=$(X|d!d zH>zvx9@PAwame@C3(I0?c_yOA_2p~&&!So2PD-_%;+iv9`ef%T;YJ|WzbRqO|C`_W zMXSE|e$sar6)K$n>_hG5NSYhp@R}K;RKNAVw%iZz+a37ZPP!827x0%77mbCyQ|$L7 zT`aWF0ohXVVKu1xJ zeRF#^KgMXWr^d_izSV=Y8~OYn}dEi|NlK%m)*Tk)`s0 zKndqUzOGMcECP`M=O@XtT2#-^0k?4HjS8eWOPsucp2xw%2G%byE*nW*2MXP*l|AO{ zMTwa&?quGwhkR6Ve*DB`g#M7@IFTgGRW2B+N zN=4q8Om(uF)BJ-eLu{Yfc(IyHqVgg5+O0l!UH#fi&V@7BE&SU8y<3cRPM-U+2BHsa zN`bjq1Nsb#8eMIb7(I9uW>Sv{T&1wQG9y`vU5s)iMvk+sqfz8rto+R&zly%(*vVAe z-crYSksD`n9o04WCdeJ~8nd|>5HyP-NJ72v>vx5NRqh*hn7QYFmNto|AdGh`mpmyo ze&MjjI(qh3ekjg2SYyJ>f{n5@3$J`fxz5Do70>oijOj!6T1f;G!I^kw~+O|9%iB@RQ*)lUZm}vi;@bN*%X5v@d9DhUi0vw_eeTfq7UF&VVK6NIr)a2u- zboJ5%x6ZJt@)y4KiibN^2>eY+7nMKtEomg}nJIxE<|?0o2+HBbKmDa0KnL&tb@du;-cBt93 zWz=ky`cS(?l7B=%jHzm1(K1t?O$0vMn&A4~s1Lwo;tBQ>PIxMu^Cah82HgIa@?Dtw zkQ;ud)G8apjSv5{qCBa2SpJalw|jinxO+2D?%Iq1ajFHV3jLl>6XjNb)syJ4rv1Bk z?f&VEBnzg}?WsfKNsU_jI+N2+uG+tUZtPjo-{=q)sA}ZtNU_GMulvX?#k!`#Q?Dim zUb30?#earS?UOBLMq=qiPHY^q^uzD}ig2RltFz;(pwguSLQ)|D}M5|-lOq{2CF$6H=up4DcoSbtlgQH?2 zz67xFlu9`=|9pS{{Iff^&%d}UbZ%$c0_l)eTbhLnWJ8)jZ#4H+4S#Fnb`Vk64rrAt z8`<(6e{&-!4}2%I)|riLeLxKTMcZyH`Hr5c%_*c~OK%J;{l2$1)qgYZ(-Q4pk;Kts zOC3w+tYWHOIk-!ZjWvfS57wS&U^qy zXy}-?+LmNj=!9CtO@ZZ?USptjSyP{1qd8R;aT6g}5 z2snAiZt2uyh=H$iXY4#sbxv<$MQ<6TuI=N=wl5}!^IyWAty0jNQR#n*{}wqXLxp1X zZ|$<*gx)onos+|F=D+Da%ex<*w+^`;+5J)AtOQjRj(NU!V5?%gqU-q{2&g2X-5@C5C4$Ii4PBv90=GOaqdpYu7(W|AM0x*pX}plwbkOW*3q?l(P7W4`|B{0U zRa8f^7)_Cz-5Oi1vxb1E@n-K!1}ZgqLZ%3t`G*Kc#GBBHIri-lraUd-)7yM9nE-`? zc6Ghat$6x}292*QSuxZTSYP3yh^+o+!9>`WQJ$}?g5hQ4?GeA}5e^PhyWgUYKs(M5`!mC^JCpP5dBN>1E zf4uOEmr5vHpD&}XC}T6-?E9e&Mwr(@gl{c8{9rrP_n25M^@cPt{^<-jSO zeKSN)Qbuq7h*7x=%+8`$Z8D| z3;zz6=ivXhs944R5o0df(n!Zpx4nfA5Ci}RzwzJ$=e$e15z@VSV4!zRQ*+PC!M?t= zO*{v$xU8I8#OHI1%gc6l9^soW*gf`a(M$=l%aMcWHF0_!V0;;p{8mRcrt1`)7v|dP z8%utRckAaC``g@&zL!P+G>CEjJP`7q=oqq=oHLK(eGI&l0&#VOBEtWL&+TKwgZ5P} z)=Q{qNh#ihQbgJ}%`)d|u%t+Yr?NWFiHAfcTU3>KZrpAlB%`->UAYzeHkkN*S+w*s z1C}VZuQ$J`6LZ+!@=BVgu>R`D+q5YBIiSeX*{u;aA1>(QDMD=gL)8<|_9;sw38>!sf+M*RFmg(fX4mFfN~&xgdKnjKh%x)iOIq48f4(3AV`8|^!Nj4%iRPUU{0Va5RkWI#3i>!}?6x|6 zoi|(S-KpQ%3Nj#m>@fi`LEpa;m$*EOTT~gP*ukqC3sfki1S{;DaJOo-^q3n<)@jYU z{*d~=6gfg2tAHu(^km00<#VdMaqwhH=kV10sTNQjw@ui#(3R4_xTc;TlFq+)YwRfn z_%j*1|C(1n?Tb(M_Sa{CL-Rfx_zV@Am0Qx$P#c?MWxh0jkD5$rA3i_-d<*PvcY=G3 zCEGv8v%r$+C&h6r=KAAj0%sgn2U{B`^19?um_A=vn1y4Z&RQ(Z#6*ffC0C@y=F{Hd zoC1lKvNORmR_)+y3RUODa;b1fN^Nd!R{EYCkj6ttklepe{z})`RIJ0m*aWsNtvLeZ@h0Y zzSi9Jx>RL-$ d%(Ku~W!i@b=dHzJeej~k{ok`*fR$XMFjaaKVxUC$zC3y2Zg@@p zBg_V#h})X{;ueyGG$DaPgjp>nzafe5joy^|-s^tT>p#4|Tk%yqauWMU+HSyR{0^aU zE6Mm)4PTf%l$%`Qqyi?V!kj1dRttdq$@K9c_bYqG9@Eg0s=Cnf`kt#(t8=~o*i{;q zs(Upl$4s(>rw-=i`BmcyB|ofq9LK6e4Vb9z%cBUp8KTczoVq6SX00>$@0x_>YJ%_ERiTCgA`9(Z=B?r zcF|OjykhSBZ->F+16AJjzN8%$8VXy_YTcz zVEf0yZ<|KW^N6Wo`}=cur*6ifu~*v=7Si@LVSQo?PhFo1gGR4{RbpEu$rzJjn=b`* zKgB=%v-8lw%?Ddy-W6T3ZR=LA>Dt&9B`uYrz2|PZvL+ivHUE z)hfylboB`8m67}bbYnt~4oE2pG*`xiXGzcS%~k_R<$3E(nOHj&wm-3lo!ElNsR~cX zQ*>Y|a;wtY`uI%aB&HsGKZ-g8%8$5k(mTAYHqbH_?7N4!A)#EU;cEw1V;`Qhr@19O z7U;cU4;Fu;PjYDtJpIrb((oxlRXG+7ygn^yy9pMLQ_HCF@5dEVb4N~ZcB5M zY4bv>L%eKe)zto}iIbz!CgS=>0u7|V>JCP>K|GMCCR?SEqR!PFeA7Twh~IOq`*4TO z?W(PL8+s?$LqlX+#Ub~4_kPOF8JiKYq5Ft?vZ~UtWA$&@dF$4+6os`jn)rexnWl+cp|ZcCn^7%N>_cWs^gli zt@Sl{p=>UVd4B3v5?zO=ZE<AL5}oDOA30^Z-ywirN3 zEls5w0L|W}I;}M|$~Sf)=|nap$C?9WXO{`Gy}-)DmD)y)W^7`zQd7lUo5+<6>D3vn~yr46rBQ-zB=gI4SQ2qV9 zd%*lTQ2mQ2h3CZJFB=gRN?csRTQU46Fy0wTTp?KujF}XI<_O(kfi6bxnhQ_ zNptlf)Vsxf`>VMsbZs1fUcH7ldTz33RNDC9#apHUG+SR33$FY?cgk#4A3u0deaxC| zxSG&jwH7>(pK~ptqiP(|kKFg^BdRn`gfXPp>E~(ZZN^mKfkmk1X!DkJ#&zZ4@*-o= ztb_3-#BTIMoQn?_%uA zS_27B3NJPFZWLzkV=#;(`*~`9I7McXS;N+9Q+0=W53ud5knt^#A0l!)kHF$Rptf_u zkDG=L@Mvr$Rm^de$#9bATvk~XmK~z;{B#ah05d1e*1- zI2G2vSgiqf$SrVvbicmm`|$TYdjG8+w?r7Lz++<8T7=U=pXbO(Y|bI|r2^lLg`F&h zJZbhmKi`w|(2P%{zu^C@sWA91&Ycy14F4cuh&V)P?rkCli*O2YIms@eTb z{j)nv&&w;Nt(ntmYS0iR;QHmCR%0bYqmu_(1l!u|mc9j(yH&H)fv7NUPN$|;#ojj- zswff(D#9$It^)lTOR`i6q)Am$5l>zy4XQr63HNSw-@cM0miBK@lOSt|3_&7;(nh&5 zOWHEKEq8XSJgj8I{b+t8m?p)_se=h67%(r1#)uaq(U5bGewLWGM0C2rLcxDSBdvH^x!kyH7ciLz`fbp?2e{g}oOEniV?)hYxUb~z@YI)8 zt{C6=e7TmZAS+p-5%-`8_bsA8rN=2rIG-%o++B&ET?s*VWJ#y^k@Cs_U4;Au$K+}a z(%w9gn8RCGKIoD;#kq3rW}{6lm$OAYWue~f@Qf^|@Go3epq$7igOF%*IWtloEH9Y7 z9Nd1;68kuun2;wcR82beCS6szQrXcvEJOW>XLUtphxLLn>GRlSkTnGLR=TQkU;4th zk+k$w2zoS2(u}38A{m6r5$~W`Y;{;ScC%}yYGB>4noBGcVf8({+o>+&vITBVTM3J! zD=#nFwzv3ULeY^uej$NwE#XE=L%0PqEjaE>zyhTSG&C|*OXIa9y6O}r9-m{DsA|pf ze_sqz43^G9QK>*%;?l%Qu8 zcr`)O7kbtMm%75xJIqowQ_J)!{lXZ_S-f3PW{qU6)+jhd}h^*WwdBVmi_c>t); z?r^danAP!JeR<8SK@qp4k*3xqI8NLhcaDG67zJm)5DGU^3gkvj6lc~Rn+k)}hSo+w zzNA?!t!OJBujZg6^?X?kb@pY6Nm8q~Iiyl0M#PM76=jW_miLdjve=mXdF)c~nl_FE zAY7=hQ4ke(oaa-%Sia_u9hUUCg4xb_Cq7u3g zug}oswLEe{RmIaLB0XiNP*?6O)Y36BVu#qnFJUtvfUt2;UwJRSCe2;dp^^|-z3=a@ zTvANQL!-=WVYduMvb0oeb;`y7gNg2_W4WmtmVEGr0GUg4NXKhe%7ieV%(-8qvQNyd zj&>#NshV!6SQBZCE=sG8`y}-##}_FrLa|AIS=$L}epTnZU+{_JlLGJks^g;A392o7 zN%-ubWN^q;aAgo<$v{Fk24TFiF0I_&483R;vds&fp(P$ARl3LyVu&O}4C+H}AlhX5 zfBA7?TtIQ-{a!%B7o6B8`?U@I&@VeJ7et0Q_C|p8?~1bX9)y!TGrPb7oEINzzPP7% zs;wv`bhminoz@+%PJ{Z_{p|fEgpLYL?~Dn=wnO_^(gu>^d~#-WOnLJ4E(D`GQrhck+JC0i`t9p$0?t<)`Yj%5bq(;@WO3M2I|8U& znNkI)TKU@@IBPkV5#SMx^iS78avK$Cp>8nq^I;#=d>#1%c7y;O;T z9~3`@c$SY0yH}LBoPALt-qVWw_kb7O=<09~%rJLd6|9I@kXuw;4fhfzUZ==~K5VbM z)ZO>>r2+-n77%I{uMCZwLev`aV=}zGTsE@yb7vuN1St*5smmf&S*d`T;k- zCeT}C{2pf-15yh$F`6mk;n^0Bo_u}5Md~HzC93RV{f(5->nV-?G|@5phII8p-bKSj z80|ta>w^1YIiUQ%kow<&_fmYCRO&Qknx!sRekJ<4@@tXeD}No{7T(9&$XYVJhfCI9 zpf;4SzeYUtK16c9_DR34d{|J@(<4=TS*@(fy@D~zhFyZ2KacjQQs&)$Z=VffOJWB}s7hlw#I-_IZyZ0Z`Fc z=S~RBg_HyW5MO@&hlMrZqr+XsuBV}=PNVZ7qvI0)Cp~mH^g_!+63^}op6VBZ5`kPZ zbv6X>B-_5+b1*ynhy1x)07xRbHph>0(2n?d!jYR$Y0v4P_{GF}oV*cu$( zSiE5}UUzq-Rl>45u7t-W?lzDcmCuD@0BlL=;3Y;;O5i@Wh1`=4l?KUwCYxO?RboZB2bny13~UmF{paVpQ-i1K z0FDQ%`D)T8PL;8%IX!DetS3H4hlf}6TRHs!g+|ebF-Y-znXj}|?v%t5^?aehEEOB| z(hm!fqFKo}*oti6cDU%4DY6hnN>X)v%ZipoxuQGOuvU)$M7bU}q>WGVCpvJi6VA=) z7~QmRQ_JT0tT8dGQ)$puUU|!Pzfxy7bR{(h4*i*KU<9N6Gx&P^sYw(zE?UeN>1o!V z^xViN;A9I+RhoQZ>2cY4Ygl8tM?V(sdup-k#i2s7D<3t!TC!}E+j&@zCKt%d3rQ@y zYgo)0&?FV?>i@{3zCqZ{(#!NL#M}$X?`$w~P3u5RP!gdKvB2OskwtNjXD!T$j;jp~ zdCF=R_f+T1o+u?V^>Yl8e8UMol!2_1MUC#NHYIy|qD!xozyCN=W>%h_|H15QYj}Y? zpIrSY1PDEn3_WaG@`TMUHoWs9C&beWVgoV4@Yd)lP)sFdRY#YnT=z~f|D&H+u<&CF zdBx(E5j{C{iX4icB4bj(lrrIZty*@~3c>UN7VV|bG5QOZw;q{R!~{M0X{61BReMS- zo;a$U82_YFy1u*DdL*8O&`IVdNaxDI{(#F3~RNe6Z$33P;OYPDDpd;2>Q0 z%}%`l9lEID6Nma94?XVL{ianBTzYq)?o#8W?j?7d6)hx-P_ocBWFg3KobGmn!M*}^|K=7wY@Y`VN%T7Oe0K*|AJgWDfDQ~Fsj@jK*=#0ztu%& z5%C?Y4ogR<#b#m@Dr9)0>@*02vz8TP0<0`%W0BZ4v(~nN#v&5hS-UK|gJ7PeQxpvQ zJeE-?Sx`D+lGq&6^toRy$Rx@vB)*+OH?}(Z%msWqxrUnT zRWa^;jCO}gR*7>hlV9;X2n|j+Su}D)Pf!w?rcU`hpO3dBvuA3aj0_<%f&&#Y?Y6f# zOzwd`SGKLttM!Z!V|l!4m@6mFen7J~$fvl1^y9>yDMgEsW^aS8Xky>#t)I*I@+`%< z%qXru5vrtnn25zoj%+^N`kQ0kf%J{zH(U=&o*-8w$h?~B@+$n&-E(4-P7zLj5|@zL zJg>Iqquv>eYWpp1g~#wB`=zj6l{j%Zyr}v$=W%kfKdLzK)V=$p`x;zOcZTzsX(gY0lQV=6g@LtqWvBTCHro1eGJQ>G(OQmY8g^=s>l!7h+E&}5(IZLt@voCEo|#JR-8_tT&X?j zPrnqpUqE!;+>`#Af7vuK15_X8$O61z+IG#9&PO*QT zHGl2)=&di+yprdGd6*e+2JZM>6~rmWS7=n+)__!_ZIp0Y)K*)&3gS6Kkk;#p+;*Bz zX%V}5e38S>&Xyf)r_z4l+0Ym%QBZEAF=S`Eked2oyMZeaNMn~w5TxCjQceK4SUBZw zR>B=re}YK^od|hG7s)#L#G@Bi{UL^t@;N4#Js+q3?91GgIi{rsu!mV-lU8lGaG>?( z&`6@ajdy%AQ#D;M5q=9z=w3miD%(|^+;As9+k+>OH@)oo@c7Z58_g}%JNq%mdpiehv5wC;K=XUTl#it*SccuReCgVv2-2xMQ29M1USrHw0is!q%2h)D$UN9?9Vd%aXIXOZ(& zNPW&Wao#L6$Lz9o{0VjYOZd1qDXFkpCeNufmb?C5EfFzjd|eGjSm=lG((@B=q+~6^ z{tHJgE7&w0m>x)1WSAB$z#er{vmqIVg5Dyv#3s)!S;L`)LB5SrUeie^hH_OtI=LUR(3kKM&G*3DJoz5jH}JX+H?)8&NoH@u#C8W-sThG_SjUj z-{o}9V5LUv7DDE{A-Aj}#Fo12i(ECKk{T~d?XDciSr7vK05Q*BH0!&}^S*>zfw-FE^#Hl&zMAQZM(=x?D@9 z8&FTx!-$&x+(62tExJgZ$(gGWgtFhO1kPX7UO&{5c%dZmqhMT3z^<)Q@N}PEE(MC) z7If(cwRLF$V4zH=>+c<>E-cQ}0fuyTkS!kxG#T29l1HpS9Wozib*SeKXBtY-$=|>q zhZQdH`B$K8#+fW|M_ zaZQff8#+7Sx5VZP&_=2PX+g@gjuOAbE5%%xLz6vnfLQH=<3lzCce`#1EzUlJsT?T#9BIq{>$-Uh(Y-Ncy@%Wv^{iic4NpT2Yj}v-tOUu;}RHPJH`D zWPA@$`#yQRESSQz7J1yiBso5bINfs&smXQLm8a9tRd~Iq1#5>`edmzsTxUahIvrh& z)A9po8`KibUUHxPN}IJW*Odreu}&K=^S^MeuPWGcf2Q(XQj~2}iBv*hRI|gTS1K?v zZCn;K73m&m!(lUywLY9Eg_h^TH{@9!vbADt=Hd2VzADQtN9S!oUz{?u6pNURH6mqa z!?A+{niE|7`)h;6dJ4ttATev(7AN;&-j9ea<~nHT$C!fs zG#-7L(Oe?9_)%@*S0ynm^TVd!1OL<7gr}v^%?ny~Iax&&&B6FXyuAk;EU&Ez08c=$ zzoVHE5-J!u0~wN=A5~j4ixNG`&33bB*N0yXam~wVA*E9-;)%w zWb51^Z7YNWz;RL0}d60x;w4Qs{0MBFE6bS#VgHp4(-VzhYv3bv(U zBMivfo0V!`Us*O!EQsDA1%YHBa8?6J!Eb&_fq}LGf$Sufe;v4hPmRmwUFlr6xucZk zWP)VwNcSiHLiiDrkXm`=SMHkpjAZZwY(h%-(_i_U^Uv+Jg1}a=3_NZ2Ggc7D1_B2I z0z3ES(p><)v{@l%R`>w`{iFogvk}r@Ft9#kX}TZBF1wz~FKJ?Eq^;zD zj611^ z)P@$(2i-oK@duppt3ri!a85JNRF>7twyKubVr00HSs`|fShX-MM$U8XH>ET}W`D(0 zj>({^Vm^!G3DC5<1{%}FY#ZVBP*iL#&r*(Ma!WBBBU_Aq;CXq{jwZFyXswhV^DUy> zPdo*s3=vNtkO-)lwP)Lx_bo9V+F7$CFWa;TxuPqsTB!0wu-t`Izf{eutz}9f&G#l} zg&E~C4o|C5vc-k0t-|^5E%Ne4niU2ERe2-cMc?__g;t}#vC&v15`p8weo@X!8VE!O zCk6su^0*le=Y(@-{kXe8f2ZI72NU-?r6oL#9 zD`T#yXDAEo!V&^mSHMx4&d6h|HXVmk+(_4s8j3~S$BJCMP%h@Qj<6F=7>1ZZC2T3g zh`~u4o#u`&5Z+SojMb0fbRT|HoPTih@%V!!hdh+;9>*We=CYkMN~u`NuL*M00~U{% z_mLEe7pk-p*$YO_$qiP|u&8-%+kd}(S6T&s!Qi+)K+OJjV7eI8GQNaYRmYS-%3rREXkdCJY`$6|WJ_qm z^QR=wE?EBh>lc?EXY=8_d1Iw(XGS;EIUT}g1%KGxkcZMYEBPZHH;SyLiiYafhsxf| z7i~9HH3leCm1nRl(C?X|B&+|U%j{;aT5T!PXe<~!zo#`G`Y&*?zd{WAuTV?;)K#!< zg+(QHf{vcOEhT)tH(r*_w~(}yi|OgdvMHSRxH7Lu$tfT-DHLQ3I3n^`6M|@EXb5J| zY29`%JSr{Y35+tB&&Wd+f?fkgBWJrY zDe)sPcqXB*Z)+?Rid5uTQ&4jxHl*^=Z4g~brO@4ciPoYLdisiolG8=&!qkW`83dh) z<+9?$HWz8VRrW2oB_)1!c%GG_Az#QS#glnXorG^Xegf?_G+#Au01azG{W6sEJInG0 z=#?Q41;9qhvlEr8r+e%SR*f zQ3!NC4V7y-EJbDB_+59%NxGh(%tA>~uf3}8&CNXt)%WIJfj(2Yhh1NVQq6!ozmz0h z>;j?p<-+#CryeZ#RBpcJ&1p=GCBRPS3TAqeFBaFFe4#Pf(>E0lq#yCwP?;>?dm6Jn zeZrS9(eGV-wb|Nv4}5O`bWeS~+YGa5bu&IZMU6x6wETGqQV$(6^YAz4ztLtwk?RVO z3iyR!R^=EFskdPT$Wa~}2P=qdxM(DN+giFj<=1pqYk6i?ys@<%ANMgmsUHy$M zV5T0A1u%E7pB`b&jeR7sX4ux+kjRh{GxvkYgaRqX=rOLeg80Z!BQYt4khqqcG9+i& z*`HLP!0J3Y1+E}z?!EzQWzVp3J4Pz&*xMhkumCcc=U7&8{5@)_d)J#?uGGA}t$xWr zce%|y{|{T~=+!81%&YHzws~`b&38ksaP;6-lthP4H<;b174(B?l#X8P^60pg5>#=X zmmr~YDg^nbwzdAOR5A#g6vqR^9pm|o-V73G5vR(XC3CiJ>7v&tysk?al&c(?zr!W zbz=RP!}oO4m=rg3d+1j5!QZ4f65~(I5WbmS6ig4orrpuK0?mu)^+ad2BIF z{s|ny7Z;Zu>xblhO4H~Rv8&x{d`bQf1C>^;Do$SOB26U-6^dM&{ifB5P$~*-)*E(f zflO|QaP0QED#e`LPs}w7xiW!}D`m4eQlWq&W7l}jjFjLC&JHMIKJr1U%WcN$kS}4C zdQ38>fmX?tDrJ_)ccr;S#R^4*-xWKq4*b3k0Ruigs>-sbdQi4M3ZD?_77V-l<=adN znxM*M)k@0wFf|PJ&+RM*$Yc>#2S94b3@;j}u)p{6Ar!7VT!2eM3kMP{ElW+P zGBxJ`+Eli+m_A{%+C)mP+vcTgKg#pxq9)mp{PBi4x3 zVv&jbIqBzPTcumWx)>^?3c13hsMTO9ne&o#k$2Jd4+08CbHsaBzp-c|e4}?`UR>O}j{w546Y(*%W&OW0jP~31 zn>0)Go9zvu_tAEKFMp_4Jg8deT?k2^rEc(e)b(b%ihXjYf?sIXkb5iHH;&8YRhwE{ zB?zp@XI_AO7qJuA>s=te_OBnnEzUNIIUlVh;hqA@aT*ew!KFes8l$HX{pJ8m&d{lAtC z{u=$}b>KRFH`*C`zag|OM`Z^c@XN09m6+{z={onG8j+XRe^3ufZ9X1=?R4=H#>iy5Ya!L|3dlwwr z=jEb*9b=osAxjZAclYL?=HNUdqqj<_5L7VORSGes%E4kd8S1R7|A@1Vt}3=dDrM`b zEOv2(1AMs|Nd;})HU0a}>9&R6aBJoeP=TyeA*&f(E_*HSRU*%Ga%Ouj*qf;))lp@~m1|!LF-V z3AZh;J&xznA?p(P$nY1dQf7i8&+}@s>Z{l@cCYr7+|7B71zfRxO~&VyBVuK5 z>->9HVj5zOJ!|W06)V}0XwYa=@cZ0Dhhn2jKgh_Ktd5F16q|}Y6AVPFRY0Cz(%gG+ zVuNp@zhM8DfL;=P|HfHNYHXayGG9DzX_>1%6! zdOW5fn)|!#a&b{5$Rr1IHW-XGaq-I_f_e|(a0!-UQ%3fHo5QPzT~ss3cX|wBDzvnJ%him+*{Ba7Aq8q zSM(irb?lwNE_Y_^d70UgwKelp=F2^J?Ad?n5xWufXHUoWv#|I>x~zSftF!iI=?)RT z?BCoUJbl(#v8#UBnLoEypwcJ!qOI5gQ(Xl9|-KdFZx>rI~o1s@iBr~m`W5- za9U7WTD&~o7bi>q50$1ohG!)~*fg|$e%AIs8(#?-&tWgkfAcWor*Z#MOs%i>&rIj5 z*{=2bgKC%q=Wi4Jej4UvHx295jsu`y0=Tx~D#hhepYG)Ca&XLY?Rl21I=%&| zvi=J*;Y`6G<}BAP+?>`+gW8KM-g_y|y+*4M`_`jtGxQYEYkW4IPBrL1fq!(&KhCo6 zqdOX^hLqLHR98yqo%;V_mXRIQc}%(=n;mrKJU=RX4vooXFPa|zyg$#ghUEq?n70G+6kt)RB_OF|Z7G=B=b% zaOv&Kt@qw4UcY$1C?6O1%C5QX3?*xuS9@DmTgMDhJ19uflb#uU9Y2&?(p-_XjksVl zZXw=*%>eF~u^96Yl8(F5@n6$DI)B8rXFv3|JjPgF^C?BNzx*vbY!V4*k0e}c6DKMD z{?KuX3j5nR>Ry&BkoJIMb9q+QVg;YcyUUPY`A)-7(m6V%~pNWNPk_s>Odi*RoK)P)2) zoeYX@l^Tw+KuRePN%4gS_G!!o&_>f7=bT(EV6HCx5*icGl~2D4szlD7x>5rtj6)x6 z*6LKgu7;FsZ{#(c?XyKL@sd=h6 z8dsXJxmX%(0L639g_(|wN<>kKkd7tCLbO(O8`E^Xp}G4|J-Oy;!n;0>NU-LEN({#d z-9GuGr>=P-g89qvDa)N;@F@}y@O46IrfYkUdQbTA4OY9vEX5ZZF0I9~Ac|q9sXx^D z-m=R%>@@Q>%IG&Zc&uEFE_k#rMLokr#w!yN-X-t>kFR$m$Nh1AXU@hYcU->sbJdYmsj1ZfK!ckc9H*rMI^JLV+5jfAt^|V0gp}}N8`j8i#NSf*j}l{a zSCU1E3BM%8)jU_CY^diQaf!bqB$AUq^TsB=NKE;S=!;vQ5e9ZE??(zNHQP!!Oh1QP zVAEVqX#lwhjGN2hmF!7N-Llql+Mgyf&178W3X+1(27{hvFut}2$br`TH<)UDlY|!6 zZ-m!qj;7UGh2sot*OZb;_ zEdXD}e=-?a9>WBz8rQK2IE5NbAgItd1>xx$5!bC9pH&+2&tWh7GK-Y?m+cO^9Zyyf zQ9*Ia{0X$^=8T-UA31RlMO2aTbi3W*u}1rW|C9XFt69UDBrvOBFA*uEh$$5!h3`PvIZ&XaGX=e61Fjm^hrlufJuJFFSx7nG(sG^>{UYvS#`=*5L=U{U zRfxu9+~9U(1p3ndT8yBy}u9G%r48J77Yr@ALzp|MB%s;ZY)r~1xUV^EiZ zylXSVk7nj)ZxdU1C~b}?H!@CISZLC8*A7&V_;ngIR(cyk;w_5wp_Q zA6yqQ;_VeWs5T!h*z$S;Y|x#x!fU_Z5DO>%h9CbAS=MAqG`JUOt63i_yU0+g0nEA0 z)-ejm8W(|+SV^3Qh>$FyT7Z~8=ef4GoDDH7a3<4JO%T1K$lKfHs#Y(9R>Ee}e4au!3$XEE2PyF|SdBaf zH0!FlRVA+j)eAt5eiMX^LvbH$n$a_e@@Mx@n8)A@90q2}QOdX&yr}R!sXS;Ys?jhA z%GaWdd@MZaXk-&mDdnPx+sy|OB2DJWXP2>9X`rRk8X#Pimb3J_8pu>(w9yx0aI`tZ zXeBK=(mG2NeNmmLC{`MkMbc(+JTAUAI`gF-RiLI&3REKwk1t4aRHRLrr~DO%e}O>! zipT$oFnn-&bO2C2|4`JjGFdtGeagRWs-o^CU307c1LTbk^p~RCZ}2~w^;yK&^|~#8 zawvu^vN zE{oGTne}wm^}D^dn?=;{VCSG+nlsy^WH4DleIc)UnZP$ z>p=Z(WWGc;GCVROk&KMI*BX|{Vtvi516HY3bl|VYdQVHN(gPu#tOJL!9UaT`qO;ra z;*Z@6HL<5+|NKe$&!zh48pQDWS0#I*7;N7Tj(nbEd3f5LTj`=YjdcJk7I&aJnz60j zv<=Tb&o_5ASoK{o`-Ln1W?OVXV^eu#Nk%E++{Vk8$F7p{`%W!rQ7yEM zRA+8n-|!UILcjUK!WI=gE?H(?)Lr>%iWCIOCszqTEhlcIfM_73zqNUf9q&C_4yoVk zz}Sz*#y?qL=F=J+wjdX}0LIvVIQ|P)flJ`7+mfkYp@%+hq`vu~7I}ZC8DuVmOeS>a zw7dJ;fXUq;OB_Fymv>{QG_epRli>?spD&i4zq#7BgT%Ntg<>%=L+;z;q2l>IZ-38h z8&WcimYF6Evk1CcjDy!{?n~Xk=%f~%15>|O1o{7{9pAj?MeO?%No((Ndxg)}_@#z2 z2rUz#l!)FNfDE$=fcnhfzk6Q}@Pq8E4}dxQvW%vM+K(IL3{MI^l*B=J&=FByzc- zu>OM_;J&QveZZX4S;h?7oPP;}>I6XR9E9ZEedPd=0a zLV&?i5Hc@Grp7xcB+xx&-Of)^$xZIt(~`rxX3I9}x!K!Z7!q~2+cIj-g5ZS&n0)A0 zl#!oPvkg`U;{5MBtqPYki6B=e$}A^#U&^vS_&vWNJLN`$n2P*)6YNU{TOM^7 zK_3GDQT3cv$W6_xx%30@C{tW>T0>)S%fnpsjFdOKgN>G|#66%PNt}(Z8GXAJRnHc) zNIVOjp@h5G=P)morQqocre3$qw0O4kj^a2E1^x$m(^T&|qIYI)yL9IZOWGub+28xN zFzxA);^j=ks9c4eF&-a+{rayPWzEvZVXONelf!-yW`=$r+p4$4TDJm=m-lVYHo(7r z9QwuCcadJQeE~?%x$HV!Jtn)1{YGm*e?W-z_J3$ZtR}_klY#@Et&DR@Fcc^hn941K z#jj>8d6+q|Xp>(#T!5IBq}pr9?@1G54Qw?IS<1^;#oAgs@Y~%E;6sg+YV)WRwQ~fc zTlD-$(j;0CQy3znZ5PkndCsDcy+AgG66wmQvf_L44BsIapQqb~Cwy;Vo`9>{lkhrY zU50!FgAwSqeugaF{!CIzw!(XJMe8WDu(cY2VLSP;e%AIj$pbz0Bc*Z@Rg+KHN~UB} zFOouyXxIF<(OebvSSu5A#Y1-LA@nfPd!O%C&BJC!1#r3FrUEhf=Wr%2q2lloGt(lA z*9nGBq0d&b+)4T&>0LzrX@75j!UH;8IQjc*HmEnRGyyv6>~E7bsVU(mUqaDC_ze2? zeOv5-)>xlyXGv8WpPAh8iR7d&d$BVmuFo>sHtZMt;s2nW3b|2Iwkr=EIm>rXTYRa*|gh`kr((8IqJjQq0&cJ7ui3&uG4&(0^7+& z`B=(#MXep@(-D4*$Xq>aVY$ndh4C{cu-|a&12*wBz15g<6Wj=iopYD%&JFCLJYulE zBD==4>Dd|Z+Cj1sd)$^ng*9v8?QXD}v89Cb?lu~CqBVTUE*`&hAlxN z7{zIVWj39G+5L{~!yn@sTIq5k+jzGaI}f}`{9k{Fy2DK?4H7#Q?HFj(w<%DatsoZp!*#h!MqPk{Hfx4qj-XlXxC9 zYqIqj4jTY+V^?gH$(TykQtOa$712YVLSkf0t;3G?4wep`4?HWq4C~$_>(xi}`n=b< zbyL>E`e3Miq<<2}whSU2U z34kUsu`F-MLKdzuJY7BV)-&p6rrEY4(6KsY#R? z_Ap8<%97NM4oB3l18L-tM-gRINu#p~#XENaK;+2yAIKm_!pt>vlF!wN>={gG+^W(_ z0Hl#Y4tW$%B$=LO=uzZIC|?;HcW?}icUD?JkVX!96i`H&H2Rfre)XS?rE5}_EdC}X z;@yZwI#*xH{6TWCdUt_{|NkX?*tFg;RLA{M;=7CK_p0nXbaq z15ii%Po1B~^?;t66Ns4wmZE&i{NJaR;Tdyg-3K|3jKf}aPFu?-&{!$+uB|quvOp}o zgtx)fD!k|mh*&RyPQFg(bLKh^+nOyzb61hGSoypbIaKR{33W@veFxuLLp|X-Ic_2W zK4ZIOc|-qS$p~@70-_#(5O4pui87%aqncVY_u%@+w<5+@_Jl*azyA2XaPW^G2uJ?- zq43jJj)Wg|4|b1*!?Z8UH|cbj1~=HcPIHFZ>VI#RPD+~iT5GJhy^zdsh*xO7|Xtu5y^S1b%U zD6A8p2QI;OKl3QnC3ILEL^{DG(0Z4!T*^lsDDZ3zXL7ow%46!Y2{9K&OoqAo7cm`1 z%!d&dVZ_ZXO85U&v5(wp27pVUdSv)7R1umfjC3iKsKR|z&n+fg-IL||o;^3NnBG2; z+O-W7D2QrF`OQIl8of8@U&BBN!`~Q}uvk z@j!bKy-(=h$3PQ9f)Nkn9}vGJl^c^D^5{gZhxQ(NLiFEYV2+_IL=0+lp*CCEXB%v` zOf=C%6HRmqMv54-5Rp`FOnQ+&ms@n&N~hE5bUK|*rg*x z_UGDEdNmrQU46^lr9CL^8fs9XDycYQd*|Jrm!;fDKPB0L1D* z7d`YbzzAbZFb%Vgx&FNlieVFd)lfNSlj3R8G@0a=*{mni%*pItmy>-!Svz<~9W8_j z8$JSrh>#&ifeI};^cXN=!h#Ju&bZLw%jJ^4GW{Ae1nECG!Gz{lnsPv5{`jwVF zqU+bk`Ah%ykx!qz0V3B1GzLY6r24m?eG92;(H9pVq?ms%Q5NB!NdbZzr>O`kV;^Wz z42lxjb}4_cYjkB6e)uB)Tv{dh)RRR;T%%(Z-GHJ*hTQKb-XbK!Rr}jd#$g1#u{vad z58|nd)wNVBRT|DtccmWu3AH$kC>nzzV`(5~7bOq=WG_NcEK;SR#o$)T)g)4)q?HM0 zFAAO$oFu{HyC*q(DZ^s}8iOKZ2}yPPPqP7WYcZ$?GYNn&1Psb^+@L9tYH4;f?;W}3 zo;1(d%&|)EB!LJH)#9RcxUm`5_P95jbbcw&IKz53TbQc*G4HrT{gV(4<6U%Cix;fx zHt0x7QfY42VLl26!ZP0E6IOOHacZ4Pa z*JxI>&^yLyzbEa_Y5RBR3*(rJU>feV7r}TZlBt?~)$*r|u?R71cAu)OC3eh5%eoA$DzPLfxeFBBVdt0d;+=hv$$dcFxK*DX_h=CmR<%_` zh$U)5$f##GGmBuUQ&7rf1r=I3vsU8L`2f(5B;?DvnW+sr z7syJeag9X8-d)9$*FZq{Ht!-9*zQo9OA(MG|1e;YPF0;Gq zE@kP@A!Z#+RdrsGy;~i*p-=G)Xq{Jlc-)ReDK;a{nMDD1F+y~SF|M=8U^B^Pk*vV3 z)sD+5s@CQN;CsA0ih6Vg1Jvx<3lNW?SejXOmC)Ml&CX7mRVc8TviVi?+F2qkjfKEn zmP@y@C6~EY#;h_0wp_lgG*VU#_{a*Jkj{&aT5Vx&VY&3sA`%$KtTF|Gh?mdzq%71I zjFz2Ev&?0pm}z$pn_ik7(}mXS@?mFBHJ!T|T@41c`+NWHHYpGm8s3~}o^2TRs5g?&g!@tEjY+}3_HdQet9#~#=b*64g1k+W! zr7f}Bg1BX0rY~uOdxmk%E^KzF>{R`l+f_Ia>l2R|&jj)jC0U(zk$y^}&JlAyU|3;d zed5Y^CTKFRU4?~MpSX}uqdn^K6FmBO3Om^a($uPrK81Ho{THp&Xg0bE2V#BVG2@v) zKB7&{I&Hi=4(ckb#QMaQeD6l_3zL~CjLhywA=W3ZjAsJ*h?1;Mdy#%xiLS#!vCpLN ziO&dh78{wIUbJ^0~MgY&eXZl5)YV zebr&+deE_Nb=_V!5|9I3W)pH=o0Tc7-(BlLwaA6Dd@imA8)B1FQZBgFtBS2PlfA4! z=AClodb~FIDTzJ;T++Uuz1}R3B~UJa_TXmZK4hj-G60r&Jv&$j&C6x79bTxLmPEShPJ~ z^XjA{+s|Hql_=E}Baaz)SpQ7OW0>C*CB?q{t%1kN>W!s|(x84RsFpbYna+mCF)eVM ztSHaY{8st$5+6$Q%(W!nW>5sBloEy9#WzC-NsHd@xLziIz6>u4nH9BuW|Crr$N)bul9AThcZc7^`npU~%i+37f2|i58tf^RcK@c>Durr0us8{;S7$@dr;X z&9)Zf$GTPpb9GvO*R6bcUcBMd|9{3xcGN8dr2n0S+W!>YL{)?}gdTe)4?kv2H_;P*Wu-D{s6Wy`^bqOIChP$JaPDqzb|08j)JZ14~T$ zI{<`v$E-(OM@33@x;6J`{)jW?x;eoyoIc`NmXgfPi)Qothiz<@dmwQuQ6jTA2)a&Y zcBwNt)7@r20z?O+@8eWS!AXeetij!!HV6Tbz<~j;-J-zeN)lV*o@(Qcp zT5P>hOk9`-1}=zG4OvQ;I-n#z)!`<|5Bps2*#bazRM0f2JEL5`z=4s>%P~H6bPg!G zy_x5&Qpz2Z%`69~6XaFI#9sK#&yX_MU@+MeWi778gjClH6;u#Z1pvCF9xQ&uGv{cN z?OvYOB3O%otG_`K$J;<|4u`>J@wO5r49MWj_}(6qF93J=LHx-I^EvXfmS(k>gx91= z;Z~5qf_@m5@I7>8NIo{(X1RMFE6BEOG@4?A5o>`uEo8%s)*2MEH3yJ25CjA1*@D#m zHY#(pJV-e~t120f4M{Zr|zU%?8UzR_kA_k1rvaoc$|8nh^ z!C<98L<#f)2`t?P8N|}KqM^g2?8&HF*4#k+gSTC09=0zx_w_VeM3^MoB(*^>>cvsf zG#2oV3QwlrC}Y}79F&BVT#jEVNWFkmUS)Ct?5G(y#hGf8b^Z5>-%3HevR+OxAz;_qE8l`xAL^df*`-iLW1G2$I>RGTOCG(RcYHM z&FKw!2_AZ~kC@@?1M(Ii9=&q}SXX^21c4+tu5`uA0aH=ELK7oyQ>N2Fvzp`DU5x1Y zOoWmmJXUzi=+lj^9`N$w@zBt;lLagP>J}D`!?G(xr65L@QdG#^Nq=ALu5ew68=7~S z>tMpdO(%J3^p(|kHV`ZA4u@#cBC8#pa1hiORtG0kTtX4NgQ~r5%vX@#occ7XHpz`*PH0&1~D_~WG!b|Fo`I*lvtiNd@q&92z zmfk1dXliv)+yi`3PUgf>7aHQNTn~VdkCkR|sJ4Gqf3f zf)>$mqGe;kEi2}j5wp9RO73tHtG1#anM12N0H{yM@2oqqJMUVD3@oJ>)n5n#iWo2E z91DPXk>sa$o83-f@{wAvaS4Ir@WC2G4ZvK?Rux*Hkr~dJQeb*M55>Icz_;Z?A zy|*NUnhXQQta8oV-k~5Mz=<&_>zE3#>YyAkJ}HVKge6Gcer>FZnIkW0I6$nilU7*Q zdW>dN5Nd{qF?4OD=$RTQ!bSzxF~kPcyk(H(W#GXf7%yTerhlJtV13{~YY|@{AHcBk z4Hh*!Wu`SHr^akZL)3+8-s;l0f=e^d#33cX2SS~#7K!BvBX$adEsI>f#37@<_DL3T zt;0@4V`(_X7sLd53410a^VKGd1B4UTiLj$#M{K2rILS&#LAe?yOTw>As2X>PMgBX# zly1V4oa<7J;4@0E$go;Y_BYKzLI!F2-V&v0#Vg?!V#yK?1`5|3V*&gRu{sso4Y^3wAlW zgACP7vQ0>7%*VPRajDJ1z%cr?#MVcb70O`>qs;@ zWlW?(j*8;ZNFcdq56I(y3;PSs60;cf%LWl38)Fags`waRVk(VD!25gev0%P**Iy*s zrX62TS*Cr$&3``rhIZj7%7j;kTCn#RH^wsm4y80$A4! zsQjNz7ZS-MjUEz?InG!@IwwBTpSQK&+I&|ra!~w?wa-!UT26>bTVL7*kj>Mo)Jiv8Na!n`e&v<5$!FV#11fX$f+_9r&XKpL_TUu>9km^U&MBYdWC$;}u?Wo}I;cfe>=l}|sb5i1>GAxW&^O5J3LL9u*s?n8q3Y#ZTP?0(ns8M zSBb%Cax;DpoI?y9CK!jUzDs8>DX}`bMtCAu}I( zH`fjJZUPKLGI7SEsVEyqfnffa-|Bv+Zep>OKnJi%dol+lX9{CPh@LXH)H$cVV?O$P z7aE>)S_06ySt{~o%@@5tJs0R1@|`f*kC&SuxG1wmtbD0;S*QPtPK_XMS0^p(V00dj zD?7kRV&JSO+Zf%2mum^ki%g z@P@$GHdqSP~^DC06A(Jg=VqX(+ZJXB)p_X?2~K|_(^q@$HezR zRAnI`=>~ZMasq&WMVJR$MRv-M9m?+4dF5nfTojTCU00%^?eIH=%b z|1eDzX-rbrnYtzR4hlV5_%jFzGR zXt#&&dhtq541|%GNoa>OuWark141jJJRsqCHH3~fSr7>Bf2wK50paAI)CqewpsSIq zd0>}}`iFkd2IEdz0Vs{sdTeeV7M^VLrynyBw%|a3$vM<=wmxT-N>L~DK^&CoF#=Ce zY}ls|Bt4G(84ih`xHShV?gst>OGXixP_{0|DH(F2?A*nbl6Ll)t$$ZAs}3Xc_V~5> zdII3>=H3m6XvM^25b&_V0*S#5nvnf8$Ty~m?wh%ovlh$bwl;{up5u6|@=|zMNUssg zs!7}n6lBsPQTeb&o+m)1;ITt(u;v7LU*=g#GW16a_wl>{HAUN;OrLXTMD`0h4qnK{ zInc@a4W<~l!sIX0dI3C@iBv)^emfs4{i!-J5)gcX(y9gp0J?wSiN&|GIp_C)lmj$~TNSbfK$D*@SFb?|SGCKCgz zHDfFl3_ltNolo7piG5N9oU`;lGqtp9h(gL~-V#J+rTKVnf^{S|frt1BUTYbi6+`a? zwC(CgrveDR(xZ#qgY?r~oPWkT5v=dT?!Z-Vm<%kn5x2%$oiewwRpfEFb=|WgAbsw` zPTYLQN^k)gK}d7e{g3i{%ZZQX^ZbDm!l#pzMChyw5S0^kwt{(JJUWAw8SGF?f`jQCr|Dsc^GTvEwQiZLrGHLeTz^gypzvXgVAli-14c;dbRRf3h*c;q2@x$7^?kabbsOfyXo=zYpkeA*J9vRJ zg#9wHqH0T{QnWZv=`%)iN>FJdq=zX^u|Ky1-P3D!?k5sOq!_7 zVo=tC!9&i;rGtwx(TGCNsWm^`I9@ZI9zOYx1DZ`U_XPQLdeC$9DXm_9KkA{G)mq*S zlE=mq0&AH;#X3P6t+AE^$b^wIR53?R-hJgaU}X?G{?fZb@?$2I&->9XPg)l+;EG(Y ztSldCL%6d%z+8k>jb#d^k3o#y)DCcSi*F!%odN)N*a+O!eO!-9T`281f7?6d$Lhue z&Fq-;p2XvXX39h!rU2~j&>OxqP290zHY{v^~{rU@)OvCU5!wlc)A|>TD47Rg*@=kcU zbkgXyW;XPP#gkqal^jtB26=xMj?**O(tTc2r%%)*(I$Te;;%2#$2Ge@JenAdytF4w59e9^chT5;rDGp!+~&%}7a` z2HZB5+{FndJna{d7-`^ux#5th$7xW`=uj0BS8JLpW<%&kc=)|VE`T5J>7=;LVe}m! zYJgtpIj}o^danB9^t@xCMQ+@gs;^cC=?zvx%m~00Z`(A@E=gc@G@5pACJx-P5A;|? zA}cm$_J&`S{oChp!urKs7Bp)QiAj}=6rR>{%DqVf`}t>00M^41{l_9q4Op3E|?%^@(-S}QS#yK2u7nB6iAqiy*C!dW(0 zIhWV?+eLGDp}f*c+oGEmC*7k!nJe2lp)jNr3<9O&u2M>t4Gb{&9=WTM179!EEi<;J zqFcR!Vbu?d%eoeekgcm7Dkqw>!tqVN`l=c}4Ikg$t8*0w6x;xF?y{k#A#tfm&dDBY z-jjmi_|pg69%y^8+5>O9YX5Y1%TJx!U?*$tLAS1R8yv9rD$wG-0ILK#dDGpuGzM4D zv-Ul%7fF=(tOv8M79VYCa*Gp-18BQid{#wi09CS@52)S0?dihJPbC0I?#m~#DKc9i z*x5OYlEpPO*3nbLC{S@l%}JZsApC^=I}-$^W71S_bEegl*q1`vF(e`9b%t^oWZ^RG33*z~1MzzTCx9%Y;B6i{-2ddX%)0*{h;;xw%)?$nGLBY zs7B&Q&pJowU|Ju&5T_!m`{1^}kJ_HJt$W7hHgLOa7hh`zH^gOb8{ltc+h!cApRET4 zw+^n*T92(*J*5P!#*%jvxPXmh5^fKJojG^Cw1%U{UN}WZ|Aln%N;JqoC(*U+Uy%&5 z4@_xFg^{t4DMr=pd@MP*XQ2{_!&uj7+q5YOnp|Svrb+=E9;atp5aqheg=>9aZM(N8ev>{RL!qvQhiG7grbs;(ExZ(|i8TYei$$Gffa)08T`b0_ z+fVOy2eRvF8}~^%-U0Tc%w!tZ7UV$y!)_{K9y=QB2oO6pcdxLomKpd;Zb0NNBlWLT zH;#6eCDM4g<8YXFiF1tOl&kunifp)%2JYPF9<|%A8V@7S8Q$HuSR#TzEB^L68fvzr z&WR9VUQ^*>cUpYb_ewcGie>v}(?7D)gShy^TZ}zvZ%1$^83M}+U|BDkac(9=A#m#H z>RPDd0DT__4P0jgqe_U0G!#OBfN_OD} z-=uSqGOar;@C;w{$#Rv^sa2NhX@I=9H6UFSwH)6*cx(nz@>-STZX<`@W zx@+r6%lc{;(3AF|x2^rmy8`Uj{_!Q{J(gy_mWWLYu3@^wg`Pjz0G-Y{X4E!=@iy&2 z2Y^r3E||!_GzUvyM`@L3#E`qf51~C1`w1FR_E@hSaGXHcU8*o#F=#2)ELedj%=&Nx zy6dBYv_$;;g0@E9LMTUjj z8rIA5Ns%r{@I~2cKL#}$i{OuY8j7l7H+O0qxBju`<$mp-X+?Da?bkH7?A^4LJyZal zrl`(S#IUtLYnPl7@-*cVaD!daJS#A(JGX6%8(lzD{+x~pXNq4T5|UvJix}b{gKNuU z_O|j0wa0|xr;SU!2`aOzJGaOm0e|RHwY9A+U8t~rMDW!{OIj%kkxU8c5wR>Mx4Fl$ z?9XNwJCTn=pOU6eVUx3SB#3nLvE<;M4NoYEIH$fPVwDqAo-fcMsMJc4T~4B(O-&If zq@k>u^2>m8G1*pfdgY>Lql&<)s!NrUKBY^_x!DAim*tq`EW(|psaB|d35^7 zW#|I&Nb*cxlY`>rlbrT%ZvRjz!QhxTL6J&1H1Tki3j@TwWXrbQwqnr!qVMVRZrfd( zY_wwI9pUw4q^D*i7bP51&|7ih{AL+1n*#_Tb4t`#64qe++O6-du-F!ZXpsEzv_iKCdsQX4Bh6e;1 z$)U9@feK!_ZOsx};MdCbN`HW>60L?@>A^$l`Mk;a4ykwg-+KaN-VQ$Bl72nUXM8h5q z{Pl(!2=It%Svtt6_~wd+-B{}7zAejjbW&XRBTSi8+NTuyKY%$6vVru~sJTq6C{H8; zG39VX+5uq+bWD4NNxVo|2oh6|RMOHyE_Hk9IQlUL@J>w5Dpm+cmbb<(LEiGHFs3Yr z-#b{z^SZ}oPhu@05~avO^^z%G&8G-jNz|Klc}%ht#&@z6<6~7p(+L<#Y?|km(_w3L z7-@xovKHIv1Xych1#3)bY!-$Rp>1@nD&g1=dCHzvznYvY$aywKK|D%MmT{!PI%~zs zWu!JGklD%OXC=lG+y7ve`PAAhIDk!qF~gWGY!|c)C6@ZT-T%p zZ=UAC__OW|pI-zW=+Q2M8t4ZwP6Qw@gBk+F8`c?fe@0P9iB{nZob3L^NUe-cD9l+K z^YJJ8@i&wE*2Ovg!w^`%&;*6*t}2MdEM?OFZFDx&c1kG|Vbu@=9sP_^(CIm|JZ9~K z8fFaX8O=3dAKg2}$QPLS8U*xa!NRaoFH9P0&Tiif^);C>-QX(LeXi-0wrKYq#+VzA zN+re^?cyMDw^?)NnJ5C=IB#7Ll2V~5J2~xKSFqK zyaFlaD=gR*3M*hCZCJvPhRfCq(q~1R!!pGcc{gom+P(@RsO{KA_7V>7HsaX1GhHyn z+b{%HuOCqFkzo6aJ>0_fiw3f`xH63h{T$WU>tysQGGq!XttiSL?&~36A!`c9Iu@j@ z0$3MH!c`XpOeDS%$CUcmF)t0JvYuW&3uEMnL+jspWr?%YCT3R2$Xl4L(FoqqB>sV|U(PNYgUf{Blv(%TA zs~YvZZE5+LPJ8mwm#NkS({;aXraRpdi^+Z)Msf=VM=@cAnA(*nbH_QP2Vb=Er5~K| zADnW0H=7B;q|NlQM~=KRW@NEj-?j6SZKW|(esYxCOism9J&jifC|szWk%qntV#FIMn8U4r>UNTnHRH^y)NPQX@U6>`6e zPWu?_iQ-E|U|q>bQ$TE88Pb7vh#$3}-rf|Js52Q!H5G9gK_vKr$r;uzR4Z`R%zP6+ zXgVqLx=_QM4DLrZc+vxYPUSD?uuL~w1pxJRELN{36q4GN9<%H%ygA{a<9wM5alsI5 z-!{Y846T;$ee1mJKmWOm@^*wGWoA=kxRf)!;?S^mLWlfz;TrvU1K&%`cB^ zd6N}b&q}dz_Z)ZZm()H_|HG%-pOqebrz`JV0d+9Ro(B(8^|lAg z8pC)<_RtU_ruXi@SNLJ4jYuFMQ9Zuyo9~Zv4i8-&9J2&Q0sgR5;G7)q8^x4$odaKz zoD_mMLJh^T!DbFOV1}wZeD<`3zc75jI#WG(Fg*O zJO!C(^tSieN znAoOGK(r?Hy;k4k3XOjKNpDpgD^%OZzqT)Dvoi<3!i#R$uy2w5B)Dq0lI7(-Dg_Vh zpK+hqDO|m?6$d2HWo!$W8MVINaA~W(2U2YRsN43GY<`nbrvFldhwp-+au+N9k{>-< z*6VB@`bRmv$gK;f2}(;6xfwlaI5_*yN6~OGnEmWicXu5!d!4sl%%5%?)MF!(-9M*I ze{{%}EJ^#~)vq#18^T0^D}pGOGF@zUIob_sM-A-;A2it2omAJk?u`!`7+5E!CHy(t zu3{qVEGK*oWQk(r!`Z*Q`SXNevg|J2kujrXodg?xX=*l4AD2P72?4>ij^0pni=6d$ z1D&0u2nd7D^bp{I46rqCsw}mFOXyUMl%t73y_ufALIz0O2LN>u!9V(U_U;|TzcC-O z;9xUwX>H;IUX*|{KAaos8E6+krVq79d3DE*{LfCLg^idMTI{xK3dwvt^g-9A72W{` zSb7Jdf78$n{0LXB8@(%Q_d;&FLAB&wgcbUOiE-m$emMUrU4URAuL7gn2okO*sKC_w zpDnEe+=vt&k{M5%HT(r&lSUiEeNgmPpF~>*p-0)E7ji2}MnjgYTlPV}mvaxv{#X^u9}po@gyCqD_ndGPk3G<`Ibf*z`~o?67zDt&E%A>d!_Tglh%@%U{>gI;qMtjjNilDjOm>@| zSEtyefj9t=)wLpKedb+3(vxUds4nn zht~!h%BUf;%y`%4147Vurqgxt$Wm}@=wDjY)P7upUZ@X*D6{t;QuHe)yww3|^)s;e zFH0v(;LqCejSzfaWz49mKw=_q^~wv3zzEIOC}Tndv`>B4c2TnLWa*I#11ZLZIWb-n z)359fPoMSF6&V_ZTc3?io!^0W?HN;dB~8^PTEhc^oog7hnTb@U?O}o{2C#5_y_KPA zSTkfRJH-l`%an{^MK5$!&1tq4zHw=MK5K%t{q`Nb-m=W!KCnt&ZNc^mtN@N=3V*1- z52wh9d6T0&-ZS~lUCuPqkxWbVJP~!IFB`Lc}PY&VA zEx0sMvK-+<#J&8{ABYfDYc7BSSYM;kH24CW?p!bkZckWB6QX>JOxj;UfU2nv`aH4P z8@_cTrpzhwSTkdvaU8o7`yX|Z)aPbZaf2gg&u5KSB7GX+B`-L_iift+Uv;t_%~xZBFdSnu@P{8MPw6!!f)YVi~l$CIQ|NK7aHC2%%QQ$e2p(&EUF%*Fz5Kv!@&i@zo z+(chbS4Ug8ITUE9tEs9eFZC7u$FN($W-%Fb8kIsO5swI*D4j}HP(Or1<`Dq{vzPhG zfU<)gUby>i6)#)-JMDqH?zriO>#n+N$e;oLJL9AijvDFfsU^W;{bbC#ecc3d~pS(BXtv6nJ z;i)Gcd+4q^Zo1*BD~4P&Xu$u@IO&9=jySAeuWs!G4mzO8etR_9ZKrzMY*uHZ8ooER z68P&Mm=f!(wMwDomRW3(OvaLhh#cHe_wZ3ds|C$Fi>AtUF9z?@p(O8rf3Tmqd}`t zDx?yjfQKp|;}5GJRNY$G?jl`Fcw4zsCwfj&2v5#SBV?md^L6@(NMtSZKQZYP|Inz@ zpO|)4+P9pd;Rw$kma;G7joCd$pl<8G!X`QCeLOljf+5!#A%X1*L>A0W7Iv;{5XZ#x}aigUF89Pm{UU zqNhmo)h~$Th0L09HjUbP^|CC!{??1)Pa9y{CGwi=vOPV8NK-Xn%dKG28`I8?S_208 zEK?9?rQfYQLPgm{zQx)W&2}?Pt($6l!h2!y%$&Jc*khuRn%Ke8NKc?ZuweNQ@w10HB z4Vf2A^Ko<7(S%-82&BlYDhBPNY&cSZ{|}1vAIO?H5=39zt#HNcdyIbdx{H*p z;>JYEWDtoXQfNH!Q?lf~RQm5Ng01t#0unwTIn(aocN<^tgByuMF~6x<+s_*-A*J1P z(I@UV_F076E!^e=+jDz&cQRw@fSzNph9Q%1F5!Qr5m{Y2XV&kzn4qjiuaf!84J-dj zXNKPMu-GTVBd=nzCn=I?{NZ`R_RY6$lgwQB>t98tjlT5>SttK$0FYGHrJUFt2>BG0`{Blu@tO@L&50W zGK!k2{+s9_2gcBVp*%iW&;`d_7ZS}WltSTOpa~eO!MK#Qhnwg_GQ=c3SH-b4e6*{d zqCCP8ro6||%MrzWJt)9StF&@vr)gp%=#+qMI=Ne9Pcbyu_HnzB%@fqo8X$sa=R``otc8fu$l*`1E&?MP? zet#PY=nd*D*l zG;m>4EqKTW6Ficl08W+BAU_~q=xm z8v^2F2%SW=KL^cdKJGr5t~GbCzH_VnQ&@_l&#B`suxK9EgR9H1PILa%Te3SkYC2~~ z8b&!JIj7;ZQnp>S9l3kxD!7uo8V2txs$eiZu#RqCFHE+tC3w3I?5;1mUA>|VzREUz zis6E`eGGhu)5oa(D?OejqKdBWaSm;c6knyf`F-$Rr>45Huk!Jq!@BK{(E0=pQDoNO z03!YhTrH8PE#O;SmIVku9>uqZ-r0`h~tlcyjsW!Ewi_9#9oO~8P zsGpgxUSXEG=fZVQa4O|?Efdh-XILN_oVm~T!NIAY>ta`5QK!;tB^B-c?3P00G!)={ z{qQ_`MSI_a9lc-6Ls&ypu`8)yKg|8KEkn44bU0XNZlPutr)5+f{r81c*<4JcUP zijm*!@c#7(nqcUcmqI&0Zz^ zv;Cg|QSh5|hMJ)rJ%ke6vUm{0-*aBKi|UD;7~qv#ABx#dT-lwG7@$)pPHvT)lWTQr zO%NC94vsldQ&($S(H$&fkrC6yMA5XXNj=f&PVZp3>$2RqI#IbhsjghTb~JUJ{X>ww z%<}JP66h|(>RQ=)cAtkr;=V2?DAe5nuMeid*Tbh?hifhR`nUmA$CZ$4NV`eyAn})K zx~87b^+dD~i!2{1xBI%tAo1(M14>uk|FK#tW<+}cQlcdPh^-((Do97sb(T?VtfFa; ziHBplCsrCK(!&_rD2?`_D3wx$gJe&<ru-{a?aQ`X(OdA zaGYdesYw(xJc8=$_~Y3Wm_SY|XpDnEOfPUUG&C9%!lUFA9Y$hvvBHO4NoUu!2_Rx% zxC589wYeNRfF0F-5Db#NZ zO#=v@04SiB8vMSCK3R!dR3Avq1q~paq0&v*W(0`QaQGgN&WH<8`HEI{xL{W>Ec}Xu zMFqciptHA+SuFzPm@;{+NxI)qPE`c)HoMiw@PjnvT!c^UfV*tyEXHs_v{t1r*WVy3ZWGi2kWgI zP#|YRq2`dq)&j|5*t3&pP@0LuPzyA!3CUF}AszZ)c5uj(?k|mD25Ye#%P^k%&PFVD zPPh=?E%$F8=>`OF1MJ1e;0o*Jn@B;a>Vc~rFCeaKO?u#Q*>LXE($xRY`|%#aUo z;gy4tmfE6akEE8`snz|7 zP4TwpK<&q-w!ry0)tHXq_}uE=ZFNugD=J4yPF_z=xw|JqbDgCDJxgozzdS{l!e%PR z(4@LcK%=j@VyHvMYYVov(amiePkrpr6w8^)4{)T$d5mTl%OegB7ip!I;jb%-z0EXC z5D{*IR>DCnvzMGQQzKG&swA3dBL6Bz-zUeGD`jP|rywYzsH!QA&hor(Az0rw%}Q~`5TuJ3|J5B1w94%&>#fJP=u0ACI$*a zb`}Dk-u@@lUIDE91A-?2oTKn-ReA-9hGLKc4~aC?rLiGrF#~Coas*k_a)6yZoB?xp zITOa2!`V;?7IWM}&V@_tkI#d8mSaFz34mwQWRXC(YEsD10y3~`J6Y&p7G_85(@N- zF~mA(FzkMWk~F|ysNQJS@+O@s+){)+|58zCSn%7*-a>3a+Zb+G;EOF84OcZ*3liNQjYSfChbv}n1@8zC3G@>D36Bswm3A2UiQ z&@aZ2E2TqmVA%bL08MH5vo+0nqYz!I2z9D(OR=QC=U*x^gaW$Acw0>kv5ix@g)pG8 z2vM(23>mrRs>V6(f~18|gw`P{#3XX*U0xC}afp#yacqKnyWpG+F#%}xPbkeDRGlqr3Gz1dYSiUNkm~V{C|$^)E~^gIak}NDv$km&Bt`ZJu4G$$MKS> zoM&zgI6p}j$j)<`G52f7=X4+z9xWJObaAF3m;CXYRjIP9v8tO8HJUBYcJPZ&yVLE- zW#dYutMXi*KeVBVHUhTZvtvJ=OgYTF@HZA5m#g)LlN+gS%6BX6a(8vjnaj=X-96iO zGo0seckWMK6#@_IfT1vcb`(TRHO-W0%nZ}*7|4%5;S2^Qk^;P|y;GoB>5Mt%X3fs! z1qt^1rC1VDt_XG8ys8Bj*37Rrnk`}Nj___zM1L?Gc`j1acru;MEfQ_%2xD)=Nt)$FS=CM3 z>ko#bv3!m}8pA&!!iJ?mro4j8xL$QlEuk^hNNcR$(%RPE;d~SGy7=7!R45W-5~)ny z)7#fSFgP@Q0xq*xuAM075W=k{5f0q-cia zctMn8Mb&h}v@nc|b3H!@BkW?sG|P(;#CUm{w$~pFN8>eXgO|B1FUs5f@qE2M-`^iV zgb8I_NTrQ+KE$eS+O8kQXP3;0PoNjltqF zLjsXRrci0f$eZ3|u>}N$X!UR;8jB~AsdOfr%NL5Ja-~|UH=3<>r%T6uT@Qw%@nj{< zcnbmSpPLz$;{{QY6;;y>)3P1cgO6_^0)7j5@mmOr?=1ikCX{g@l{VJ-5UU!}e%!7f znAS6QCAWMC>A#^Z%BuEV0%S*Se=dzrOioSDXmtaJ5pd2s%Wq%^{`^M>QRwy96vKFI zo^3v7Mww-mjT}C8P~adz`Pth>hFSNNK3_MSX&J7q??HoSD@A^Q%HtF`x& zk`=xkIzmw{di_wOeLIzCbs+{dRc4(ze}vkMQ|BACp}C;rW+=z0-$W{m88+LTT5D5g z)N1p6%;q|M8ZapiJSGdZ7-_L-Yb`Ma-F(66Z=jW}YIU8uO*Gk5(*c1&!6Bhx;SrHh z(J`@c@d=4Z$tkI6=^2?U%*DHT`2~eV#T?Au%E~J$tEy{i>(y`2uu=a&z~<;_w6_iA!>rAn>Q>huPq$!xLO><*{P z?eP{B`$|g7%Ka6URn;}Mb@dI6fncbqxh34%hL}%wLts1G*FW(6&B#9Mq1(eifC-S* zZP?lM%3d3MJi~o`>U1C&3P+-`cp`~1Pbr}e=&=4v33gH>=s4+A7|IhqjUL9RzoyA_ zHeW1P>&#EfVyS#txsvK%VVjl4Yz9xk1IIxR zUP>MZ5=+MCap1r=#d+jOd`nlxzjhRgg7=u!j6DePVV`!hEmA;@o9>a8);gNu=mfHh z=Bc1@H}xeYIBxB9{7(Js2{O;9MyL70%r$g#p6_Png*f26AhBBtY|P4he~C%4TeYwC zD`^9>8R89Ywsel8*(%)gyp1hKkd_#lj$8HP^Hv$^##|qsR0JLd=IBB={^X7(+&)>_ zm=n_*qcOciqvsJ}JfBsk&wTc%{ZEH;COylu7Y6342(JHQx&}hQO4VFzfdl7zPh;+1 z8}ndk?IQrqGWzxpo__k(^Y_o6md#0@ZJ%TMF)$D}2MiBJKr_Kd)`N>fg-OK-=+Gh7 zsT%5DgxDB5X(syd&hMX}PSlLVm8mi0RgK4Q?C+ZM*tIJBF}W7R%#Z_l(}96I?AH4Z zbi{ki411|6W#7>7IOMesqgn|7(!vtY0>C;yX^co^R@9XCN~$rZmuZ_m^W=h~2E`d_Ha6{*mAmmqts2JQghm;)I*|S-K&oEq4#P#k58MSV-DhT3tYE5IQEz5h}IT zLBhJgQo5HQtj~l>Z6RKxgE zP(lbHgc3q1rIb)gDIpJqm~vNY3o+$xaNWn1dKUPx^e71-gpivIcV(v{@~(ggbA*~v zh`B4Zg_uT+F~(YJt+l%g#FV>I2M8gA5JJ>f^q!VJ)6tt!Jqx7tq(nEQt`66?*%#ZV zLAP(v4^d&5!d;-me(}n~rAl0K{}|?-OQJ84qi*}T>`#`k@9>K1{a zCg@L2JL(Rx3`dFyx)ZeVoX-v2E>bsg*LO-qti^pR`wlI+R96~8<_55{(?nQGwuJi6 zoJ;o48Y2hu(xo3S=YpDy!2=FpRF)eY#uyKu8BcD)U2w@`xhu4Jx9@S`xI!vzYEOoA z+?4J)xI%W0HnoESA%t-2NOFe+A%xJ zhxpgeSO}t}6D2FOM%Ib#^rcaD#Bi5mNv4aQ^ed5AT1`x%M;}{*?2dtnwv3m)g4Eo7 zL_X)Wy|cu{_Dj9Tm}Gn9>(+pkP1Rsw^L4_U0kfS3SPYm;Rh>U#F~)egCExP@;7-vo z$(hZ!=ucjTI&w+}0z{ZlR(>1`CuI}p6h^7J2LvMAIh2h{l?co3ni zdl0zVVnxZ>3&7~P+(KDeP3d8P2!~~bwPluAp9Y9}c!*FQ>m`s>$v zy%EOUpnM`DTcY4_bE>3?KcqwCq|)D!;(>WOLW-MCPcmhdpKn)|k|aDf8q;gM)h%E| zOeo`y8~L+%6~;o#&8aIvQD-$3IH#YQiZXj&{Zf*_*fB6D;JtwO3=m-e2w*P&E&zPM z-Y*_Xz9XOh`o`_)kqf_l^U5f|cx>r zgAiujL@(!6*E@VQI)E{Iw8V;0fLGn`*4<5#ieBjxmww1RT9ab^RC((jA2J?XB6;9k z^h|uEO#1Ig$hNpJ@a5&pddjW#rOu};xHJ#|0KhQu_VeF%DSriU^~Nrsp1$~^7ERDW zfyeTcxjG{#%1FNpaY>wllpw|f@!s7$xgD&(*v)y6Yr@j3ajv%+v_UPI9gK@Y+;td`P`=rPP1Axrtw z_S;JA`WvapoGy2sSA3zOla`A+6@?ZXUW;!2OO&<;!HeUV!!@>+fE#W7c7N%$9)RBD zN8pun3El^o{Y>Wh3MF2M?6D0ngQM2JvUmwyK1t%Z(gjiIRyn(*_w{+k9 zC`idqpZ}!R0B}0{GauOti{TOHF%UBh`!3(w!7@32*oj%Xd~*j=XDfjHz}-)W5b^WR zhY;Aw5q>y;!SnZb`tDWi`9aSCQ@}#F(x(qr;a#Fw{DJBLZZ1xv`r4Tyr(tWLSvKfQ zHw-!ft2b0JdT+wfWg z1lHAjziCKDUz#BBDoOYdvVF$ZA@TopK!KlTR_rhW-`B1-)9Wmre@oiU9KQJvh-lrr N?7!cn*`i$bGXP%$q+|d9 literal 0 HcmV?d00001 diff --git a/pr/395/docs/static.files/FiraMono-Regular-87c26294.woff2 b/pr/395/docs/static.files/FiraMono-Regular-87c26294.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..9fa44b7cc2d38680bc14df07ddf3f6b320740da5 GIT binary patch literal 64868 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

^#>IXLWsI|EZ!SFj*H;jok!m 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 literal 0 HcmV?d00001 diff --git a/pr/395/docs/static.files/FiraSans-Italic-81dc35de.woff2 b/pr/395/docs/static.files/FiraSans-Italic-81dc35de.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..3f63664fee6ddf221f71a57461a7f4d1de281177 GIT binary patch literal 136300 zcmV)LK)JtnPew8T0RR910u*cj5dZ)H21ncg0u%!P1REj%00000000000000000000 z0000Qg9sah=u{kmi9!Zo0D+=z2!T=wmlqKT3XacgjO8BzHUcCA(liUwH~<771&mP# zf!B2mfjL_!sI}awv>@!n`(|ou47_WSJP$%jZL50){F{vVenHY8P||r7-3=UQ$B>2t zQmbE2WJ2i}-UCumA+1f+WuL>nKt(t|KKuXw|NsC0|NsC0|NsC0zs+P4PL=L$J=hL{ z0|4#PLD~@z6Q+fX@ykR>zZ_0f1T1v8p!P|wG~f?HhRJ>KTRmzr#QrOCRr ztT$US3cLM@p**wp6ltxI)~XXKtS%jBFq*^}GX3~0=^8*&Zg`npIk^hYLKt<>PL^~H zt zj+~UGZ#7vNcFA6NFufiu3vK4KMK^-}O)Ko&LJ=sUeCrmveW#bo%7*SnhG&>Qhayk} zil~OHS>&1mG!~q_SEjPz2JR$)qI!SP7q%U0TFk3vPRsgGKZw}<6Nl2eKJIbz(B**y zSi@!#g~HQ9h7LNvTLb|G!BUtZW2{)qZ0N{Ya$+qrMkotJsavH-{3^ci&-s}Pgr!qF zA-(u96oDyG)-5RbETo7@U?OBQriOGwKVd=@qx`7OiqxQEl5RX=$EUYTaZE`{SVPY$ zr<8c^eWZSJCyLXz==t4ju-W7&z%RtRt@^!Yt~Ku*QP5@|#0PKqz6e*>q%k8zdx1k`IyA28z;>=kLHy zl$J@BYb9g2CTUf!Gh@ZtD?Hp9wYN`#a5kBjs+pXw8+}Zl!~5c!MfyxwX_7FF%H0eL z+OUv>(*zU^je^4smZS*{%B?qgcp&#q{TE;JA7K|~u~8oH zPqH9q#q#)rdHc?A^S%U|xlbNE0CU#mpTFRfB{kdZdP%*3#k0p)=6cKul)D74iFa$| z>=U?-uP0cZ!mRp6S=3tz5Ru6*P}7GFbbZ}o_rJ2DQ5%Af9$fQkJ87RJ*!j2qUbyk! zjhyc@7ZFcU<Ci+bQWUDW9UmGN3# zycH7-$ac9fGFrcDlRL?I1DeeA((Iuc&PPqAR}h1sV(vDkVS!>-)?9DTi+qrku4 zj@d9fXC>c7KVIl6Pc6^$sh~pjAjk%>qbMD0Danm_eQsHla5`zzEON|v^Hb?365N5> z^E;1HXEAu;y9ZQ5c?I#^`_eCpxR%qLp_m-kT=0upxMF2@hrd?QtO)z-9ynb1H>6vMw$1dQ_5Q;z%s73t=Os6aVH(ydL^xrLz zp+rC5uwgk#@4bJ7lj?~!)P4+CHU6|%r&fdggIR$`1&yD0_@@kqL!`y{WH$JZaro|6 zxsw3Kwha?0L$Qi9FgTQe6eFTBm5zwHFDsG3+J^dmoEO*#RIlv?R&JyR&%)pE+#mD> z4}N(`|2#Z@>^=AWTm|s_1CBFGaYVEB;{RV3|8B2y-=Ar#97<3_NC?JZ(F!3{f#|VS zpAnHYz1wp@BJ36-Tf`QEg&q*Oda}jtc>;j_l_+jllf^7@av zTMy{f?9O<3kgA|dCH|jI)vdbqgB&g)%gx(6E%NHDcV{fyjum7TIG~1ffwdhwj2)i8 z=KEJ&SgVy+L)0>tpi9&uhh$6*EsW~np6!RadE$X{j$u4~+~|RZnoyi`6ySj##COf? z{%PX>@elOChj&?PRN$sSn$ZZB(TK*392WKAs;&2_iux7@}fKH>|0D6IG)dThM^MMpd{9$7rhwtP2!GhgH?t>Z}|LjbJK39XkpH6Zv^Hx$w~I~Rr$ID+}@ zS0(A+=|QT@tL*Lppisw(!IDzv+z|k9tEN?TviTs^OiGn*FTPY&2FN^9GB&;Otz($J ztykh-h_TFkmSyEYjBl(A!(X*Px+g4J{l8TE2jo&?E=3Y5mJk=zT$0T6-*|uf7Q0oB$9Wd7 zE5B-ufJV{-Ar5=j#Qot2(oM31II$TX`P}+Hy<#m=0Ul%=80L#=7OM=cS>`aG4e;VD z%@WQMCpmsVDk@W@Pt}PES5=|P1JAMiSHVe9y(+NLy%SoP&ksks0VWRW6Stqf8lroA zttgftA;h8u19mRFIhqYwi*%+>vQGxo>rW;W?gLITYq~(u)G3-K^RE3~CuO==qI#zj zz0*1&pZMQ);(|FvrnvKG0^WRV5TdK7S_p7kn|k8&%}~t|Wnn8;j2(WqCPYa2n_bA; zKKy^}XPxdJsNCVMH$SuVmH>40=-3s?GAmM~FMNIb@p-$tdf!YhL4YTOBpi@11bU50 zJOC)cLg&POl`aJH2|PcK$H(PzWm0*GOd3sYPN|cjuvnf2Cfy@ z>jpNvD=_~WFbD(4aF&pbu%FCYcHwEuHI{|%=@9}SGFj!!i5=dFN$|4$4I zq_Xziyqt<=5(w%*^~2Zye*ae8`RS8)f_QhyL}=Col7-FkC>)_8p-=(Q0IFd%90jOu zf$^fg==A&8JV*n_0Ra-FSt<1o%XoF)Zd$Jk00;t_HBH^Oj#|~o;IssJ{X7MwyADqP zdO#jPoH+RZ|84Uj>3iQ3UZEs?bkYh!B+WvxmVM4XaAtYY8FGD@*LiE{a$e(Ceh0dp zZfVd}G?ZqwJmD$;OQPxekyOZNo|px%2pT2-#uNm%&u^zhM5t_d23bV+s6@>w`0sV=&)j4}&^7Rf)$Mj){?@c_Hn8pX%PsSO*pB^|Qi(y=S(L2eFaDdZ z4B(7>SO%!5Q+4(t%_$S$1N>5||Fl(~tI~4Oc?^rkK4}7c;83uOrd}^4 z&H<~A;;3~b*ARN#)`$O3-6vtUVK>)#34gf@JETtu@3It>m+F)$${lyrkwf6WsWn2p zNDM1nG=!20iu%pGzy5PKi@az+L&!eiqFakP$Ce7}SYs1%+W%|Q$lKd+Z9>gp&1_&+ zu4lJUbG)G%0SbTqU-2L3@_+lEEvG$BQo3<^1Cnb8UQbN}IbwgCo8PC?R_**cgbI-C zgk|$PE5x%q@49P?YZ?F!(n|AS+};s9{Qtdgd=g4*CtdIf1^JhH@YBsr>0J7Z(0C($ zIQTFaoHd{Wg57XKDs4N__clst%% zoFlSjA9@x5QnE;?CrTNIRhC(7D7!PvZD$zw*=#+GJ1=vx@-oYKnf)#&KX22rm1X$| z+k{IlN^?;K>ZDHk%!l_^8Zyb|CGswN1Nc79%-75qj=8*)fZR2JZ7JPxmjwQ)X(iR$ zEI4)e&TeSu0qg=Lqn_DueYJYeszrTrN_|m_^u96It5d~4VvCGjjPZWN5ucy}io@5y9ESq4~s5Nlwo1To{8?{+R${gAu zU3lR|`GuE;5M7-gzn0n*e_7qQI~72uB4k{J>xL#cQ@WYZ4^J7~D*&xr8CfG*VQmQh zzgMfpyo;5h*&rpG$=E!h`+s5nkubW|i)s9N8ap4~m=;#mxM7Qk5FZl}I~P%8IR#Ij zCxPv=EY%7mg`3(B2I!2+KYT+!#qxE9%M6X6)|Bo1aeX^(auVe%KrS5+$#oXN%b!Xi1!vv=w*^Qpq%|jIXJ5MqUBx&_Q@J(_jSd?+-kmn2 zaNV8LX8kHjs8mRx5;BxTk_6qBgxg308zF3?U|L9^76N1}pxXrRbC^JyWKU2@qOIT{ zfs>#E0(YX5!=&KU^W5$UZcj>X_okGNbKG<~ObVyH-u8N*)4n}l_HtOi|Fzz9og117 zPOw~*jg`MD^xnIno-*}MhoC1s0qPLhC>3Fs!R-pUa{GyIV#7@=&`*5B6KZ+BZ}rY| z`evP$NQCig73)jFQc;Zj#v>$1q=4xAp&OsRDv5eRM+ntA<)Ev8aMMQaz5K_e_b6SBJCp^w3eeR#CKeoAN<}2#9{Audlds{4?X&&hM|h$;*HZpis-;uy zln{or)QTYpaOnBEamIZNK<;Mc&6Gea0df?$)7uA%v9*u`Nir@4NgxrYY?Whhvpuxv zNu1y;$?VZwX_McZ$9>!q#T8KKuM-tAv$Hvx%v0|F+vZbXI7KB^Rcs7{(5n9bn>Jes z{YxA{?zZFf6zxhCZRnZ_|Gh!X+)tV}Y#KsH4oP-J3}T&xDwfWjOXmugE(-${&ZY~T zM1gRkH@i_y_PFMz`_dHIe#L9N(0J!YM=*nHP z-L)7fVYWa$UJ&|@bP6aef%@&Sj$I^|tSXA4RUVY!3+~`+Z_IsrMU&1_f*caWGQ}|r z%kcRLf+!A+;pjO#9U7H*y0_8SpBv=;dQ3!gzupibAtDkI8v);mJ&J5dk1Oe|c3b!a z6O1uNh=&kDg!!FVFa5{0OZQ*bid9t=5fw4UGoF!u!2fUg@qN}lgUJO4XAqJ7-Rg0)kVudxXLO*f_)ixPUplR zL?eo%DVib?Nsd*QQH9+>e9;Bbb1cKK6h;_fgb_uDkCAS@pO``r^n#Ft-TZi)WSDw5 zIf#fz5D6iOynnTK2-4lq?;8urjkU@{#2Y(^h=@oC`Qzu==D&yPH&hIwE-6X~+K6*F zoxcBjkkkMF8y#n?_xIhe9NOwsLc~l+2r~39Vl6}V(-vYi$PoyqQf0DDeZ?2y2uE0q z5n>R+EdN*gF?IeL}G9b0K}M*-SOAgLFVlL1*PrP z_IhvZ_D+$d0)osy0tp$*zW?FreE<1BYjPg<(SFwER#a3(#28UgQBhUfRi}}4GK?8A zlpo3yj&KBHZg`9k!gLu*vCzR7Vs9;35W2Z@Kff}vt^6H{x8`gp3iv>ZAdX%)BezH= zXX!0GE24<#Hx^MDj9XP@&lR4l`J?mQy`TAXzH0OJ_Z@$&9kEl#>BSWxnVQXQBJD6h z`uV*mrAh~DJ7ZTZ(;jq2+r0n*2@MD?V;W|SDS|Sl8lY}rmI+od>jCZFZ?ZpE zjRQL?Kf5#j%YFRgvH8!{_}>lb-F?^ne8B%q5#{~PIbimFKXZzKWaEJ;U;mpRvJp!|Nn=0Q&eRqCLyP!qM-vZB%Fci-pmF?@Ci$x<(0Dj{>8WZHqOwb z=8X&qtdmgKyX)5Szx-xq=SB*OqundZ@@Hog1pME%0)l`BP=((Z8bBjx0?oeve@7!I zpazS9#laHcAI_3s4OMdwhRs^F>(s5Vw6XvI!4ZN0n!r&gUZ2{7y;AxPMS8nk(b!Mf;M|{wN=x1n?k6hDXr{S60X2i{;v{2@lruIF6Kud664LmK=`CuUTNyX2HLC zv*wGsafY$gUoai}vQ%tt)ZU?;m z&qv`%jXml>fGv$V-4b_ejO7IU-+BS=fgAx;*&Dw~ULCYcofVv)yg`&=Go~PE;dJAv&-Gg?H$@ou?)rVOGgvOJ#}%-aUTIQYJ6~wV)=cF zL4;3V@cE=LIuG?{1q?06C9GA7&0|1fi)$P}!9pwD?!xB6V#*%iV?nH6)iX0waQrfl zupVwIp*6bRehukEx>MM)8_4CLI%rj7C|OFg4*2ZRzp;k3#Dsmg64Ln!2WBAMvtdUO z@cKi?UFjf{P*z$MUI(SU2%$O5Gc`5&Hjna0sCrAyAd%zBsD($v>WV_f%u4`q{;5?0J>?(1KBJS)`M`|iCHqYOFvvq;3wbZI1HoNnJT4=0xl$kh?E&1FxF zzggth=gdlkRJ+nEIpXwcRB=S`shRvyfM_1}1J0rks7aXNI#SNivqX|+c&6l~gGQJ# z86|yueK8o1e?IRCxCFpMCmt@xmu1B`Y7;a$M|@t8hYzJluEU6X>%k!UX1RwYjZxp0 zgs2QX40O*l0az!e()tCEy4bCQv=WpXQS>Fir(nfaTdo0H`x+^$y?vEgvYD)E)x0r8 z=}J@{ipJ%~h<4Uc`4L@JtR=2+K-^N^*$5&50`+kTYAAsM{^nb`4D0Ur7&#c$GKrD^ z)vm%JN2}S6f}h1|v?K|U$TCNYwgj6dwQW+<43@^Hg@poIwA*hgv8X@IbMW_&8^!?&`pSSi1u{###7BihQ_Z!S*JF<%#Tc%@%x8v>Izr z1y7GvpK>k?zd6Yu?1Cs2UNv}B?19&H3JKusnJp`|0zS)Qd6#_1x<;jA)7tZ%NkkG? zQCyaE8fA;(fu7nUfTSWT9;M%iOedNI%BdJslC7>MFm!U7!XOJ;0M-z%rC33*gC5)n zi;@~Q8n>wIBma#dEmG8`M*AjeY$mDQW+b2_(xXi@a~tz3+3ItojYkhhX)yKx$BVYu znx=RNXP((|eDx8K;847t0KV)3uY!sIImgZBxJ*KgFp_pb4LJ|uB+?HrzOT|)7c^0+ zyO^6YbdNd>=-mkV82VjFWYv1mur)EB4UphWGxUhmbx`?#L(aDCOe#xWvJlr+lrA|M zCDECFXEWgp3?q;|ogiBmYSIvIO4adKxPRb7$Qn?}#g=}DrQUNez>EG%S|6NdbA7P& zMw#G`MC^he?(t(R%(x{FH_cZzp9{midJW7aEPzMgzFYiuUFh}T?CzY6K&&zFN=>pQ zzy<(4FYcT3unn0)yJ>YQCIW{f)D6NSF;yEY;uaIgl~U!-#L0+%6>2KftX8^2|C`)3 zCBKDE%7A0$1dDQ(FK??WV`EdP6*NLPd8@7#Jnp-Z|tCrPnh^ zj~-}$UcD~2@mg>lsvT)*c9j5Bw$n&;F!T$e7UgI{Z3pmg=px`}9thB{pY$}VBt7wh z7s>+LPdIU6;W%1n)Gdqg0tE=wgTP(Od1)Tl!F-gs+J2yCPnbv4RJ7@NGIRy{MXn;| z^Oca};%Yz|1*Q#YOcqO`B5~i)W1Hjp?9V9|u{N044)%aU-~>1au7F$jJ<=IpFP?te zdU0aBwXd3B=m3BKk_Ipw>Rylo2<4^33gm<(Hb{z??5&kXh6(eo%UFOB36OPX+ag$} zB9Xw&<)Vp}0WpOz(V?p&4pLiYrB`BQj@{i)s%IYtcpp2z14ho)^z4?nxW|081y z(JRo#ko|Tc?58{|VA>8(bvn_RS+jh+^^W58#3p95!c@{hj@32CdDLMZ6DGBHVc0#^ z#}HN}tP>N3J_!u!8j_Q5^~(1Orvc)`ylC*>q@JYO`25np)UXFYP$7EY62RJ$Mk{;c z6dlwme}yS)G(?3S%8`s#z+luRETv#H?s6IzG06g*Y@~+@3;;tOs+^CRW3xxh*SH0H z03B}Q)S79%XS%w4_Y=<6u)>qEvN}XR;{z9cByZp%KJF15}V1VC?R0leTYRwh!+vrjK z8AeXOb#jgw9j~;ytbiJ%ZlJ3aMPMLPtVGDbTygtGVV8BdcW53Q8h;#9|1UvDaQZbv zVF@h#w7mD~L7{H)#q%%FumYZIn*u`Rf{Lr3sqTLo;Q!tKCngq~TKk(l*}p-NTzXs_ z>O$?~k}Jvdn)X-#4VTt}UqZ=M)sm4P$JbPf|9ssq$lu|A;s2hdF`7k)h4MPfKG^Sr zp~c1#*omR7Q@K{2sL@a>vZ>B2so0J?u*RW1w4c;RBRCL0^OGrun!~aoi4al)?s?#w$dLOj8QVQ(%^lRqMs;Jdc=o)3O?au=#=gGoX<=zYRP3k zgM;iR$N0+9^};RRG&xlQ4ai*d9-n6c0?G$JaIw-OS4*GyiJV{bW^#TN=UG&!)Zx7E zIn*0!HQ)0aFMRO3g@8aQSPNHurV*KOB;d_9HU6w%x^;d;3fvqA<%YKQX zICfdo#sM82+9G-g4Zp?t28_lrm?j6OFj$ZW<)3LfuuI)JSOYFNSltU`4{%xvz-zF# z%9XD~1R=2v@i_u%Bv~^ur_}{exD(zYc1Agyxva@&R}V zO+ynGYLsHah|LP3K4;kFGvapH5I*^K9LAI zQW5#cLbhP25~co|Q6Fiq`wEL*bXgq7;0Yd$3#>53!E3XaL<|OzK`>E})>?$2eu)hxymwPkV52NeAe~$-Rmd^! zUj8lBO7-nE;|fkwe?S!G1ybio1{h3dv@qD}F}i3ij1EX{!RZ7&!2rD^A#z4Yr+$(BItbpv=>il^wv77RU2r&!GoP9Qge>B`j&xp9kYyb0u&ALHV7|x( zI*A$EfsIAv;*gIg0p3O!? zh-%*2_~}&UL}UFu&ut@F;zr^P)PyX$n-_YGIaTTnzQH;yWyA*oQF#>WEi{`fl2pne ztMX7?uFA>gnbm~b9xCW<8BQ3W{AV)8>?p4&AWwb6OQ4izVnM(b<^|^d05zD?6{@9i zMNGG@w35Y@DUu6oQ^8E2kBhBRhRal2`WtE(!ql1#9Kh5;85Bjk05ehz^~GggBN`F7 zfV|ZFjR^f!%S)}qX|b~8a*3Q#D!GzNb#e{BV86;T6yz;la0fAKVTH_VW%Jh)<^1kK z6}?T(AOYIsu`OXAQVB5N&6O=H6NY86F-}mVbYd2S(2>p57Wb|8X7Pdq+F>YDeR=PU z#JP3GJi3xYr@r~_#+ftcqC{)at;r{|=6v$mbzRLhKENgw7_{;ZyVvBA2#4+!ACeNv zcAnp9ut1auX|Z!v1o-?Fh)`-swCGATw<62~oDhm+Gfm!1?lF!x-5g}19C0e-vZ09c zz<@3Z=fLm4S4Rikp;w9;T9IcRe@Q{wt?8Z|NgvnD{fG-B!FG}Q5zWJFN`!L(jP}6UuR)MmB9r=Kz`wfIBee3I*C7`0!w1n7t_)+9mp${F4x5 zI%i1N-KHTFDZgC$4#LLifAe}|*Z|URt=O=|696?1;3T<=E3(~NC@a+^sGjbVtrN}{ zMX!iU+38<-S0@yR7H3fddL!-J~K>BpS zDzF8m-~lTpc-XpEdafbeplC^~xBl%g{TrBG%VkZc$^u>}q%@D3ylZWWCh=C2S}dh9GB36fNi))?S4{sXA&(ZjWal&^0A zXqYoC`Z7U$n(I3ZV@d>EnJBaoRIS+3m9&y+gwLfK1Wz$;#+<*Fo(U7AA6^1Lq!@S|J(*Yny zH=#6i07-XB$>R&WHBwGYsI}ep!n8+b;2Ruk;*_xJ3Z}J8!qnH$8xt`bFK!dQwSn6Z z@-Qt;FQURu8%>f5|T90?3BeNj)YDe|heQ{MxY0kN+TVforwI z`u#+ahgel2H1nuLBr%e_@`PMU=^0+6BEBloz!06@;N3$t_>f_apXdcLFsI&JL%*TK zEFo&z&~HI6BRVO0gyOJ01HFPsh>&<+TcWgomoAlk(q64FX#4fh|AyY^qcf@lCiIq^ z^li|@Du;042FHV!03jlZqr_xV^hu==ZOVuo1z5_|Xwaf#I5mt)QXfX}Oz$;Ul5N)E zZ1MN9Iw_&VWr|mO%x9%Q7p1lMkCu_ID#+|j#eDo4x~to(4|QKf`|Yu+^+%xRdZO2Q zp|=`Nz1v^bM+Eh`dt@VPnpI50$7N*o^gzt|=^4lvo-8>*j+|=6b9a_o1M%dk*?2-W z8L6L>N!w&*zMc!4f~c0VG!E!#?Vh=G$o~99IiJz7-ZAZQJT$wKvHL=%zSu0=x_WvBfTL_Ma@SXT$L4x0*nhbt4Ly8o@RBaQs_<1Aj9B=W7fZ(jA<&a1R1lk*c>jGok5VZ z{ykCmW{6+!%M>Y4C~r>`Yxv3L3NV2X{qC)Qck+M|Gp2bD6f zQfLM!J|pGnnWW#`%s>Hl#*$+e4oYk)r&?;fy``tTy#w_pH_|qR#LKi5*5`80F$sf^ z_`3*_f0wAzljggMpzpfZ#Pv-XI0&DYxtHz}BByL98oY)bIofj}QUpZFr{Ro7EhWln zrH0fj;Gs(M0s=#_;wi#8!Rb&*j8?|5I`6-GSIsq4`+sBg)=+~r)WpLZX~Ez@ov`SZP+3K}f46E&7aEM8>W4nOII*+n{ir?7nkHbFS4(r>=}L zZmrM6a?;uc#cqC#eHbU)=9Kg9whUyPhgaY$H0>|lA`_pwvn7$1kyF4b|2q0w!|gGx zMmbvtoW2p>)I7_!kcjw8(rGVk`6%1%vAw3@AXskXDZ)Qq33|9)R(4ZGc$UeIlKyC;K{b+*SZLWZn1 z>&apa3u@_e*}Ty&1hvwRv|DW~Q*Ba(UVO+Tw+L=9?IN&jA~8WsuCbx&9mz|| zX@Ig|Ly8D8u{akq(&c>n_4S7rylPftZ#S)qoHGnkS3A}x^7B}NXCKNAin2bDTv#;X z6oYgu81zCdq?pooW+Gm?x}>4r*<0ms-sV|j=}>2sdNJ_sH5lg_bQy7>QR-fwm= z@Xz#M=#y z0>vtAMT<%b5nyc_V$Nf>O-~k~+YJN3Rf=KC@3HB%nf0}39ml{3HhNC7ocF2au#OL4 zpCH3{Y%5*9CNic6Z44zfF*Ff)#4L|raJm-6&fmnCu9u@wL|VJEl11uk>x~LT7ol{^ z>2Y_j4X-4Shr@8_7URwWGQ=9G1(ziukEEP;+F`>Sqh@LPwjWrKS-kJY(b5=%sgus( z;Og0wVLU;i-4T{0!xAU4u)VXdf25K?2za@c`QbRXj>jtDZFm_5cb_&n$^%+m+I|Y zVBciwasYB$Lln$xgJXz?ca^j+Hohf^PKMPy-~M&TPyDy6WBj5H-uM#0JVS?0U=WeC zdv3<30gTVS547;(o$;D-nKYl zgG0=TKAd>ZFxM3l$yF%ZDxJy>^o+0WV+IzdVXTQX)Bc4y-E3PeJ(&DzZa_~wpMc`X zfpzyXX9CM!TRQkXwv6se%0V_;%o8z(f+=pwjy_yivx2Il{>-ef7BI=tJf zMb|Y>;5I%rK3{@UxZ9Qiu=vWedxyXts!+rsvd38^_w*{W=TI=#P@bzbG}kx+THNWl z$@*YaQ~-b3bVoMFE&EQ0i57^RL|;r!P<|V$qG=R#>LA!aFw8*MHg;xK=z%doB>RX8 zVJOXq?qg0+_ojdZFH$ewL^ON#B0Bg0zJY1IiY{TD2cvB> zBWmU_v=(GpvZChbB{Rp3yKw;Rh_!fqltF5CGo^y53u_;x+Xi$qm@pgaTW|_jq8MLy zH!{E21*zSiwOG@N?xFf@qB`*ytM+8i*h>|Al?{crOTV{|ZSJ%0f+?(tHh7CmLU$9sba&4gFMRC+gpgey>G7UK8X4qJSh2lqHRc+%xHay? zqAED_r$!ZI3RA9|_vH|VakE8Xpv&hZ3q-7j(o{@oARR;RG(b-d^ME-zkh*93pD15; z4s_T`Mp$vVjR_;w`3My64#S87_yq}05goHvAdfs?P;!<1qizMRxqr0dTuG{GbgkDr z8wlg&WXxV#zyz_Lt7=$xn`ufPZq=~8Nb148MY^FucX5 z?UWQpo(CSh?HTqb4mQ``eg+@XQ8=b!#FLXqX5T+*JAIvKkSneE(pX{Yrc1~ZnE$Zo|s_Ddn{T)n$r1l-62F9I>N;bxEE;>nY~^0e+-`k#;!IViFZ zK70ALSlflNHw>-NkuewOqVIjgcy-gt=Wh8kLWn|Cs+a3Idq@U5s6+lULJ6Kw1!=Hg zh2H=V_|Rnz1vEaW0sg%+W28Hi+9N4Ys=+D~L)Zul|&v)OBY|Nw@ z7?eUq#`O%EKi!Tl_~`jz5M$PpKzVQ$3(^AQwQ6o%3)*`Bpfs>egLJX&(;I6CJZtZo z)oZ_B2N8d66Bk*BUS9H?SVz!tI5D!vQ%URWWlLC{K`XDz!=3+T!mi-DqL^LV#EsYg zgj?v&?v;!5z*Tz06F<|7y<$V~HuruTe_ZtmThQ+{yg^%2G9?A?l6p#;X@jCZf$zwZ zTR&@$?%&Me=q$I_CVyyK#g>lkvgHgt5IytkC2j9Tf87qi4sm^C$9EEGWROE)j!n;% zy)PZt>d`=pJH(+sH7bx_Vaj#$y*$Ejnxa4AFo{79rAmJ^SVs^WB77ay`$gIX6Uc;3 zs^bhOgfT(n)zk?eA4g;2680w*ug;$Qp$`*ZuAKTQrN_G~h8!baU7^ z3rOgIj{H{uvk1GH#lS&pwC9@y2UC0=XmG$7I0jJ&wA4hRbR9Ef3yMgosRWI&o>9}M z%#dKpco}CU*uE4vGVqZD_aMja#m|{koG7F(vZIfo)Nwp^!>XaXRh#pNX_2$~0q7xrl|puN{3V zutH_488@h99p<z+62?Z9iFW73ave-(FxdsnD0uE#J0D6hpa5{BjlO!8jmb6|B87^}^E;6yK%2Ht?;yCaV zwxR36qZ8D+?IwzXqj*K9tYg@%z1eGzvFi8~;nJ8+;8%W1lix@Sq*c8YFJOwrSM+N! z#l;vW!Ebl(0-p{@7vv7OIZu^d3TSU`WCSwaZhx3*$Yf?9b7}>>aT$`$njc6`!oTVM zE*W@|%YL@w1NlMP)8I+|NNvH5!{j_Ym8CVe5F+IOlXw-#p@a&WLH7$PC}c3)Fl%$5 ztdQFVCs4yQ5(AZkgM`m05T1+PH4-k>8~wtv8uFePD;X-WK6NLoDb^P1a*qbubNz8) zYH}HNG&XaKeTjiXLH5nu4-d5W@~bG2H+6nQf8QLrG~#Q=4&Yls8j@G>6gbZK0-EPC z#kjo#K}Zu=i#*?79%<%44GL6fg+TvlW6a&$@`F7NYd25^Z+tAkLUWN86!nO0C`cyK zfnLY=?&cZsgIU`)u7A`0%K!H9o);2@A}^L}uZoGiiH~L+O-bousg$rGwHnj+?KkGj<}}Mxt9^t6VtmkN>V(5=G{1*LB_3BjUg9 z&K7|OUIY+AWG-I7$#L3S2INoxJ5{cFx%O#%T4?X-e~hz3k=C5fR7BiQ$yGi5rczE@0&gBFX=AVPs7w`87gF{n0|v&*$^wpqF$ zb4hXAGR1Z6$Op)E?e(=EIN+aj$VYVSPS(AkeYt*-I^76FkJgzijO_uRj~u?lU8Jl{ zNx9GLylv{z`S({)C$7?|ViZmLs=9`{eK>VvQ+Ix*jJ#U~3^Ost$re-R^y%HZ2$E)2Hy8 z^}Bi5^T)Iby%{M=VVbjyVOE9FIphsu|Hy6vSKtOdI*$K&p$xI$# z(c%!&v6_U18)T5vi+x~QJcRNfDmpYM$C@<~8k6thC>Im0d@kIe1I|ZG9JR=?@|r6= zVXSQsN|O;o*g*1n>5npZ3B4(}#?Wsi4y+84;+XbdcTDr&aJ`)baj*_?AO0Hrf!fOn zk=k56Tp%6J+T2dlJ}=GgM{+cBrY7|M7CS2k8OF`&qFumY z>Y|e#KHnlw7WyED6!ZDi6dn%FxEyRecp6DU-lPV(ApmESV$nKbEV$Q(5n+{WRQg5 zMyce1Sq5HrvAUawa}9*C_kYB=#Xxl*Ch$%&Z5s>5M3qCw$ZaQ>rgq=7N@AtG7gC6$ zc07mpUARJc?n}KP2&reHWk*z>Bcf?BQiOHwSqFu^(bVL?8q?i(_q=w{fB$%aByPPL zXn&;CqP7^$Zne?IwN}Ab+~fvXhsRq#Z~)nzCVt6(Yx4TSthmA;yFL^^cHB~qwv#@^ zgoBWT*UbHman8i1>|4Pi}Y?vL(LDwI^ z-PvkYhD(omT}(0pH8d$k8*a?_v&ElW9|5QoLJ8RwsBi+1+~r8ADOGV48LV82tQ8V| zRw=J8zegZT4Fg*MEvV+mUu=6aH^`iz?#)1$)iz8*vbmbi_X*PEn)|!P2GC0nZ|C#H zav% zqDayC6hWr2>|$=S+Y#lIqLI7s?C?LqNJO-0#yQNTh&N~e`mXF$0t1A<8z_EOmm}Zq zA0qq~ojE{ccB`U$L_==VvpPAePR?JRi6FZ(M_%nNg6kCt9TR zSv}6+l?qIyH&Q|R%v@)k65kd65;2bB5^KMcM0#GA_Q3s{t$s{rE}1wRz(uV(?z)0F zvw1f(J^d%XYe;iqi16vfBF!bD(`%!{0pj!^i-qhij2v#Ntq&7Gj1sJ@_3bhsH8I6n zcY)TZfFk3@96hqM)bLm_uD6sNHh{8v%HeK+1b7Va^=P>y!37T|dcWh*n0!za2-K}{ zJ{+JhdnfVm(m~I1G)3xr=vz%wh5QaqDKk?rC$|m;SR(0PmcHI0A^Sr>y}{v?Y-CyET6`cL$SFzP9C*d^89Y8c3nXl z6mfNu`8e>}=E#9)1rcGVQ&?Jig>bT;xi;QVcLC7nS&2f{t_1ZF7#{$>kX$qnHox}0 zUKM|AD9po?4B~2+*_oyo#xlvs-{LKT?P-$DjF>VfpkcCMzKgv_jg{5*sb7%?t_DA@pWvcG%OTYUCJcW!8+ z+aLHL%>Dv<)9>%@qP~$Q?T8=;S`?>R9f1EUXBM!nalbCs z6ZDUF2~nUd%Sq!3uUCc*Aa9S5f5X^N4!LJ6Ne0Vy(%YJ;$l;2l5cXrm2}W*cziyTZ{g&AiYw)i9j)_cKG$w61*bp0}FkxEdli4#ss$0aNenj=e<~`9QJQEYI0;`xmh4#~DJ6FsV|bA&CZ|ejb=%;AfApsmio-D-xzxGvpA?IlQI4gqJDoyLDl) z^X`t76eC(Vd%Z~R>?*j6A)VhGL4pi5Gr8tKB`YXh){U&brU7vgQiC@`Q&U!H-N_>VnI)G^G&As?wTA^+r z1jLUF7fJvTkpAQf#$8N>S|7hwi-)~g(~@38`jakNB9B5W-fsOUD##3{1KCvq$>-H-SK zmbWOVp8y=~PaapC+zOE@D!QW7Kz|O(nnmlP1`b#j(c!RFl&PA;DCI$0zl{ixKFUd1 zO8I^SYC0hepaSHR3x4hbW-v|o7L;JksSr@!Fz>uiLFgPp1Sm-HC3*>S-=50_Lco;v zYupXVKYuM7ObzP|s|`mZZSrzd!^j>O-J@cCa)F6lWR}1-$B(b}1cXljX-bUO%BJk@ z=eX>xm?J1~SsGr%>pYS?^Mw5xq*@HOqA~hf{oe!l1TnV`Q2sYu`jS1W0_X$~Sl}m# z+cCmr#A83lMvyTwAV5)~SH$1`*jw@Urw+>^4xQjeC{f$ekw4+5^^<|85@gaD2X$+P z3SgA*S1w;Osbj7n$kYU710%q1^>yf#_kLSDNZETXlPC`4y9VN)<1RD_!|1NeV3;uI zf_qW)<_~nX7-!kyRlSV6l+;9(NrUo5Ts|RUG>-1o!AnYq_cD!1@1Fz zivY$0xKTb@0s*BJK`6QozN%+p6B5J^GjJO&`P62Ghmjy8;i@Y^k5}>5uZ!$L2|kA? zE^fHc40O(IKBHOeR3D(MR+(`Vswv{y=}C{|OW^M4XS4!L^?$E^#HyJz_$YU4glY6%!}_6 z@Ph(=1gF4{1JS1h%s_B7z98Ti2K@4XUmFFt{1c!HgbCqF>>H_MP)=?I75Bbj)1b$M z6$c330)>l}EK|NxRWfkH=RaG2XIIg-%fSMnYnmUw#XQ-P@^<~t0wl@2^xT>Me*^0V zR@T?7SOD|?zyEw%hZ&c?pK=y~`Cq;YQ$NvdIqi9Nu59cE?B_Q*4Q`J*ufhC(NxeP$ zlM$PdC#%`#{B8FZ`Pm89ov;AwrTG%IRWSJsw`r3uekkx)X|zEnbVpwd#z>6ERLsUg z{D3vsgdNz012~2=xP%+HhbMT2KlqnVUmpY+!N4B>#k?Sd zi#x%@_kK##nBGj%Ypmul#Jm@{@Wn29naf}4s@AZ!zvD~v#YjxWLZ4=9#LiELqs4)0 zKu#Aa#1}Oik)-%59F8c&HIZo1$H7lO7OOXq^~2Uj$>ltg|6V#V=^sgf;2z3nHHCC7 zl2i(oA))3<%@J7dN)2CZsbyq#1~U1l*~1XC`R+8cdn{!miM1fk*daa@_%m>buZJa~ z8v&q9B&Ewe%VC?yYGT%E#O#ZnfY?{Al^94=@GXE{qQ=+WKb=+*1h5>eA_P`YQ1rx+ zOae%T zH&(~E6C_ImL0SkuH?3xx z1L{)&flw16i(|t5195tA3lRe>YDKrpEQ$aJbPx(eg3Ro z^zzxh(ud~q>etNm^>3crTi-VCcl`5w-}C1OfnU$6e`5>&ozVXy3}7}w2SvaHMZ&cx z#NncG<5>(o{EH)~vIJ73mq?K%Q>`q;wzkzd(zwk|JlGZ&9&fvA-3t9MW2JuO-^$$O zf66^(Dm+>FAUG0K{ZQb{QTG&|VJ$ze=Upc+f;xNA>ingLb@682QC+_Oo6O({(H{yE zM3ik)o?v3dHt~5*>?Szd{2;j7&J*somxZ_ObqU|jwJ3ZD+_fKrI=dU{5AzrWJ^m2kA2xsC3DycOvjd)@wdGhI;BAJ}>3^F~B3(4%lE+Xekzxoi@c>R4bgSXSJ z*;DEG6n+mb>o#(Yov734FFi)Ri7bYUV#VMnHkF~G*l`#dii>d1P&|1J6U9fsuuwvE z4Ns)_;Y*Y{QWnbZ5 zODIQ~5>4ZD7?{&zGz zS{thvWYg_u-f?UGaNoEqBziFxTt=a;W$Ieb=4{{zH^~)yk_~%N%wXS!xo39APPdO% z94!((+L*A7Dcp*kMo!}Zu}Rz^Z$amd;H!)i2*)d#sIClZZAf-1ROeq8G*>X(!SouNiz{pH9qOEF2;#E*>#)9s2YeGZ%zF zVazT1{P}8iNdUw`t2O%qi!8SKHLPWA>kt5iDs-7~~q9w~#tXi{f!=^3U8nSoS=k1DkEZ&O` zwIG$CvZ-)tAr(nQQ}xt*&SIdTc0CQ~xld4f2u$FU2gnl!uY$uD6M`=z46h>s-&hfa z?`yvy(Y9xz)hgXXag;`7G)8yACwdYlagrx(19l1rHEuV6KD(LrM_`*^BoiKMqE$d$7(cuwj+IsS#92(n$YNX8LXr0Q7F z{aWDyLMRt+H;2+#ZX=HHBH=HHSqC)jvZ%J)Qf-P6^xI0HBdx0pLh9jzFRr)CH))2O@m~BV)0NRH3r8Hhhb(%-hoI zM>VZgk5HK!e*y-nuWD#&>3|FdlORw=fr*La>JS=C)4s;oP^MIynOj)djY4B^c!HH@ z01+nCG_P}zrq<&zSYlwA6;@eeWStE**|g+0+9<*659dD%+7&F6FiRr-J;+R+_Eu^zTI6IVcnf-p=$;VuDN-39}an4<4wJqvSxnIU(yz=yegIE<@FI7K|r4 zqI?plT}?F7V6zwuJXL=8&HHkAqqs=JFtU52iWuWaVG?PWlFrbNp5j9UPtqTJuA_O8 zzTEb|aN-Styld!_hz11!SQMrNQ!jxj%tc}9C9o*m2Y*dvdN)>HJwu8}yoRFfgz=RZ z;3&&N4$eQTu`tU#Bs0o&%j`nJVZVojn}g=%Xhai>6z7C@6gbN~aT=Uiw;X4KJKzjc zAZF^!+yc+eDxFWz5pcvgDBkRZkyQ$IEJB0|+50mIoVgF!CRFNPQt$XOtW3S0?w=WBY&@uv{AYUidK-sp_j*L+vPz-GN??LgjPBH_eu;U+%m zN02YcI+7%xHHu`=m{P11AA?e$3|e_;O08T?rL_ljk7#=B8eb#Y-O%&su-=DvgmxEs zoe>w+Y0=WV?NICJu4>)g7ju6Pw85V2{Zua)c8h~cn0>w6JKEoF?+qT5ZioGJrhoI_ z08fLEAz%eVMg{TEy7&>gtH1#R85bvf6V$H}`V}Q!`z=`+gIH$CFp;@V@!?U7gaR~7 zDbpZ=bSzt2d!zAV6d(u+m;udG%=#UQYPjd@+msC~PW5A{$kwVW8%1_d^_)VW#B}T` zXkrA?7|n_HuB}~b||u4>^IJ)8e12>ZO-J5PR#B(;30S=8lB{HI*QP((Jt%;H`4vKmS&Pm zo1#ET$tcBomL{t_S_M!ErLvT&sjQW|%}J0_E~(Tzh^;{2fdJI}aU3_B}v{b7P zjh8sqYP#ImWwK@0+7~*gag)POHQ)K!xw;HFh>zfz&g)p{jn+r1_l16v#ea|~P*A$y zq+1~zH581HzDL1mDT<+umGU^4ApJ}nbSFu24^x7DsI&U3x)uz2WAA|Xm&*M(r{nD#H-GIa& zOZDSPH<3(}UEFDxXU>B2;DW?kOu8i&^Ri~wrPv08o8XoNY$y62g~ja@zMP_+)&_4h zJ(N(%jF%#hLheQBx!}&L_lhc%SGMPlip1W!H^&G%IF&|)lSYcAk2@DVdfGx)IhC&6 zKZ)i^JNTqu*Hocy6|ctzPe2^+r3e|faQ33IK?eOqu3_2$?3Gw_>KptB$H%ON*6Nav z&LyWkl@lOCNlE9P-=4}jklfD;(M(3S6|s9IF>R$*jWh?dwl1aIPzFm>p;$<`6XmW& zvEAgnzvIQ3-?kn10m2I^YWj`Kx=HUXRx}pTS1htrs+y;S6pXwyVK?|`^R_uW@PUvju~-y z2^Z*p6w5*H;Fa4GyAJK#6Spq4yRT*sMC_52J(j#Ddg7VDp6QbpdhZu%zfyTg?iHCo zLPZX;#Vey?nNg;yEZHiQBbZ#tnj~)_l+rGFI%u8NO2*KJ(+`LAN!EcxGz8X3n(?IF zn6M@=*ba6=n`z)`ZLaZh@ujpSY4Qo$W#AlKtxT>hueP*{uy8fF1npXIIX((ETJy)* zt)}I9uAJN3((RUhllBM72a6v+MO9srtnEW$#%2BssOtSqg!x-$hLK%p`^5p_#xxj@nW`=((DMq+%VoG+2ol-45 zxbZGjzHiyRB6l_`@`AQX0|}k?A#)2eDZ^^z-b;P0CaQKu>Dm_%+1`Nz-wSVTo zgarmgcMV4exZv=R@q&vW@G>0U0;~}({2i}hB`OV8Mbii?t*gV8Ekt_H$Q)bk1=&q! zLiKhG%>wDFwRvm438(s&ixgI31uQ6erm9G`@O~64_QvsEd;?P@6k?JXQn{6(!wflO z6;P@Q14u}ct%LwmRmXLh`lU-xJJA?=xz*3gHQ2nB;*>cd>dfHkOIdTX zZ~;PP{H;_uwHh4EP+!XYgbL-SAyba`6M|kjXI6ej)yy>W^9vC-v+}p^9^~)%e`Nf_ zlvF5* zl}c-LH9iN<(FY>eJ{qSooC|%shnM-YEk}o0V!QVt)qqMn@JOy%Z!aZyd*$9m-Hzna zNc_D!HRA4?HD!Wk97|@|s&muowdYS{*vZ7zASOF1l!Ny+h~F(x5N9V6gs8q7t_dQjH z+$(=;L z+ZD^_-3h!_)L97x`Of7dXDznQ7cSqnP_2M8fk5ET6}R?GTzn(lFgpG$~ctqpFjxz0tZEY?Zd>tp!$0!olmq#QuwOsyeA~7dZJk~>64z< zVmxxCF)y_uNy)d$ct8LQ_Zk}>koQaw5f$h?-Y&*~=XF4b*HI{BzJU+*4v2sl(j5Sx zYqz_itE=}I?V;Ft%;$PO)InBx{gst&?i~SL&B1$0LW%)3E|P$ySq5loKneh*eVsm~ z>D^PqhDUGAnutp-zU17CqT0fevA|qWTdE1sG$py5)@<3*Nl2?|R;Ri6y!MmZdTuA{h`6lqi$)p^!r!gKKPKUPlqa z7(fPmgzBGDqoo1H%91$rFkpybnOqi17|Pv>8C=%D5TCLAuwy$9TTGWeHKxUG8B#-uSY%d& zR#vQ(tJEqZk=TqHHiL?qm}xibQWe8k5vDpylOolWdg6o(kxB_8B}7daB8}$75Cy`6 z?@*zBBg6#d(VCAI#{LZP35Y-t6^M|hF_tHfSsk;FW?l49qZ#~&hICXXFvetmi zegRom$GR@Y*b55QEjmtoVE_Cv2+^sW-Ge4rWC<<1W8qQWz3N{9HcD@qYwCFc_ek&k36JXn?vm4JsHy zRkf;G3LA8g49>!xhkCrAWj@fMLx&DU)lg92ca!`R$lS9Qvqn~l+7hnl#IzgH!camI zm81*Gi%3}n7A!h;1cHkQBTeo?ASw$I8aAP}*0Z5$n|24>1^2*x@Br~avkqeNXJmt0 zs1B4%7)tc<2=eF|$5ebENzjIZB(KiO8xfLt>F#=qQBTt~OO+%lvG3=8wI=!JY~kDA zWQmmYNC76<97q+js_K}@;f@x4j(BwHz&drbsOmJQ?ii)+f;EL6h!-;Mts0!klYMQX zXG&5w2Mi&0Xl$cg#1fyF#yi+q}xW=^8MDMe7z8W!nVJpd%EMtkLGC;$N5Jj`2@ z$w}fMcJ%l?01saE^kk8Gcy4t%t0JA~k8;Y2tq8pGJ4WFisOMAAAg92>jf0>HZ6E~z z00mDLVjUj7Yc8lqv% ztXUCMkytD)7FWj;so5A6B#8ss(N^1UXj4R3(hwPLIwv#lb#~sLyzy24SaP^6B}=1if%TkNL6XAI|ua zuX$x^4mev3w)@~>8VtwWgy^QM$8rmhEmk2#aY{XL?7dsrpnqby;>dIR(FF{V*D44x zECPl{N`~R4+lL%b5Go8}BmjAp!XZE`@QC5j?p?iou=G|i@TPk=T4z`5uFx)q`u^Cj zKHN+5SFB*Xjkd59dSVYA+Y0X5Xo9o@3*e*166SHf(NVRH3MXyveimlpy z8YOPRBJYUY_UV5SN1mJbtTSd85P+j(WUTEtqNX3+z%qc=m zVk&8}romJc)RMx}L`^IFE*AniMI~*9q?nqD-DaT0QY)Nl`E0^%{>uqi&=0QsDJUP@W1RF4v;VQQd5lrbYT08!HX6A2xn!hAjfVDHQv z06LwanY)^a3$e5jk-m+GqZO#Iw-FIhd*4P&lvX!VRhc$g360_GY~wzW)jb1T61LmM zCf{vcld#917mFHG?6##HTMJ}OSTQD-?6#9hm?VfUSPn3UK4?vGp$U4xEzfe$#e=%R z-+JV?&fvG0^d9EckmYWBkHvZ|JtLWgMb-xvjAc#0S}-JZp*gxWL`ITz(ZrKL zNR2*J2>=YyE_t>D5POUuP+^H7m2=-4Oh4<3bJ|@5B7u=J$Q}Z9mMK0l|GV_!Iw_SA z?HaNF>@Pa3EUtYmC>=Y}a;;pQY26G`4GrOO`|HnC*vV7Ke?HE4;irw1W(sxF!Vfv4 z#Uk#X>{AW<2y{GOgjlK>)71>|`QS$FETS{=JcsnSDrLq2`pyR5tjOWRVr+qOr`Lvk~iE zIx=;ngBVd(G3qE6RXK4p7J2tW{u)Q5Rj8w3I%*aIx6LncNzWDG$X3;>F4 z2w^s0q5FVnJpi-s02!d@py-|GmpD-_DY#V87tsp`z5ubTd4MS$Gb)@6nDN4-1Daeg z`+u$hPh-{o@_2mCzoGLJv*yPK&0ACE7mt{6rYpzFIJ@$U%A=98YdUz=bH_8rfi^w+ zMEkd=PdBLEIUPE$SDOs~D{%WH_TP-ve}dAl_O=Dtfgb_VxEq}T^A=` z`BQ^|wR$sAoeq{SY^DQsVL$cqZH;XA<~OZoGyN96rQ}aawY-t zdKquDPH*3&cIoA}uQ2=9X;Y-V_p^Db$|}-p>?Chq+eB9zToG7B2(b195QzXPen>F; zq#mmUfIuk_3r2}gEacgMXe<)dvz_k_R1dats>)vM%~ciUIMVq>z+RRF^=%n8)`RL| zP(9KLun}v3L|{91fHGnMkOAx;a+ySCNPj6IL-9)haq<nV%Z{4+Y_DfE1Q`azEaP zVD?a%u+d2nF%$Dr!wf5nh^Rhi+SW&%^?D_P}TEAb0yAg-w<0W@2LUaJMB=c+EtlbKzPgmzl^-;;Fmqq3l2O#p0vNe|Bg5 z`*Y{KFLoZV(Y(}h0|oBxq=W_TcA1HLcEP&EOuCzinV5)D*_V51I3W)1GGUV$Qg~Vv z4(zT5Yak+;ApJP14!uTS3Q_;P;L%hmF{W*TSx1=kfeJ-hLHq1m4-`z&pR} zg)xq?9J?I$$Bbjl;ux@avBfdqIn9mO*07Hud7ll96|Q&Z-R`^pc4D76wldo5C6Xv5 z(mDiCbMhn*OA10rG8vkc5+R99BF4rkVQxvD>g#94OM0vX7|Pr?uSQ+Pv1RVtxv{(V zpI%BX;=bJn81~ri=EbHNyLqdLi38)EuZD4D#K7O#@rc2`*zOqPg-S&R-MhK}r80*wU>hEp&JPN-*zwtjtGLL>ezV{fy~6q1k1jB38o`4HO8$mHqLnGIm|dO@@v;u zOPm+^?GH{^{XLT$OtRKV!_Ic#hbuZIcpP)iGKcXc%m1i(i|$!KBnm(rK zGqRzdv$E_y35A0@0(Lkvv-Z{pGn2j>PJf>}3eN(X@K<0C6m(iQ7P@Clv`Cl!zhNMN z#b6)QjOdA`+jD-t`sHf>&+S^c&}PuOBe!CTI_6Hkly#pupZ|q~k*PY`c=Rk?YByxV zu{&`kT?=R5DteYK+sI5)MVqvtu3YIbY}1K*31vM?m#2k$>RY+?|4fB~X(_MG%U|M0 z9n;A7d0z_uQ!_2&|BpxE9Wk1wC9ozKL)u+)A~Y$RCUCE^! zEZK7@B&%U;=P9pgV(%pan7XaqR#T@Q^qI75-<7bOris0mf|jX+w}h$h+HEy+rbWLg zD-K+X$ZMH8cq?j~IrdRqIe8nV9&F=1cdpfdX=@JMh$?8CIr=E+m^<~=fl$XOuMm_M zt+9OA2*@E5gq;iKf;XwC{rBI?m6&TcZr!<8^q~5Cj5Wp+y87PCYT4A9O>(=-{xLv7 z3}9vm8z&ToNHSr`%7+pbm5@Tq%44#W@K#ktT~k|E&%oHkEEo4rh#C;tvQ0(nCzyr( z{8wB-h)KvPsZ#UzKi`ZF z!~kZ7uyI0RZ-R>r2_H&WR6+_ZEB|H6=V?l)sB3EL>KPcDJkQEcBC}{#KxC7Q(jG0y z;m7n}m==MJ6AD8h`B1_ct$(4|gcMp<9;2k9u9@xnckH&SXJBk%R*RNE9b_{&kw^Z& zy3?2z5{1U#00ME9dT&~$P-%1qlf~w&ZtqWfe1T9THk3$ZdAa^NT&_^6jB4EcdK0!m z7kueDcAL|~;vIIvZB8X_I6j)yySU*zyaGZZ;*y#E|H;i~)5;Y zhP`L++Xr^r?%0{Tv$005wKB>or#uTRl0-5oq>3g!(Z^Y?(o72v=;p5o!i4aBC6!Ea zDySp{8Vp!);LcyTc)6-H1p)A-F9b*ce6>G(m1R^D6-qSd(fOZmZ~*Bo{#!Enn*RKZ z`LAh_E-UL{M~w3;d(}&9i7&t8W>D|QV)E`2w%SJ;PuA#>OOECH&%I#l>XGHlo?deEox^jQpp6iS2OEB&C7=QyTU$l#k57;XM z7yxf{J82tTCT|lSrhN+-fVBTZMK|+*TGE;}I{AMg2m&4$P>=*dqH_#+cSoyNiuOdU z#q;_lJEi`Qr%Q>gWE1bEVvgot62_cEK5?f{Rg=s&+2m79Ibf=(r&-gQ_qk9tMgAKf zE%#y;ySN!=TA!I`nRV0GyXR)hCX(pWvGRY#F9CHgxD{D9ci9%xlEDlc~f+RA9N~1HFEH;PB<3j=$wCdE0YtX1k zvz9-d^WTrR-r$RXGvH|k!4Op6TU>1bpEEv&s_?@5Z|hzShNJOhI-4(+t949(1OPH9 zpn?Vn1T3vwCYWG>4NmhGEnBq?kBE$ljtRuZ#U~^NvvctL7iCq?7t7VU6*XaK3>Jqc0N{_#L=u@orO_Eo z7MsK6@dZMW+E%>ufXEd}m0F|K=?zAc+2Yrqe*h6Cl=1(BRH(I3%fmO^rw^Y_dD-4B zw=dszLey5(3IjMXGca|y24ekH1kvnYx7|@hgC;H7bm-Ef&wwE##!Q$pQ!0pOiCH6N zi@Ek3)Q8=@aVC{L0sMZX6imVvA+ z0OYyy$imR@=c6FhK1bA9;&Vk=)oRLv zz0m5UDur66)EkVgH?iG8$}X?Z?}zGRq}F7%z{jfV;|+~X%`L5M9i3ghef)C(|U&ifXo4tx1|uhc>J0LWkM` zc!epK>J5)#nN>#qe(Kd=iw+QBLO&T;VU3A(HaX;_FMIhbUiBvw>~-EDvHQkvU!6$L zvRc7x+rvPK3=uG#pWGRr39EG+Ne!Vl7{dD!eKEIZTxRB2wi7zzb6>za-R04GoIyOZUf=FXybcR_oeRVKI}chjuG}gVJC@m zn(&Y433xvBLh41m{A-;R_X>?qs04*TVNeUG71Rc52X%lt%`jk#5L*JA$C~BXX-Yps z>F3qbwL5}NUG96}p?Z1F6HgV)-Mq<@p8S-j_DLZa|15=<0b#Pk$|(N`Xuw2wAngC# zg%Eb{oZH0y7kiHyN&kmJFo_T%lwe)+|1Ub0xRO?KZf_ymLcT@B?fLx}{CgBEWuLRcsh;%Cd%PJM8ybGmPp^p404D8x*zHmf{5+hE6Bq`El^4_uZ zY8ezmibd}w^uHeGFLw6l`uz|7)lWga zv>jou`2nc=X!e)?53tvKr^>V>$Zm#%kNt-dl4SY1=qv#O^v|TofzJIg{x|Jg#?{BD z#@*k2!8sVB0rpvzWrY3sr7=ar%u3)T2ifuZAg^u`{$>@<7nkMZi_4r7m(zff+u(V^ zpKCfQnu#Z64y(Zz??^rfeI=jdqv;D$ZOwhR%su!0_&Vrq50e}dWw>3;--2HGx5;&9 zyV-4Gvp0&$K;pk9Ad<)wDvi!yvOowNg(WbOs;wnbnags8QswTuh0E#o3*Y|g z-RAVLc!!x-SUI@3;XJ$ox)2nB#u16-=JuGFvbBvhns4&+KTZ$LifBc=;#QU``;}tl zdF6A3D|aw6gpCslLm>H30GKV885leRQ>CSS0EH!xX-rD)?d%;Koep4(;KccS4~I0T zPKZ{l+p^z#01Jge@j^)^o?^Pi%)Eq=OPOw#xfWP-@wKjdgB#s+T@Va~Bhgp_BdIkG zV(=s?gRd<bF(@Ni445%z%lYW|#O`wA9&ezFP=aRE54b7x6O=?DiHBg4f^=O{2Qnq0 zj4qj6VxCe;oGCM12xmsocO^(7IbC?%v!qwn&O-DM@lCbNl$TmQowYGDrtd^6tgA3+ zRrY41m?fv!UgI0&J}9*Kozbhr??YXc=TfD@bdhge*EkV*jvk&Xh=8$~S?&Dr)b4n?6`?QzZ zTvDsZ`Pb`xx3osTfINMhM|u|p(qG+48G`{vf73d`h!C)KMq0WeyC21aa1Wz;6wTwS z?C3UN9Bz8H;$B!cvn|-d4;R~cVmJ0;KMvwBj&2?&nKIU9w5*w(uXEXo)v8vR%Tc0E zjjGTJwb`UC31;9ji)0Qpk6FMi5^j)gQf|?1Gum|>hE7wLrQ6o)=yUaZ27F&`(7#$? z=!xMNiP0E~@tBCon7TQgPQ%@SxYvzvzx3LT%frBfbUGgTx`9XeOg#2%XWl2;NB7LRzLzJHkeu?&5j6Y)i73V*rWhN{7{QIlS;ft|5j^|l5Y-e9ospzckbZQ@#U3MYjOQOMWQ=ox=W=8kRCGVQCezVx9tL# zHji9!;^xfVg~zpPf7jZxbpUNpXp=@;blPT;t@v%uc2qbVHhb5m&+eny*oY5#9OJwq z-#cI#3R512Q4fuejoWws9^T`7dA~K@UaOYtgOg1C2=q6po=|ePE_jqqt!gR<7dkaa7)i4}t z^k*|$+0G6I8afk?L94xcRUGVAlmCmvp=?LLYcO6OJBFGt>u?V_8w_6O0>K>9_1rq2 z!1DN+-S7~H>Vq|V42cii_O#H!Y+o9-1ic>f;u8ns;6%2azV^nqe)H;&c=2@#8^sC5;-r&PF(>~NRQf4Cy_I=YuUd6j zGt5sLd>X|K`+_KK?8ZfT6QWVwqVC^4}b zCn0u|_XTm8CM#}p@&^+zPh*0%X;0N>-Kp!1rlUXVC(y$F(kwZ^O#fiREZbe%J z4B;IYxvmo!;yW!-bS1F$cdn~nGdQeupFM+hZVawnMIjsJP`1#2ji$}*pgsD&2xWXA zexNY)i~51agfIFB1xc3e?rwctUuxUF0uSKu16{5)%gu+Fnsy>=cX|M!KM$7F-&b5> z>eP!f+acS4kI&T6)zg-qkEXSvFl=~e!=zgih>JiC>Ej7ehL1bwy;iHafhzjUL$QSNI;i1 z$E*3A(ELt4QNRbz6|4e&Z;|#$sy@s(DpPiv#*tToTbA1;HAUKD$Is6##C zXn3^QCG)lVk~1y!`x@5WZg$Xg00|gz|I0tU5$vOY0j6Pg3EZ~42Le;XNq>#*M)}%OW^&jtUYu^2P z)VlZNUbdlaw6wY8&|li(esBEQV0j;!UZl-)eTWWt?$57--rsi-9rdDq)R|nt9iLy& zWj=zDaa~oMwuZp!0m6ppjGb{}&I>u+Q__3Sd*W=N`@g!!$Ny3H_=I7vKJE6(VfV^W zzukj8_YPyVqK7T)5I)W&K!|YSUxAAsPxM?hZx#v(_d6s5 z8QzC1U4}gIhH|f>jSjl#V}K#Xn1pGgVm4y_zQ>~5!YNx_g6t50Se+0aU4pA~Sz#wu z8LwRQ^0RVq#ECkxT=0JUaicvb9(tfW{hWsvw}k<=dk~8&j7@|el`W~{LcG~N-=iYetFpcpJD2ca06SToOtVeEaR(lL~r&PNIpxxVLuzGJdSzNJ z?RYzf!;PSvM5>gRL%f@h`8Ka^V$Q5daJoAo)HvKgDDQ?Rj7nM|3ea?P`-jn8;b z%*0OI#LwhPrLP#qi&1)_hO~O4zPckn2@^dBY%-$JHzTqn%d;|TopllL%L14oOlWU6 z_fRC)cDp{0bYpjSf7giv#~@xQ02?!6V~itJYbm6|t1n}j%6yixlk+t7o{fTs;4GXw z?5JaoJJIBmn!!xxHQxm+Xt6^^%v`wsMvHfNtqX*&D4A2?Q**x%A;L|iztQK1U;L?6 zQx#JOlIdt2x);3^{Q>&&KCe&|x`f)AYN3rH23@8sIB>&= z8!vu>gsG>E4!Y=}j{$}lVKz8CIzD4k_A$#Mo9uGPS8n;6!>_BB(;d&e_Q6-{-0lI7 zd9E=JJa+9x+!a?8Bcl_Oc#2SjV@>=R=W&s+Tr+ZHA*-nz=0ip@mgk#v&0+Je-Pk_T zExGa@>hGv!)b;t;zH7Ut#|LHx7Dl4`_woOqBLsK>WU#?cac%p!-i>TxGh5oq_KG|S zAYHbGW6~r{%4AK>)xZh{HG>4zqDP7hzUDu7>+!Ot+p+APFSi%i1y>RsE)Aw}s z$KKmjY;(DNf~jdJ!f$D!xwg7WCyRWFs8RhI7X^E3a6>pLV|vV3azuk=FoqH0w9`p9 zz4SB4FyjCa2p9y!Yvd9w4EJs41C;R>4Jsv#x2;;9p>kDq)jE-cBoOQ7n&IqgjPvI7 zXO`3dL#*u+`MZA#?=07>BRXuSA5(Vnolj1?Xlm6pJF=#jk4o8_@r*4P8iCl4pYK)o z(60>NMo-up-gkeg8$X0v@8(}8;Qvw`-hw<#m5Qc3)zdu_)#yb@8uSLYHtj*rcv^%FMvn_qju1lB$j<<<3j)9s0~kMHEKbJtbaD#7PN(;v>zU5_l4&1Y_1@?Z zvq7;L+B?HzF*rWs5-=f6^+_p6MO9iFl2(|t@hXfq+GL|lG|74sthUS_gn82}2QBt% zRrZ?Ux)uH=!AJe11OpkNfwbTtGYpUu4#kmB)*DKC6)BZbGM-e8F)@;OR&fZool(1) zwv$OGIdzd+=ec&4OFygbM^%0Ab0jq&tOg90>1Flst3p&Q@;cDhi$Sg?@ic?Gsrm9} zzA-bPhgLR&*@$=$MpUMhboxqJrr*l4%fe1ehW$TE@;|H%Ljnaie*AUybQ?ey9(cgX*A#8*w=$lN}Z)qBCY2V)iqmIVV=2o;tG4VDibR-iKi%_7z^YTa|NT&1zT@PVmhn@%o6-G0G^n5@hV zHw`rvQ9!0sRJt{nVtATFs!d8N(omP5;}&{lBK-MkEXnpV>mVB_up9(ccbR*?T8OQY zCR*!z6RS@ZUvu#_PexH+;$|_GI^&@$URfI+I_zy>ZmpR?WS?ckM;R#Q9!AtE>LnPu`AExlR zEC!iL1t8ERGF>v2Q|{Fa@4tIEFBY!XE~t#Bg~d<}86c$&0!Fg)=`-m@3W7*L!=Vk} zV4+G634~}%xoo+_)EHnQRyXPQUm5p*=5PIgz$oj{cdd%B&+vQwQt!9>bARvO-4`De z9vl`C8re62^;z9cBWMiT-kgkXq;2aa+V*ay?dTTT&Tggc>Mq)Mf3s(_@4JT{OOp@Oie8ns4f4-qjM02?nm2Nx$d55EAPYIW+}6pBR>sX{5&nJto) zM^39Ty+Ihwa71jPlK3(gUP4~MenY%Q{fJ zI`7UK2hW5!7XMnzHFAr)r+B1#rg^1znrD87cg*L|_sGw&-xGhQo#$J21~>;^fUh9e zP=6)hNkj^nMrF_eCi_8b4wtV*9f&p+2t{H@Dv`^S3bjhB(d&!`v&m|)+nf%!%j@y` zf=DM+w~C!3H;&#qcIWsLzdzORO!XW7FTM5? z)*rF_knP9q>rdfp5;gQ3(drqUUJ06%kjaUd9>4LK7-+JMK6l!to`LjGL0vup9UZarF%H^dqdy@A6JaRPh7)TbVV}W@JcpIQhLypo9Bvixs`R1{m>R%X zKbD4YG}dA7k*a3Z%`4i!k^@H_ee7F*YP~O9`1t#0|J^&=yd4{J&Lx*#*7Wd4J?8$~ z-kY7_w5Mx=L`FYw|6hG$%~De?c)s(W^PwO2gpcVv^#?xcQ|{q8uI}nD-YW52x8QFR zg-j&bDEdWxGZ+4BpterSo*c^vo3tWLMaTm72pV%7jvCRlHqd@RrJMt2?C-krbQHLFgF6=}M zVQ1n9yNvkHJ&-imjWogT6KR7zU{BHodmUr~_NG|EK8Gc@_Jv{v`yD=s)&)>9;X;ZU zTtq2^iz$_G38e@wrBuOX)bemSwJKaey#-fNE5lV(6maz@;ZY5gb-0$|57$wv!1dI7 za09hE+&EE=;U>75A_lh{)T?kSWdm-bR)X6p@^AxCJiw~(AdLVXVo3lG(-IzG1$dOT;W1jl<8*^3mA4F954ocBy{i-;fJ3I1N=f*;8&sqzY#9@oiYi35C!;?6vJO6 z9sVZS@DIs_e@PGgN6JP=br_`&fv_e*ut%8C1Q4m!=NrOWh|&(T4xtO8A_&obrMrJ( z{RoC420{;f*5R=Vp({Q|Fk%S<5F2s$LKuWNNI+a+4&tF6@r5x+fC3~GrXvxmkXV?D zFVTP`!dN8j!YU47b9{|)d^2oX(Z!6~jm3m5ksOmqA#8z^m_RCF2c+)xst;j1r0vz* zg>X31;|CdpgOL$8$RzB6%s4<6;SglSS7a0R!*|$2c40T<=<6O5!jZ^@-{gMDvB+ab z7(zZC$p@T>`~(IC;8YYOXix}FMqvVnB5)dt5)3E?XQDX4K?%43B?$>i!Nn*|-k=Oz zi0{cOlqIAn2bZHfp+p6E5*3LQD#7)rOqft*<*^R7SR-!cV(>~%LuM=u;9b-tMyLhv zqc$-`9e59Q*O)lGfX`5$IH3W2g@(ipjo?c(Ca!3*@`d3G;(})Ib2KNxXaPS(OAbB> ztcxIijMkC>v;hg)N*d7a5c*~}HFm{!Ff|gAOmxCabe7bk3-HnPmsF{p0z~Mxsr7f4 z06BU{I?xmJ=q2ez?}q_>z=gh&N%R8``b%ao0Nfb3nc}d0nSFX9I~~H(fypLx!mty7 z5&y)9O`M&4jCc;Cgq|3kV;uw04`Y|1+%{o2#-B_ADVRvi$0Xu=Ouo#!V#|hh12QUQ zm_}@c>B1(Madv*{<19E7v-u3y9Pl4tE(ICpQIKH%a<1yI zSV}yOWxq$l4nr?^1Iv+UV+Gz})~hog1y*zP{oQ}#NV-75{o@& z`nr7A`r zbhsrc!0pqS9TQf>-7!=9Cai+{W2rybrOkT<-0?`*29L3fC&FfUiZMJBj=^(0@Pc>- zFDbHkwLGhP*hWszv1MHyCZ2BmhF|>tJAG_$_KFes6J+7-W#j8H!}^Q~yWk&C<6q)3 zyrWRyKcP9^AE)=SrL`^;Ke*X2?ecYlSAhLvh0uIBq_*)Z<2R*Xe!W&Fxr{*y)j5bmy+7+!XxI4V1B znQ%8@1rK2p?ji5MOV~g6$l>4&&oLT(Z7t5*pN$iCr(JLFF)^0#wZV!UL<=JDIp=Rd z$S5L2oJ53$-x87Wv7c99k%$djcjFl6t2Hhi<7Ok&w}X5`q=|(@hWt$44@cP&<3CSA zc$>(#{>mPgkFyzVzwU>~PL$8+T5M6P_bZU?R>C6Bmexs|iP9w|qwTGf;teB#gwfwyxO= z-V<+YMN)7ezP1~`U`_n7B>@|)NFWIs)tq1wVjZlxq_lUwGAY|hc&$=m1Svm}$Xf4m z6eN>q5=jghB$hf7M>C11l_dDfcLgiPNzzs2{&?m|3Z_Xa*+tUG9+G~Yd4iF1B$J#a zS!5*1u9c{BsCB$^slGaSI!&?*Ug94q1b`G3r zk;*5ER85cy>gxV^Q*%BqNbSKLxos1nAF2OBAJRY?M_5gaiheGEijK5UF_1PYX7Z7W zjhxQHCw}bvJS@yH3Ik@=iRpE{KjwM5uvy%_44XM6hGFv>3s~P6RyKj9O=5OaSl$fg zH#A-{J! zg71#uz7u%v6y7_7*UsUw8#o7$7lhzoNC*k-A)_KV!a_wxXh;to1!17RXDAL6u|3D9 zu#p-LBEm&-cu0&8xe*~KVwCn0wUMACQWW(H4ZX(4-XOcTsEG_!k)tq5WJZOws8JUU zlA=Xibf}IVpJPB&j7W(IF)<@PR)oifw%(yUc9g}5j=0boH(KIDWBiCt0L=-aDIqlX z?+e6v*Z?Ws3&Mb#1ygP$T-X!1QU(z0KW-Cc!X5Ac*B|gK0(`u(pDte}{$2P10ibLI z7AFlsYzyH$93p~;M?|t;5H)d95sg^(8{%tXcaVrA_79RPd-S6zC}G2X z8bN?{%EJc10GpJLEkXipQvr4e8L&%**dr9c{wEdT8~pqi!0Hyl0Q{nA{O0-rf9M(D zuSWe+HMoO+2orD*52zN8@Ly~;SO5ls2w;T`kPrYeCO~mpO-HS)RU>NpKngvycfbgJ zFcJ0Kphh%R);z4R@`NA2fj)483nSnLA11)xp2PdtHcDUtlJ7jR1?khf&lm5E=Vr3l zgB;{>1O+&R;>|Ch1Z8}M3RE#RPVx6K2t&~DIpVYNOz7Zp{1bXO8Hth`7iNF$C}#3wyS2$Df!(u<^^_ee$%DGVbO zPo(i5>9?1|3mFnHneaxI1R)zf$dO>kg)j0XB;>;n1rjm};g2E-1;q$Ji3CR}0#Wvc zQ1Jofq=nsTkQ5a{E!A6bujKV*5r(Odi{9qvClWANw0MCu0iOZW|5r1BE!=Xqg-+ z!s$lG@R(w@+eWV<;*`8hV!#zjalJ8eKBi2in8bu{=L5De^E{@U;kL1GJEnrwwz2X# zrjp6Fv2i%2ioLeI<84eeV{K#SYD^7FZR6l)OfA!l;v_KY>?+#O*LoF|>yz|#F2(p0 zi;BM9pQyZ_4cq|K7=PwW(Kp%>1_AV*E|Ui@rTOG5+>d^c}g5@poA+`p*1DnR8B|@5*V6f5>LhcV{rlqH_j)PrhRO zQ>Kc(H%Br4IXgw)mzNm7myx1JE@FH<3q>D}e~hn-e9@1@JjT~VyXc4F9OJ7aT=avn zjq$ZnE$}cn?RVR0v;CXCnK3x@p7e4IDfF@oy|j-Rh8S&O7-qDFVT93khEYb77{(Z_ zWEf{O5nyOB01`0_kkJwV6k<4FpydG1i1~ntRscLF762An1%OQ~0bH~c0FRghZ_sXd zOI!jfv>&U{pQpdUb= z>}fHd+x6rw+%7|jJJA-X^*S^!W+bcYXU3X~JAp#n_< zs3h7#6`BrEO|*j=Gy|ZPXb*L0D%2BgpaC^#B$_}I8bLGB3|df!R-!qyp^4B=w15sY z0X`B<;S<^npNXCD1#N{+VmEZ5ZP1-@u6jVLp_kYQeP}K86PsZGt%E^g3k;$4FidQP z5wr$IiA^wu4#GI`HB6xWFiCs~Q|JavlOEp-pmVOA<1 zX2U$xr_I)lp4f}}7JFkK>TmYNezYL=#{snDIGCftU%^~6owtWMw-sMENWI)&S)Gq~frj^krp#m}C6R;ls%7o934j>vjt zbwf{zduZ8kFFgtFqi4eX^z?XudW8q6mw1SJjYp^#cywodg2(VUl?PAYDXJKrE_!l2 zLr;lkCwgi;hZm_Ic%`WC@#?XD;a8okQ?>ECoo`Y#@pjWw;2nA@yt}if!F%|S77HKY zW9nZ%DOwnx?rib!89t}l;0t_7wZd05QBCnRzM-eZUyGgr|DdJDKWVA(ZMJu*-_TOx zKRa7i{Fm=kWBiXFR5$!s^o;na>B*>XcJ!VAq^DrWN=(R4Aq{ycv}XZwQW*FW7kr2Z z-V_dA#BWe0;d3#>C222%m?UqAPKsWMI-~*x(t!$@8V-`9cS2dZ)JPYF9&$y_`_^8EHsK#_Kn#DD0u%dXKJik19J_tnE-~({5HOPmpMGmYE zxv+JOJlOhv0}5apkssTHoY-dM#fZ0&PSE*U-;O5 zGPNfye+68VZzlMEETFxM`wVb;x&!v|#UH`7zptprKI0jbDai^x-6%n1`Az3L`W+%b zVjwmoLs=d=1Y!ZKezvHgnB-0Gbm!#ru-B@j+B`@68Pk+#n>jZ-!GVMr0VQD=rRP5p z`uv>q`X=yvhyE=csVxb}nqNf*QtjJbQI`o3Voq#9t6k-gHmG zE_t{=+;|&GD~q;VMOLqkEa=0H%n76|nu^{Q(|m^&yiom6#@qx2%u zB_oX{kq`H=Sdy}-HXv4m4AwV6^sd z)SCdcZ%}yzE0`0}L5CEq5X}9bE~O?*_(_WW{505XjX*~q1Bd=vX=ytQSUxRCKp@Nw zNX9K`*xoOkfPzGT1W1xQI|Z4z6lT^8_}3>d&NakD786-F8R4m+EUb*z zN=FDXYNfJi298irsYo#SY%Dm))kmgp;6gY=-NAxj+e4*6Mi)dH|;p@kpV*+KmxMEoPPR%i-`=~ zwgNlqHDscmR-xuz2^QaW7n5HzNqzfOzSes%{d#deI(W#yz3vD{#$NG51mT2Hh@uil zBZ*ELK^Ef@1yx{Q4g>Z(qWv8$#{^ZbpP(X=r%)ih5>~-#SOaTe9UKYkMH~Vv2415k z%%E81#BQ#>weh|yB+U6F)?Q)aK&%rR9Y#oFc|l^$Z7X5M4}vhtcbHs5#U;j=sUdtA zN)WQR8ht=9&?hxRH+1cT1rx+gLd(!4BP2fXe|O5)A{HLl?Q9No&0&RVjdgux0?Ey7 zhYi)~+F_>?L+;ZCh(|`Uyr@cJppNQAPuu^QTV$h9(!-2UefO;pg`c?QNC3cxV$Wzl zB@M41wx6j~w_;2jeSB4yazRngAQVG#y}WNde9?J&Dy|^Ov_$Vs5LByX?4^wo&NM1c z0`o#oftU^qoXtU>SZsc(A7;YEBV;e=3=Jk#Y(tn4vGgj2YE7+| z0tiEl!A$|sSdO)}!b$VgqQhx^zZ`;^pjlSmcDTAmA=H!mM}Q>olQXx=?rXia*fx?P7cO*v_2NnWr(p0NT5X$TO zcS~_e(fb$gh z5|P^uc^E(zyy}}-WY~t&8_1(?U@ZTm`l$Z($y_QB(Ek5-h(;ejJm}+ZUrpfMF9LXzXJ8-v8y6c@!%Oq0Ko0AicSo>Ss*j=YH&ndk%AiQo^(aOn>-H&a zT|5L~3fDI^rQF&{osRMY<@_^TW`gkwO%&~BAdo^!#%tJ54c{U}v8fxA$B}d#ZQQ2x zrblyxLjdKVB&Z>K{$XaLMu_RE((-y`V?I<DQ#}`c!0r)z;^3M+XArkQJTpzi52K&Yj$8HdA1SSHB47(|!=n!g?-C*WfLRwcOfT`U6P0s2KY!F&5n8yZm^>Op$j zSLk3L@4}qDXhU(w{b=jxiBBiKOI^Jj{R{wb$)u&o&3gk)H+87Q$@SHQ;yJ87iL2Wl zO|s!uzHtpyHTRTZV8;V5!_+~0qKaK0IM7L~zmriMLYn|Z6`!DH<~~5qS7=3wfEj`a zh4WDlqCHLj!A30*!)8QGYYS!qB&KN@@pDfKaTaO8hDkD@8kx*-o);w>P*+rli4YFO zM=ib6%K}%kH>mVfx$%F0xA1cpns8aXm?~tcPyseSTZ&)Y5%s_%$)Qu z21p^0Djmr#G4M%~^GhN2Ga)ta-Y_2j?k!s5^XB75Ea}Z8fj3)*+=2Xx{vV^0)ay!c z1zDOjz<~G~b}N@y^+t4#@U#srWx8k5A> zE~?2MY5)zB*UK#?UEYlBZkvt*@JV4tcNj^{qwfY4vkH@-Zh$9*Exk6-3RXPoa8kKZ ztym7Mf@n%)H(jzUaaT-~VmZtVX(1j97qM-osfqaLJljfPjNdQ>Y@|=X!BF7<=kr0q z0nS1o8q%DTB<+`p#L(mQyc%b4eo`d^ zF*0IgUu+SX*YcstH!f^8TLG})?O$D4L85<_BJk9%fc&3mQBpY z%eX`%y_|Nmbcc5Y)v0iR^C({MXxg^_FWP0Z8(mhVU^<)Sy!=%pn5S$p4{_Pga!bEl z;O=Hb`1OYW{rS^$IP8DSYcc!jWv;8od-*>6yrpb`D5=3@ z(0r_rliF*Qf#3hslsDw^Ih~y8bsEDESKdDl_ee3=+$QaD*5}~X+0G=WMpP-o>%Km= zC>+sgl`@&-t=-YLYGeXbb)|Ipxd`vj8X%Zs80q*@>QY(3hW=sI@N-n_NU)2aKSCU2#H8*zVgwrLbPFW;Y-fS^&BxvTlfQ zW09>=XASM(P?Ohmc?gHj3u)&-sz~IC&>?{iL0eqrv1f;SiwB+BzX4IS)D|?>{(AqD zrz!e_-sz287)u63ME>mU=g&tB5q?WL&jbYUtTSG9$ky}hKW1e?}KFNQwrE)`TX8!&*O)x+$gd> z!6{C%L4i>aB4lhUHEvpJ+G<14HDgKK$9Cgmn>miJ5cQk+CNwy{1c@nGzdpUhv8HR& z$7e)$rqazDkM%mmmO3rJvJ--{J3$2}5(22?`p_1+{h-EGyHco%HBX)h`jQ)i(+*L* z%1^fpqB2uYBJG&@l0uymZ}S^8Ub2YvEMgP9dK-jV5n}|g2@2k;@!?8#s%MULD^O0w z5>SA4S|tDlOhB0i6V91hv18xxpbD2nkrcz@$&C;B{zkOfgD;n_pXUFcAl9_wX)2{Z zpw-u375m$pUx7=kLbSGv;^setw|6OdJgS6WhCK48UnR&E9!}j^4D03*Rf)Q=t*W}w zv;Yx@z!vGq0$zVL3zzOwk#;QK3sfe5XVgaNz80E!dhL^F8T`kAyIdwF;3mwpL@PYt zhpy1Th+2NANjz^VjTkzdr(oU-YM)m7wnmU8tlEhg+G-u>yHU^@Ksb-EO z#NZ7U+nFH*j#C5UoW@oZXGs>zs~y_FmLZ{Fl#LyFvZ@r7ARvdKEndiT>1!%h8ZCt- z0&3h6rYaCB($gsqiLkLeB(fqU3MiprvzQ6OA496pQAp8|S&gD&@OhjVh&qg+Uh0Fa ztZSPh~f{YoZ98Mx#DIp5Htkxb$3ew+V-Ae0%870O5uzA=bE6@zEqsCO1Tp9j~4fL!9r$fA{{= z8a4x{^eYhoF@aaL5sD7BgMeD~%s?bU$PgApG$CEO!Dc~(*_=^4gKb^R!Kl8Q z+(v_lClEt%w^+q-yv7Ca=mYL;kl4ZV9Gf-2L7^zGu?6dZM&#?ygmgWtzVt)i#sFy9 zJODChHVYwjFf>bf;09PKax3jvUaYs04Mx`lW@PbN3V`V2Wm()h!gFZ1K`@>KAaz;a zKW4DNq|Zy;XH6w^#KbEifEvd!JEdpSgz``sslG7}1VgZgzzJ=+cPQ-p$&lSUdjaru zB?^RLC;d2qnAEgq4(;~^V8HRG*dJIK>j7+vOFV=t@)UkHug0{&^z!1=F4rIoC%P$x zQQ8~QTNMFQNe_wzBB_Eu0nUj5DrcDPdjtu277q!Vom`__)NZaJgSGWxuyX8dpHpNi z)f~S&=2fm5E~olFiV??AeH$}yk6=DFZ0`-=0^q)q%ap~M1xX{ob@Oba$pwPlX3cZN50748eck=^0 z52XbUE6 z5uF5t7wPqbhu37Av(x4edqDx+!8O8%2Qlw(7KU$1z4*j&$n)U=wZ9a9_3Z#TVUUU97f()90 zXwhJojDq3tjyR94r&fAC4Dvpvb>GYdZsKKAEZ4=nsKbio?qx=4zW&@Sca{`)C~TpI z?51EYYa6a;#M5w7!F3VuSstuTO$#-QlISSRF~?ZSzt*OC=% z5^7~Yq|Yp8Z@UybM|}HpqInn6D>%u=yvAHGaN*~{vsoVdp~xLcuepCAo_SuH6u~iw zX+DeF#V4$`k#GriY)Yk!Gl@MdU(W#&;eMP$m=SOqES0M|su1eTZh_Q8ET^fcnWlMJ z=3uh-7E-CKxWzKCE9Vfwqhrl6^MQ$<%GcpQ}n2ZWT4 z$%^FOGLCeb#0{7*Q9T)mHIIs^Mr;ExOPjczlQq8}g51-$`#15*tn#o8?CAqav9^;x zTv)3Vq?tbSn-b%i&DSo=x^a$sbnVT8;Y!z62dS4h4f^^zmcrH)11OQL|GMGlm_o_A zgtEbl0;DM*#t$zxD2IDZd>`)EqhDz2$2mVM zrn!1+#by>1KxYQNmtY zyidkqp3MThkfv&iYlDNFdA9<^hSo2vA*s-&DhY^TVGvX+PN|~xsD8|9?MG_ zFELo$*t!XrHUype<#W$+edB#KWy1bEHrny38s+^gD9%F4Q1oC-p6Hc9r#emCgyJ71fK?E}{@!S6;C1O(uW0)sFP-)rTgSW`OfU8=44e{O z-z)*^;m;p*VtiV_>rt7p6TQZ4n?wsh)(q>BofNI--IC>^1x(TA=Rco}{uR7c?WI@V z<3?^T8%?5mh9u}@hB*Hq2wfOVIVFfv95<a^_t0v<}RxkCc9`ncg>?CUvbQpZJC#5q$jRTi@jGBFE zt(gOlEHKn>1L}$cm-g*-jOW8^Pj7OcG-3(kJcm@}2(iIVBsaa?ggg-l`e}7tdk=YZ zj4LDhr`t_|R{{~21McZ)SmY*(YZ?LKnuL!Yga(c}igWW`CP|R|hLdJb)HSHoE`Xu` z>jDERD$@Zy%B^xUVKKq@Tv<2;4Ekn6%$|DtF=x82o7osiz%~WoWHo zoG<6W9|?-SrT8F|z8XP9N8CDt+j~o!r&CKOFmqa&x$$nE^@T(lCtuhIg9I*J>N-e*B$HS;RaIA# zq+7bMrb5K_Y*(#zi7T$O|M9Pm*P31J)C@-e!@<^Y9-6ANKx=u<<9smfDb!}6qFhpTSFBBlV zssL;z4gsX2Ftnjy%|tzC9e8)m2YFVp(QE0{x9O=i{*2u8a^s$-JG*laW}jp)=E5iTu@_(IZFIaOV#r@1wXyWFz_szNV<)2Y8mzpUBuqVyP+N5Oy)Go~2RE zz&73*Z1fv@`f!RU!aAiZs{t|wHyut{2Y|rQ0&C>7d(sbw=d?@%CmQ?3N%|IV6BI%v zvH)9S)HovetpWTOyVg~7|oH0+RVVyvwaE`_~Z zQvkpmPL=p`1d46sgTQrb=3Wpp+9q3zs(vY_Uos&3xrw60O4t}ObR+7uOtJ5eg zrRk|pR9vs}pG>MnYecbX^`U}_={glqt=hXCRJIQG7YXeaBCKswghij?mO7p1EX~yY z)vqtW1wFEsUK{QQq890aW39b$&5>|U_owaLE}U2^?s@y1g!{36C18?8m3Z!{Upjh(d| zRs6DCl!}v@uMq&M9!RM7wnLP|*}P_)OMsvCA=@)XiStIuxGD@tVqDcG6SKqQp)#%rXv$k#}bIv6?vNZF;utcwH?ad89BV@`kW5FikxWVmi?8mxT- zAf^MSw{Zr72XrF9KauQXeC*838@&ssAk{GVXT&G0<^LLlu~Xkhgd`NDN&`$B0n^vs zIoE5QLa+p?&@irXJ*yEt$iJSii+=>7it9NiZ)rtuN<#_~j)4^orsyRjvXVmx_5@>l zDss^OoqVHaX3*4wJXzRzRtJn>G9!(qm0#~9@j{(P1H>z7 zS|jGB%+l(dLZ9O3EL+ND4ZsH5CKVgmzVPrV*?Yi`y|#)-)}xYcUqB#u*vxtOEGUey z&oGun{g?{1ANMaP2^riA!63l^kTa@&g|pm*iuQvE1UNY0Y6-Y>Gt@(j_+hbCi_tnT zX|5;FK5(-SF{Go{Lp*n18QKttTJT|zq0YSeCWH#Tz7^ooYJyp&G)Q#b_0oHM=$FbPpgl_W6Wlw#ZSwW zE0KW=Y`tp5U+4tRLQ(o_+y2~Q4Pz4v z;~}FS{k4$be;LVnfJ_HpvG^FX6Awa^JqPoc4}(NU%MA4gXWq@GPUv|w@(clJqJa%g zwOAbm?S$B!bmLvml1TH9{c~lS5j{l@M`P?&CX_jX{huNz*xRv>D#1f?*o=l-9sL5T zVi-qoOUA8+${1$5Z9u;!RLAI`Kgq)$^lc+KIF*i?Ynwp7vwbz*Sr>=yy5MJu#dEv( zF78p7YQnkJiYSCXcZt84vN5vA9d4nKEYOy3I{!K>I-nWDM(xWb_X&b z!YoS;UIXmD8EwB6Ob{*}kmRd_Cy0RLb(y-DFW=9`U!ZR2AY>a|M_YSTwMSKZRJG`^W6k^ttzJ4y@k5Iwqm~l=krA$A+%~&el4i`4YN~7} zD|UQv!Q`PkbimTKA~ej+&;DxXqbWdA*QdW=gHlO$+@vJy#!nUCZj~?lV4cT9U__L*pf0;ZB1aTc4f1p5oj-sWmLAo}g0Uipr>Q zqB0NvpK6a*T}R}m`h@u2!gahEBTPS=&dfFl3Dl>VOmIHEeIVXiJuH?t@E|nmUgk{vn-M^Zuu;a|59_!JPxR`$ulK;B^{@{an$Y7yS4~bLS(p~(x0x&KLcE+V`f0g9{3;TC#oy_7o&8Zf-=;G{U+q${&?&s>t?y- zz^A2)ZHT|0hcUymTex6C=tGz&8Wvx(YUr=0tKCk+LsyY}-hMQ`SWuIq(~{e&1}V`s zVR?n}= zW&t=*a=M7Jk-`jb!=n=(r#_?=_@}(Mcj~X{ zAQS+@-z;eylTxn3j)b>7+ zg5@Yaogge@(GQZ!Dz)bLe_~w~psk|Zgeh7wBh`j+^_;?3dMlPPss9SGFS;`u>aM3-Akv4^XZ3;-LN}eS081 zu}emubEE7Z#al@SXz>#iaKZ#ApFz7xdbrd|{SZn^aAg$ECrR?{A3~ZDBim+r%^BEA zh?O@?z?DpW-R-<$jAtzdwRn7WRA)S_(UU1_t)F>b-t9=>=VMn2Z57==0+t=ZMra_R z>Co>Z&QGvd|Ch?vIot`Rq>f5N%y=^v19Xt1u&V%hoZRNB+$~j zWCILKkS@s-yhtlFHF!5L_PztnU-|5a%NG#2=G9!T2mJySb{{`221R41*yrC9r647z zRw!MnL@4;u3Rip{T%&;mh)a>HJyrp6eU!1|$wG_FH~%vGk{Y}>hIiRE5=ut$E!Cg} zhtw))v=vnw-heZkwOq;Q zN2$=*Vl8utL=yK3h837|pOSb0mN(H>7}4S%up+^>H^=t9tCZUnn(1(sn~q$RWH%&( z-t$19*Ps3ucT*FJBJCa4D}l>jCdTxovpRM13^E zM7j*PHDz8IW{$5>y6=V54aMnMta+vF-VzmRC3XO)s699|Vfz zLy`W02aEu7Qk-^gzZ$kLcdO_zX75v2`HsUCnKG@_AKT%muYhv0?_`KGmZdIL?V6xsyEtW4nSXhWMvD zkJfB5pxx0%CK0q#!fG7Ts$rs8c5{6(Vb%WIdtP0KJFyP?xW_MihhTe}1tuQnAr67j z>vR{#>ndAL+!(~flPWLHRPBqWghefWl1mLXw||o|aiCG@gCQx%`KRyr&vnP29VFaf z63jW~vl`haQ5c^m#NJ#l#dau!JPc>@;Kc8OY|rZV>8#}$(e)RR|ED12`{q-K zyT?(&58%TWSG^c-f7)=ckn1Osl!P^7Z;K2^9^PKxd65&}sF2edQKG(ZBTS_EqQP?b zi!Hs#!<9_%3*iK4v4jJ_Q#W{1sw$~<4xBCm`kuv@j>oiS z#L5p*LZq>o@wd3z6A)nR znlI{yMzA76T;KOSOd zRZl*vPnWEK1~)kNcBwxfeiVg4d*QCagKdU18t6|?KQHp?PyE<6`2*9OZzup>`Q%R^ zO>C$({7IdzH)pT-n#)`z+<<^fB2WLGSA~&b^k*|pvv&XowKi(#{dF?MOgp%39Zk>| zZ!lf+hf^x0z1hZ+yN&m^Skfd~S2UoWK>Dchv9eNlJH zicyHlLq_zou0nR~V|7IH8d57hmu~y6Rq^AOQh26NP%<22*w78H^$55xwOf{EZAelE z{zg=*(2w%>B@rh$i_be2?mPB@@s;%A>Nw(&B_tpF&BeCz#Wf^%ePTl&4}>3Zq3Kr)p&<*+kZUojqdelmv#>|w!a?}zeQ!pdcTi6;##1!MK9QCxY?o&l0mP#yu9NcvV>r-#TWvi^c70bP8^Lfk~;!$z6gAy zC1Y*Z<|GkI87y@-^=EnH(a({XE<1N1}eys>i-*c>u| zK|qkVf@z0}8Gh*MxID0RlT&vmgEF+X`U4IAlyL>rg3_v1wivaclQL6?g4~}pNoSn! zvw)8`%no?yfYUo5_?h^c_aoE=0GA8UfO4r8duT^G$hca{Kp5AF;q~i)9``U-v2FcX zc_8+JG11yqh$xz;i)uF+c~MmTLrn_TqGf#NeUeTQ3k{>;OlPsx5r(w;B#?2(76Fyk#fv4;mmKDI)C-vr;RaQqd8dyK_zsT zfbJ<#FW1p#-0jLzyZe5T7pv9pJ1O!*u&zs|!DnLX+ zyY&K^@*GcsfHr)YZ@AAC(d(CpjQ<2v&1NTaMe9jng+jjLo2#3kzTSF z_bGpq50?lT&i9FGUV!s)5h^54&G)uf(CLi( zrD6dJD>#IMh6|L#z%0EtI!#{Bg|`}OqelL~z`^aCk9Un`%)wtvQN-wpr&%G!1LQ4M&IZ8)}bGak+bp2T% z8PDoc1K@WuY!6n>?z!=%JMsdmiJap_?IbH%x~TH!y_$QQW=!poi*PQt-|Y;Xpa!dqWlC(T!B<&0?ffT9Asnnq!5rEmC?; zM7U;q_yd*5xo$rcX^0rG2u^GXt z_J@Ov7xJ(x5Cyi)&UL+91EO?$kASw};wwrN~hy4`-mREeJQyH0nyQ`8kWW0i)_B?O(f3w1@>PD8i9TfX$YJ2k{OCwG&_n|kio2P8O!NSPJ!@e zHTfE-j&P7MFYZlDhz(V-R4+N>)d`p(N{w0t!PQ6 zuyAbhRDrujI@*9WAej>)LlwPMVaKsIp3pK1eu3kNLUA<>(=rCR_S~~`8g?~R<*9)~ z>K9|db0{4R#m}Kkj%#vB_KM24*V?WSv>e4|f6^_u*0w+AnYsZUsrnRppB#`#kf$Qb z*Jb+eIzP9T>;WhV6DwxDuEn&lWB%IZCez-$zB~6z&vrjBf)oi~lJj0rAt>4VcG~sJ zx5V;lTBiH3KKM}^PRzVqFvLy*y%1%e3N!)$owVj;>j_kjvzDiFo%t)s*tFJwBKp1a z;vX9iC782Eub8r9H6n$SSwn7o98jkwafFrLsRO2o#X7&9p2Qu|;#O5}UlExW)nKr( z3S5u*0u*>`TqHHhVhodqdY8vJ)ACa}=*WPv^~HRFw|{(Twj;hwVQekNhP1c-Y}_!E zb)}BJ9ul*hGQg}gk`psyE%Sr~8D)HANm7#VmEJ{GaB^O;>b?Eaz;^3Q_TDJ&akMg! zh>w@iiE6w)E?G5Y@Z(=mp-NhFe{>_78O?5UJsops4c&88F!c0C{pFyoIQC{>YKiUAeU|MFt`Z*ZsLan1GFg` zxeH|c4e{v|W@MqT;8GsPi|s1Xy^bvvWIgj;!3Qw3J#XqnkmqOa!--qELL(X;Il0$c zYwT6csZGM&iM-*hH?oVNnFnHpTb@qF!&y_Ga|GRSW+rnhAzW=Lx856h(lCO60u2z0 zUu}0X7`Ln9*#HCceiZ(zcwX}!(MEr*<94)hJGCq>{90<<5Qp{a3%t&^45wF`6g;Gy znDVc;2qaf2KRZ5J!)mQ;PSZQZh{mc(;)CMwRJcUsTS~5JXG-4)BT(S#jL15id<<5l zDt`4{dcN4d?w`JIg}jZ$3fMMKkj;!uLcnBlL9SC9(0jQ@8~FWzE~|}l61d6h>71o3 zI`?N{?~s^_8F{%IQtOQ9P!yoprO*NK23_u5-=OS`VxE54HCW1*Bnm!oBcY30+pL=;Ah?WWZmR#T%-o51@=C>8(beP9TDMzc3oh8A$VRbHLMb zEfd`Vv82%Q<8CC8;TH)R8KSOay>x=g-pTc;4+@H>s(A`^NItohtmm|J69T2Hauvqk zq_@@E^=KI}?~#h9qXZM=OQ2_Xbb->66fKbZgE0%fJ$}BcYMk5;w7+w0c)xPgPX%)o zkuGBUs_Z(YWFG2wE-V#`GJNI)_ET0a@G?zx?n0z*VijjmXdcU;wiSvYZ@N#l?LQYf zRIGwqIDR`$izF?SQv-^vj1%eO^RPK-yZ5y-?COxkdf*^)$uKHFH*Hv#r(h4Y`cRV zG3{vInSj{-p;d~W?0#^!BDmeZC509ES^9S>-%b(uwLtM#4N@*#17QRS&DdJjkRXs~ zwX3|r~r#a?kv2bJ`hK6mKph^=MgZC=ycK zzx$!&lqS1W{I}huYbXU+SxN6Y&MqY+WrC8TjE|M9OV|2cfFZ+~zdwT+$wuP7bgla{_;>!p=X@TV z5*YLU-y&?ZgyYbZjbJjlB$Pa`)8_jrNo6X@NmMD0p?7ht^M7U;0K+gIW`4^=Ql*pR zmRCvZ$&kr|$(whk7K{;$7);+&n)vdjg{DaK-7AXk>b<-4houq<44^oo3dc&x+;x>x zScjEY!1`0R2sye8oE#KA+(aXj+xX|d(Tem$x-3bZ4^xmczgOUAnlY%wC?|5OVXH1s zGVvL}v!4+pEIm=H%|ymDZsU1fdt}PBQY8nQ zt61dkk!A=I+PoQNa`T0pn9pN2`D!KUqli1#*L!XXfWFmI%ubZPV7w#7*S>V4?r({o zEthe$$CJ#s^^NRCycIkX$vrXiW9tcwY8NM|749@8r&cIeA8woUUfQ@DjX%^{?_HPe zmi-{hpd~a4`^@LJ)8?~+a5ThhzF0Pb>GF`0wx4Y($dVN+QY6VZ57xFH40(iy-&hig z$oAQ@UTtRTDOL0TBgyhte75YRi1LrWl72Z={Mx_P&cpvZ;RWgc9M%_`eUs&hpGUAl zl?EOhXd3q2Z`@@;rIEWMv>GIuC#8{9fged!6$2qEl$YYuV+d(k%rd+W2ingX+Md-? z?-$0d8ESS$-w0uZ9K>_Nyk`cH^1Jqnn$Axk%q)Q7(rI(|NrdW|ECxkE@pvE>A+ZgHL|$r zy{67HQ<{RK%ZTTq_+J+!)p~E-zNTCD$ZhwZ33KZDguMvLem8P%#iXC@SwzvlMi%|x zyS;8^FJZIEVNDlc@Fj8E^yWI3>&)J5Z8LbY&h@n7!LU~P4x9OFY^uWV)D9M?gms-3 zN-M&z{iE2(DY68RgDjsF>mJUj>t#(&HD*iijwdbOIL~3~>axKBooh!5pxNnolh-p- zq+Z#RRa(N4tSW29iqryBX*o=9>*=o{SN6!3ld+ooRhV99v<9pD;k`Z-T_g{mUv|n} zCdD>gdVNb)l+)9zd70f3?Ed|idy5nY@$ckdl7<2hJX#T4Hy_{0eEl+n8Gf?1_;|sm zXR*^Gv&~nRJq~pSts`2VlH+10#zl@-uj*O}$a2wCsr5Y77C`UUR+EvEcbWl$afUFmYGv(DwF(&|^@UT^j|8y5L51PPM5ebTe!P zTl-?e=vqW+uqwnAY4Xszs2~G)Yqkw9{V4fS{VAq&!?CR~e?aMaRmA*A*)J6?N*VPu za7u}gDy)YknWSo_>dnURq-Ml_yViz1EA|u%EIYZ7n)fgN%CdB2DfQ#O8jr^;e;t2V z#pmO;BkL%O1hV2!pHNSg99yHtoVEL?I6{F6%E`?*mmx*bKJBBo6I=xv3cF_L)WnjY zqV&6Spf&b`^gZ4xlM{y>wUn7M}kjl_X`7)eJw=Qo{ zT!n{?F9_D9;)$3%y}Uzxr-Dx}WS$}gI-J~5y)!$T(#<-+!b>L@7yt#2Dv1r|q}lny z%~mQfF;i~Jmq8a$f;PMpXXTylouphhkDR7bKl?QNObx3&<5T~-9f4n`>1#Wuzq_z5 zQ>MTai{vT~2v)?yU`p?aoFEVR8Ob5?Geq_45wIQa$5sl{M_ir6T#jpB64tXQiG8Fd zfHEIQ8}oTc2Vu4GWq8r zjpf^t$DJc!#@y@(&7>Sjjb3 zJQNG&K#;60tqUPSzl$M682Kp?O}@&@X?=DUU4yAbRX8AvA|pEraCtCm&5yO`x8R9x z3-@kyf7(+P?x^lyo$pGKVcDCWz44=pssgn&qms$kT1mlRFP?BJGY;ma8FvR>b}oRz zXRQ>NshyyE(eY@2`{Yw@{)I_>ldSpKGXO5x#V#riiKJrW5=&huAj_a$uc)$}Y&Qqt z4oOG8=6i0Cxmh1=RQVf4J}SnU%~XxxfMWwO>~SjoC0=a*b4 z38B$)hsNFXbFZ8!@g!?hjMtEcme)9X1V+n>Vl*cyG=n-AaSPfx{6kAiG|T_G1vJuR zljHOyXgZ*h8`F-i+Z^UeXguw0oT5KPG(eJAkO!oCpR+s(7tKU#h&Vx5HCzh0cIYkHL@BC4MQ$9a% z!7mlkxl9i#W?f|6WP9gGHy5S-k0sl^}cV%t932oj+<{VsQJ3 zi+_08PHEn0>g7KfS?B*4ykbpJekS_ZLm*pnohIVJqx2&V!Wg3dy{VG!qAs-|$A06V zhV?DN-j)TO7HShioH7G7VxglIbvR);Sfoe?V*HA1<=L18Wr+Z>^PzVw2JbqoeOAMR zar2=BZUJGsW@mzwnV8#G^AI>he$%}0c}L5x_#Fi$4ouA)_EY)Y(q1(3mqK)`2$4|G z)K2rfCIOY3rX&|ix8INHtTB0=m_Oj|a$H$H=nYh<5O#AV27Co&AT;onZGA;k*41%$ zgkwgZd~|8af+E&wMl(};S(@_iH0msi?8uo?CRJ6}dd$xTWI(l&R!`YKwoV@qadw54O(#C%uRj+%WOkb zQcm|CDMjP~0bU|yAue`##G0a?Hh_SQ*q3^DDy2S;nAyn=Wc-Zfht_hiFxG~BUq za3WA}7zQ!6AFeIS_xghllUU7F(EqMwV1Bv}*55CFf`sS?B9k4&Uu#bq*fbkXGC#VJ zMI%Q)pD^Y8%8?bM=S$r#xxKS{&YiTox}_qk98huSlUzlfk!cO+ zv>X{v9M&p{&$=m6Sq+WrD|AFd0=BHe+o_?m* z|C1f8kB6V!gy?@k0#^n-k;%*jS1K4e`sNLopQ1&2WAT#=?em(aU)LqXJQfYmG*o5M zR8!Gg4#?3=bI_@)lu zN6-K2WTbrJtA8KlakRFx_W43amr1h~{c*qXCyJWURt$8E&zsq(yRvR(Czq~Zz|?pF zAQiE@NTmcDT`}4e^%MUl{%-wlTHo3{6e35Lhoi$2?(rKWt0-oXBK3F4Nq3IWZxpNH z+8nJz#dfpFmf}s{M{NWCrl{q^&njk>W`&AdcWE*^r&*FJt_)UmLX4erXVJ`_&`KW4 ziz#vt3PJ=-@gfH;?5=;5>HmacI%$+tGL(wUw$Opnv6>%?Z>HsWPLzD6~l@$;ZhgDv2l;*$X@ftxu2)lgzBhN9r*QS%4x7 z&5U7)HcY^W9=-tfNFY5{TN6NKtA5q}7`ACcFa|w{{-Q$zc_|1nhDb~_hC$4RaAYzp zRs7uCezT1#Z9|LOes1ghuI-Cg^}kWE*PVwR^=IYfzcN^8ltVjh1-Z6nZOOt5wnKTU z!Ur+>%8NoLtC|D98v6?se7G6+-`IsnB_kLjl)O{({Wrsydj@&Xn_@^Z~c=7|0No$g}$ws7cl` zH;ySUteV-^Idk!>8s){+bNYP7#tzDM+qu=A#!m8f+u79=`5nFi=oXrIGEM7*%xi3B z^4vU)r_orbD|Nw3;PXT91$>CVO{LWiW>183mVSJ*&Z)1|2=u;shpu-ylYEvg*O^wT zQ(4YgQeF2VoF-tuq~3MT$f50?qA3Ku&f<*9#X;F!vq8$twf&K_tmb^sWbnpJq~7eB zIq|}dQm*mwM)#uq+wnHnboni|6oHr3LJi#bpPZ_JdDW0Pn5sEHIBrA{Tk(=ZbnuQn zN2~J+6N2Xd(j2--{PMap@#Tej48yU;-3y9tsjz0FGUMQ!lx(`@$y11x0|(o8WOl}e zE0`as72;fE{k>59Aw0F{WHARt0b1~T7y8`5vIvGTl}~_5mLLDYR4V+J88e~I{IQoS za8uTO)|FNZBDCY+hS3DRlF45YPHSG!4~un%U)jTYw`;a1?}6ewy^az`$(MyP&JB_^ zWjgx)t)F#rc*(48^M%!YajBcc;vr6uEKPCdR(FFwBq!(!20!>U93f$wdwGNIc6cqj zz#cH<@TW6)1J2CHtMonYupvh<^A54Gld{v!(H#o z#sTE@UgvMRN*c_bvAAO!*SlWT-6}q z4|%GUmzT|LB~!9?{oY#9P2S@SX}wBYtcSAO64m(>Z?FTC!#+Py)iF~Q_m=z{8Zvo+ z9e355s&o|^cX^S8k(;JZbg20;U?Bd1s$qXfUfDfU*W`hXUeAuK9(zT*x85w$GBs{u znT0*uQ@+|gtUwoAuztV*mzG&GB~3%dSM6b{*{AXJxFTfq>D&X6Pu(;rChS$0T;huj z&K++xU0pIg>|OlIK4g+_82;UZ^C{XQsqoO=n56IunE|Nu&%&~2wE)_tXMx#y~yG}`qHf8sA{xQN>p9oN4o85GSk~AG|_q{ z>!eOKd|@YI-)PiGgUXStDE3p1dl00uW?)uc?w#Cs@8qz?bZ*iQ4m-7gB;HY})y*!E zeaz6>>-0Ll;&XD=Tpq9~*?)DKU78Y?a=&y~M$9O})N9Sx8CTM-SU z*En{E7ZkFG&O$8>H^TvIu4NfDYnUf5<)8>+UKV9<)?mg+61UzsRxc{l7HQ{dTUTEQ}I;6a&&;V^<=$G<8F&~mMC&`q=%#v{6_~)br85&>sllW zlbI%_zrC#s`?{k|-Bf!^%1}b^QIXAXqT3N5s z0eshI%UgtfE&JLnb1b(v<4e@YL$%zJSI_ldQA2S->v~m_o>Z3^qlHzxT7zqduij<~q+Evrz_}WpA z`h3YhEJ>~v^8b=H&`Lq!`%vwDxBRc*)RWa-t=j6#Pjj(VPBvGS<&eNLi&Lk9MVx}O z=<6Y7<7DiWp&;#c!2!WbsC#Qh{+a@!NGa#oRsA79HA4AV>5;_KPdcC<65x+e%42*s zV;`Msr_Ru(86IAqh0FuT8ACR!i;g@Q$KNT2)MeZH1ZBR5vC&!TXXWV|^D zW3e=Lw5C*O(-8*%%_yH!m$e|Q{s{XfVu?lK&GiBG+Hs$q|JE~w@6=-60j{Hu)q+Y;H$lzXF|Vnz0-{$FT`0#NUgAs-gMq?jh3>c@h(^f7jmC$$#!795zwk=; zTa!~7vieb19omeaJLMo6>M2J$if&x|yIg3ctp=;!+Dx+vAz)QRP#&r8s?%{-4>UxG zdhg!Pj4sTeV4_*_2Ny@NrwrMu{89?~89w>kh2>m++ZeT~ZbN@U$e_ckUc(Bhlj!dnF25icn&);5nFg^JH6biR( z17(t?vnsu)EM;|x)8f!5vrDkQU{bR0m2Ip&S0gmmt4&%-WPVMjZ6H-ax{}!~vp8gO zoE;CWMU>jU@>8oUE@P$IXJ1~{1W5>c!7EkdSGIU0wPH7Ycxi+r)R^@W(?H#Xwf`Xe zNdVP=0$!8Si344SOlA16?4O_ z*o@kbt8#%a5g!6x{AUvRtn6HixD4w46(cs-iU-8go3J%GW&_s~s%x(^@YeRl{p`eR zw>JeaOvz17xl%q&nz`8vjR!J&Sb*V2%jrOq(N;<#X$Q!<1QowG?N*5mmf``aAe4=h zsjJw9JMGxaEEsZU>fJ(fCtH3TGJ8hXbE~D^6ZaCIfz=H8w(XjM0FW8?x_&B>yGMzq zLQ$rRRiwxb8+i6$bYgr^_QQ;YA@-t^?u{ck@GQI)-$2Obp@0XBYE_lIB3Se1%{P4v za+gXW#r-crO@f$xyZ*K%!FA2dVp?2n)duEoE8p8_O^tj6s_>W2mt+J^;aOm&uEl|2(W~QiRVT&daw zR(rt<$6*UzL(r5nYtQx{r;`-TdTYrIf@*-Qe&koiqm6%%Ua&I^OUb^j6*8|KkRTtH z!obyT3mRQiIBUvy-n*c!Hwnq9@IH&BA0hg1Y`TNRg7pX5}M%r3d`iEBY>j27zmZZ6@;UcVK`s&vH1< z%PXyyRYN&BhBH&9w_Y&%i@u+SJNvcV1UQvFxUs5O)GY8OoHmT|}d7j3kN^F7>FWx84NFx8CJCTKk-q!SQTuk zNvO{IzP#w8;-E_vr=pgUS4Tx18*otkSq%%WRb`#uhMgt*wKz!KpP!i>J-h*X?B<6k zMgQrdwvikufwoZR-Xnn}(6~*`Q=84;V4=B;2zbegYB~3}r4>s#EqMHEia1Bj(Hy1X zd8{&d-kKqgxDf#V9ZPGrg1rb+8CmI}NNbw<>cWy0!wyea>sr`u5@@PeLA=hx$WfPR zE>iL3Eb%&MeS0hDFC_?! zrqV%$rcEsVCy=ZV$%U-+5s5NhVeBzVQ>8R3AP<@%!wS@iEI{#Vj*=Gy^6T!A zr-CRx(}tIJ^W9)gN*ZETVF6Nqs1*?#Nr)$SB}@7pR)GskFZ=p1iEmILu^AhgETf0W zuKvEB9#1AB0Ipd;m6HY4^g?Y2qW2h-PQJ1^*p;e<+Kjh)&Q_q^)fE`{OJ1!_tO{sV z+&FfV?ZE+@Pa9`0-4nBiEmy5f^>WE+3DLwM($3Y4}K`0&!}_g|piYtbs&`$a}& zm`WW@`Zvz(tJd@X(-kCuWKUm5K=gAM=YZa{-nyYs-c)Hpj$&z6ASkV3sg)d|OqJ*Q zN#qp52mWltR|P{BpmTTU$iMjrxBIpK!V8w(<98bPm#QjEm}$S=>`hWuchtL;vbx?% zBjnKEslXgY`3ZpxUm2(jX0DOvl$BukrK>^muMcH|^)pg|+sBwXheT*-w>7}@(0Mu8 zKiu|Vi?_X}1_NVoRgYi~SVME^I8tPxa@mwj7B}$;FQrjPzr($tAhTO0BE&3b=2q^A%^A<~FQA5=>IB@vV?8Y$=4`Vpf~7RljK ztVAZRVps@el)&-ct2O$?A1toLTKg85(ehjblE}l&nubj_Bo!oOzl0d(CC|7WAVQuC zDJl9lELX-hJG?fnth-C56?n-$^P8u1dV8(43Sk5?a@QEafH7;(2hN_^4hGsYnpV;l z`@b+3s3Y3WR-(G-^ma(JTo{Sfy6WPCbwyK&R6oD!37Na{YK8|O!-qIPrrwq1nJp%r zq_Nnq7$wReSg=Bt8!I`4Yuk=7yN$C zTYlVKD%8~q23p}u{M(NTFra|Vtdw(G@3Z`svS%v6fHEkNvm}ptX|C#M2ae^bN9~o< zHg;=p#TjD(g*<^C8(nON7``--UlbO>s(xxjU{ghsrS5wlgk05vOjcs(%NCoh4mkvlywFZuco-_` z6Bx*%k7+($E|Yq^EBx9FKRox2+6YpsL&kh!(`}fwt1}LzsXRQxzc!Z8((xN(ZVwb& z0nV*l9T4oC3d%T<4lB==TX3ZJ7bQ5mOITJ#N`Ck6jG-VPi2B^$L3MOFidOUh{Oadq zDT>?JlvjMRkLbh1X8t?dsC_T`O1=pdp2((r;fV+Y2xDLPca1QnaqaH5m0a3JqT&DSpu*2$RjTD2sWogV?0r}_k`O4gs(7fINNUi4(U9|3 z@v|RN2I|KXPxa@%r18DEH~$Uxn)>sp?>+3x3HP}fxIeyZGAbf>%?u`apqlSyap?=PB7ReYqEZVI}#=`p&DafAAQ zQzn*~7f&&z9LU3M(RX185Tfb*5W1^_nlb*9Bn&-!cH&2`W9bSZtKe)|o*yDSFj+|0 z{blaz95fvNq`41fk9dX#RvBnZV#6*XIxCW{z9AMLtOsOjGM;4u{_LQ-_8qtoFZVY9x z1SKn0p~w5t?(E$S7xJE1pN>a!%WSUbx3guwHPFM&L!DUW{#oydkn3l z0t|Pz_#wWD+u>xua>awcLX469ml0x>Fb~1*ZQMtMgyX+MmT66RDMj>NXvWLv!%j0y zskL=BK9llCbee<1a3L8WMb(zaAUZ2LGwZ((LZ}R>nMEhbxHWgoaw-(3Gkw4Rdu!ivIa*TDylZ=8aMFA`r~ z0)iLa1T(9$xf=FGb_!A>n48}QJC2JZBA4~VTM;72a^P5S_D0Fit~!WmZHEfrHao@^ zaR6ddBjC83@H;TQji~Rc({ff1G)Ai9HVc7F>!#pH+!oF$%R2d z1oRXG_o^4%^rcJ9+I8mT)0VC2hm6@xMqkLIp=Cd7v74+qjkcLHTb@i^;couYcS zOqQ%c+0>9ZIO=^z)5~G@ehjr6^X@FMm@e?q2b7RMboGHv|N9lkVc#Ej{W2d6^B8TX@i4 zcnp#2w6i5ZZNa-U>-ZCMgH_PLXRDYvM}=UB>)~u+7^u1vxw!4J`e|kMUf|%j0S(i% zPWCQ+JV9}Ro#9Ng@;8jcBNfHy&wSAO`=&(skfDSk*Emg0Ds^LlL7B;+SYT934E z+$q&9ez$ObkJXZdy6y)T{p3!Gw)&CtOd#XTI!T30u{G5+vu3Og#OcSMEFuy4S)ieN ziCk{CiSb-5hT`5xno8!Hj#@)BnXgb^2GT%XCy%O~!KFIeiGsJf4=J~jpP0EC?*3+;ivA0MDyODm(n5kqOarpMB8y{S20Ps&Avec+*8LEV^l$hoViE%Mem8 z1gKUfp&%4sKsRP6t+(#NMubNC-Zg+R3tRMJiy>dF5mk!xQG8X8Q4c(@OS-X0$%9k+ zJgJ;pN>+NwvWlL`XcZNJ+e`d)ZftkhjP0BD_`xy>u4!#ipvj~MO|EM>NQW85<2CCJ zc^3To&2066K)*`-6kOezL+MNXoU?jp$-9>>*1todQk`C&6#sd1`X9%V3U~?ex3~vri(<7;Z+HZM^=4aYHJ9dZB zZ%uzQ!Y!h=!|J8n>Y#HyZ{NH^SYZrNyb3LgN`+EURW6cTVbNIRi@tr_H`x_!h`!8y+uOXL zu7Q$=sC)yK4wc6aiJjgNRh6AA=M2Uy&M1bfXtD3pBd}CNFCvE&X!MHqOPtQNw#igX z5wPL`QK8+qcI9`45WN^YYOsH5;I{?q#$i|zqioJa;<{^${nB*yz(o=ho>KfbeidR{ zuM@29rk3C-hykK)_yAb%G$=%W%>L_2lJ4mrU4sv428yI6b3%UK`{8=FjBmuhvK@I< z&eZ|J(wQZN$Jfac%5o~D?X}eo4TlwJ5Sd+$!iv}<6H#W(->twAR91njQ-%Jn4=w5H zYfzXqP+JR3fBNz#48tk3sP9y_U*rwtnh2>>NFY!U5O^IOGg1uz9O#TjRx}?%1Xrd#% z!nti38YTx(s!-kbpE9LHVm2;YELWH%q27nHvm~?RL=n}XaMN9WrAFwa2-C~wZFhhH z2YkMN@h^Wa<2nv}nYS6EEinvS2iXMh?E=@@Q+&r2R=hfX+MI>(*sMpF)q+@Eg{Gv> zP%P^*1>4@)xIwjlDH4OZDA@}Bi!*InqH80xxih$7BxV7tH-prxxg&~o344V{9hC^myyOQ@YLXrxf{<71GY%ppMh}@ zUqUz>28V5QmcNE!#34QiK{U!&dfd2fJvW9@J(x|hfGmxlqJ`Pv9%cy?WN0?!o{Ekrt|JM{Tk!)#>Mf)^wOVC z-pk5y>^^aZ5FKx5fKug%8jYOw@8$s?QtN-5w-uMFTuoULk{2H%#WK}Ip@~Vy{X!ti zbDjKCJ|u^KVLAm2$d}6s8;($@1OrsW{7ByX4@C3BCq7>QQ$Vc0S^hio2Amq5)&j7M zM~VAqTEZGV9D6Q_hM+lyc92py^b5oo!CVrCDkxn6Ddj`II~9Wf`A~CUf?%-12Z7B) z6s156k6W53$T=6Ckqze!t>GeWEDHieL&;#n)gyDyrCA#+P+)v~RaPZ)L|RaMvzjd1s6Rj>6sYM--8r%^T# zsisVmuB5E&LqA}9#gkrF{rLTd0m;S7mPzUwvz-Fu|3+yYM=|rVASw<2ZsdI>B2eE@ z&|!wsN1!4P#AM$L8Z}!=c0*lY&)`&W`ib)jT+(xV zk{@^qEq;m4-zr7Fcd<}p*2-Fo@4eV&Y(;^NGo!&z>f~6d_!DEUQI0qj@IIv64Sq6n z^GZwNW`jK~F|&gWIhg+Kj#E5Ombp!NeXr|AJ@Ucl`OR#9dRSQ>*Q(cdi)2i~OtBhy z#=!r#7NRA8@gv41G_y7#{CYjq-9wlm)*#LpWFJGKTuzNFsGh51hQXTmKx#Ehx!9?f zX~a^eir6ky;OrHtiub^pV3c5$cvZ`$JKgl*Il?&+D66B970jDcRXtp|uM9O~%>UZ% zpHAu)`+UG`%s(&iO56ldBB4uSPuCik+aE zfqHm6oMSOC?~^|kvw$wtuNxB-u!k1$meEEZs~QGY*mO$O{-5T)vrUCa^+J5KU@k&N z;x!sG^_st(F;_PJgy|`jLi2j#doPM-vldkkhh=M)1lr#+v!>Bif*s<{nyno!n@~%c zlQ0nOjSyWa#6F*KZzJmgXH-24=ML#s8&ssdf~51-!2-+dmeI$wDBSFp-pW@(5&JpEvK`X|U>i%>QW zSIsY*vcB!6%*@NFI8zZ>m;%rQafW{pMXeoj@D1$ ztM;%gEFDDktnl!MVAYv|U3Woh`husb-72ukt3P}cth%ycuH9*7{o-7KB*RtFY5BWt zD3zq}aJu%u<4e0!>@JO@O5$y%%LXK-#cPj}g+@8o&}K+zKDqr*h%U~!{P4~4!z0C6 zW|4-^Bk1Wgpiq0xq#Ja%IJLGOUodO^Zm=fKI{x(g&H0Y^_hrd&Yekk~i}FDaUhB6J zr)e!pjZ8?$*;uE(h=)H47V_%*yIZ~DZL{Ms{`Te2<>Bw{C}6cD+a}^QmCJE!;vAr# zmU4O=(?WPm1~e6Kd6W(IuuZ+@5W!LWgCAEuTb>pw!%|gclq@kY5K~KwGg=k$J3@iT z%<}UAW&K#u+hoDpcW8GB_8=Q-^DqA5=j#CS!m1Z(?ibv3K{G1|2jv}VmI8&4npCLG z6o5dFS!Pyw_=#JF&KfQ>QlXqiz;xvK<+9=*6t(?lD$BfA!wgLj*>YZDyGZB}yyx_+hQdA(Cjsxk&z7 ze*St`s)UEXaW{+qRzMdOkeF9`^s8jye&3z;z2BrmlH0-hl9!IYthW8%*QI&Ok#-ET znDh#JmUH3Q+_-8MptqpZrk6^l6%8Bf5Y#!HeMZ#P-)iA+o!(ks*FDf?soFBVrCzJ? z_;54iZ;ff(e%uU&QKAx42wxH$E7YJlm1V=JkTsScn^1I!G*w6GZTBF)q*qqpXSFVI zP-YFM;sEreY&!8~PJwjXNUH24%ncWUFt04OXe*c}L3>59oq4Gi9CK4$%9YGvra9fy8-d2^ zVtDE*%F^0)6YK*S3jLZx`RCYYrd&sO{QbZePP;ZVY6T8w$~6{086eu>A{;~?Y-@j!T#ogSztqL z)k@upM378kthM@k8bXpCeK8ALR&L9XnoX=kqrjVla6m&WC@)SjTgruIPuVP$sY1zU zUe1bZ)nYvY(mtM1*;T+^q&0AKB}mIqAN2aE>*(Nzni{0g;FF`|KJ8xQ-xXY~$XgPP zju|SBlF@gix!PQd4load8>Df9qV}POudASyJ^9F-Z`AVi?JiG6fR(suZDS~qyj1F} z9;A@PsVO`|B!ja=b!Z~6VTl%GS4((2M-5dhsV$c3kX7o{fkQPA%!%PGZ~Z-}Y)+5! z=J_j^LTs%b-{LKz*0yE`f5&)7Z8+Z9Ao_f;2t9YyY-X)`8B+=q6I~5i_A*+|{2uA2 z4>l_w`0=$9qTh?8KY%aj8tX1c_?0~{Hn<=Mx&YVHpJ-c2!^9aqxPG&s|3TeJBFUNN zvAvZw;}-&M4@56HpT(BJ_0zFXEAQ#U&5DP9K9-rlOlnVDzieZDtYU{Pw+Wg z*&2sks}RPTd1{FT|7zI@97$w0%E`wF=*v%J2?VT8+S<@;GkOL(CU)@GHIPc>X4GX} zOUX=MkFn^Mj@K;msDugHpt%-h%KM5|hNtLUyrX_?pQF~Q)W*C4^tAqS7}NqBQJFjE z?U%ubD$Y->B0aGvAt@gu5?>yQ?Ha7F@kPRhd?c&QpH3c|#Ykcf!^YWNex$CKkN7CC ze8IsgFi@4%>o@-5_oet7fENV^!>jq!b?6d;Tm ztT5}?_$3rap0O?47>))~TZ5y{R0+BjnZj^3Tcg8mfG@ zU#=j>zKhJqZ!Fi7p*{J&Jp#(YO)gBcDroC^~!F%V*E&wtwl(N0^sADcw;t%DAT zcJ|zD$q6FrY!Qb?-O+D~QFhpy>AYJh6a^vc`6qA)}La`N@ra;T(vbJf|CC=yr@wdVFR9vAtSv zw0V8zDpE&OZ&WDx?Yf{z!S7Mg1LfeNNUkcsT?KX4EULjJIYU;xs2b}0GpqE72waw1 zvxv3aS^u*W47>cfL!0FUH?-i8#V0VxZ$H7=i@~8bcNIgXB2F0!k4#BT-wB1Eo}OEH z+<*s}fRhzD$5WQL^7F^=sX!NVaX1ZDmk|VC+{OG)XZbk@-VIavwnlY8apOPX^O_`DOeqUZnc6zcb)Mdo!fDu>)%Zy}NF@>bSxmNrdHwqomfIc{V zu`ByS?T0=z$OyV!TO1$-Ikn*=sQHIf+ja%VxM!sZUBkvV$ zR?FX{Yk?-pSlJ2#b_QWkpRUU=4|}-P zmXKDcVyS{<`2lWL2;&Ha!jlTz+(N=+VuAI&7A1SsFZt2P%>!rb3T7Z!NQ+2YEBfQc zJ(%(G^~4-ER8H219Rg=r_bT|%`!%cHibrS4%@nzx5KTAa&40C&oVZOPBD9+t4ZgEB z0>-a{OV_aF&9oloOq>k4)M}Skn|Z`&RLa$LLzQ|-3j=*%leU;9GSab1EL+Z1C~}Nk zsY53uJZ0G(vhU;eJ_$NRw`EVDdaEsCEq^qo+u(~)ZMC+MqTI8I8ma1Ns1c{#zvaAJ zApGx8zuN}h3P;!-J8R6XU8`H5m&i7Q@(XC1$NJc_BXRstKhH_S`~8v_v2M9R`x zt-jwu@qGmW0kCRz%90svGfqc+)l+0&tN?es-Em-7$BvHO=ThIqPHwBXd1cwpEeMu1 z95BO6@W~IOWv@L?nHpx&=O@?$kMEkZ<&Q{xA~-1YBu1) zgKd@4)`8Z-o+%Jxk^mF2nYEWN>WLEnFsxHuSUICZV!?LJkpi-@lf7dTt)fpB&F<<1 z^xF5|r+8`?z#E|uBP2h2?+lu}VBfwkt4oA3$=N`_mRnx@AXNMmm&ug!S*G$_0zTvn zpVsgW%Gtv%CkYgEkgpI zmr+|&tEsxoZQuH~>{;1>2wAw0sX=xTs4Vz)|8OKINm zYApY~^)ff$$Z7rnbB zv&;OMt$_{mtbAiu?Dmv>15QcJAY@P zn#~n{HeN`m{O>;h!xmG6ejgS7JUU&D%HPL@i;q@- z&)>!MG02ba{Ncsd`z6&)P+3iz;R6BwHo1%w1h`-$>Dm*_3fHYOyb@}8dFF*I{OD?i zV$QyZ#O^rPQT3KIqu+Kz*I?g}*kJh51iX}217J>-dT5XED=*c|ww58juTT)aCd(-h zTIWnR9A>Jlb)1Xs0e!|&hFe?32)a3yfY+|swnZ5eN8^Kwt5b2=+N zR6Jux7D0$M_B_q^3>kHT^hipCQxJ?!#r`TNe)^A;s#$wuK)k?G2~c??+VmBX8;#pq zID&9j!e1S8*RcPl!?UA0mx1lW|G2)zG2T5ia}ErX(0P`ENBt=Ou?n6$Z5#^o*cVT8 zI9mH>jUd538(rEmJEy6ZQR>s#6{^G=130>DJ1gp$&fsNMgX?FEqBO>+8Fq$@60v4+b4&oPsi359Nu?i zxTSAmqzLSB9#0Zu{W_~Q7?$sk+h&=D>94~`huptF1TBkx4P|CEJQPdKE3gs<%8lYd zA+WIwYKCFw#tm1D3ea&&CG9&>(JjN+0z-Yf@MB@a1H+;&1(U8GKy&i zSUat^+vQ#R4pXx;|2%e9u2B40hxnOKAH=nTH8pm(5#Y!gNOD-DX3;?`WS^ANa7*9c@dU$TbEKOE!j7fkx?HX`y_Hs-EfiXJ4 zm^U|3oDY3G7Q)Go(k%B2Eboht1~$Z~ZHz`V-Fl2B{js~<_-m(+S~yfWq~b{7nfgVa zVEs7($8bW8>;{q1*(&2;`}eK%#HIr!)umNh!f$zfL&RC+vyAhttP=?J5lSdvtI}{} zuJR&_IKQ^&r+c%Jb5VC`#=PAb%_p(BTyJI9x~_>Ya`n}T{?i*-QUP}u`1xsY2ChF; z-}E|`q=mO_)pk8X^Ovu&kt<@e6VHvo&aii<`1bmfa5^LUD+;r6m7MA{=6P?*KqAvr zX0J5VV|yY}0cl*nA1j+N-49X*(t;VmdBfTKv@jS@x*zEHpWAI9hjw$T%U##7*IU!r z$k#K}#?s%_mmEGlH!A52; zt*`VjSV6gtHn-kq^|0hYY_Oiu49ZnOrLu{roTp$&Jk9B~f-Iuh&eYcB^SkS#yAE)x zo4^sza~1ZvhY%I(p4XqsVUM2t?mMJH_D3prnhJfxqoV*MO{9Q?qe0Xdaawh4OmzY{ z^6em}FJwRQ3x3V5+A9-;BRT)Z`~G^_UZuo43af2=uEaj?w?o8_z9H2&%^l4k9`%dQ zZY^3`I7AtcRs8_f3Cj@vyjJoTPIRRk`VX$k?Atr;sT|1~N>_4z_sq=bW)5x}94KA* z;M?K{sbm-3{1)UdYhY#9WxLLPEj|m3E%`hmEdSX%7?!vi zF(GU>XgB#W_W(seu2hM#u&4`uMuoTi+&`;biH^H z(NL9wZNVxoz&{rY(0AWwJ6$bK{s>)=A0cFbC>P1_ z3ZFl;`O2kJUi8v!2zV(1OR(E$I*fYE_6u0e!*%g6O3v9pfIXwx2hQxBfCIxBEw$2` z?z^9tf-)Q&#y<4x+M)G*q@Zc`P&im+@^$*l6-Zs!p;AMaL!gYNt_(jbtK^vL7#((* zg1oFpo#?+Dh+J;cmL0ku_wUS+|F(?E@X}zIBeo-zaj*QY7IW9*QBY318$C#|h9)hW zEXyWc`$so^PTqZM+N4}a@s$0{#Qf}5Nm|{{p_ZTP29nN+fw}#mb<#3=>vreH_j%GK zya6avIAZcOl|E-|I_b=rth_Ink&!$I3PUZ=HRg>iq5c2-UhZUa425d~fJ6er6&|De zQx_aS=9Hyj$p8aY_+A}63!&;C3l9T2k7Byk+4Zcz9`N}nY(Zm_(A!!;~gp znV&sLIPq|*oHqt=G?Ru@ze8(2w|Uxt%||I=d3o-w+Q{>~tX>-2qCgz|{(gkZnuzPA zC}r!h&pF<1D{Kr_AAhdEJ`b2VCJmH#Po6gE8& zl1qJD_hOm!VrXZ_T0HOCHJ5Z%M39;=V`5UpmeFQ3%gz%Sr{S zM1$={EB<6gmT+Jcelm&lUv#PLcOGdPuB>y4#`4gb8Gy5S%n?+S}BeJ?;@yaYMRF&bDqr?nrNrj9; zddICBn7=n2kmnnE>0GQ;rD)69d})W+ClCMEp`QAo54JyupZv8kMAp{!U7hvH{3{K+ zziZlZIIN}f?#&iD*}vubSPVVBHfH`?J$By9Jwpfhr84>0AoSGer8b_4bCVHm0s}#T zLdYZ*3x>cze|1c)5vr`xT(XBZA3c#L6tC|pLPGWBaB~WwT8+M06y1_aW<_nz#)%8V zs(?ucD=Ev~T6QqMsl5KdflM7auh5(uWyO!*TUW?>E0yPUyeWOl@v z+0WAn`}$2$la!ZEaHZ~~!uPQmWhi}cfL)T%Em=QeuC@Xkrl*mMs3mentr6LV{6jNf zz`(Gg6s}b82_B&eeZCK^$$XXcEzjAf0Rzderi9Stv2%dYOsU4iEznEr~Rij zG)CMBaqd1+KVU(a7qFFr5~$w`4Iqn{YQh=zl=o>%VB@nbLj?!c7HZ{=Yz_CrYnu%2 z-nPP8-%AAcrDIJmjg;Kwroz(toNB_EH|Rw>kpEU8_3fo{dL>GklxfTFfCq~rj^uaz z_WWITd*-BX1T0NBN26Da*~BK9o(N5Q#rPt!!5Joa^zOg%kxRXjOXZsPW%&gA(w#GW z0b;q9XW6KOu^}5GyXRxag2dPj1{UQ4qeRNAY`-_2dGvGkENykO)#&JJpS{D((&pq8 z-OyP84Tru~K>zUmJ;i;$iDfK&Jef6|Z}?FBJ8W3}`8jY=MZLz+|M3b;IaXD674QhN zaWUt!e1EBT=N*=MkI#pH5t}m{ebWh{j;T`oR1d!gksX;DyH3;0fBM(y6iPBzZCoiY zzV9iyFY%7=*u9<4VTug+=2C`OtI1@`WE{TO(MXl`0H#gfT`U*sMBK7@JYl|;!cnp* z=pI6uqJZ%AZ=Xf*<#+t}lr$fz3wyoK%<|)tGusd~rQdcJRct07oOjo4*&Z>Y1Kl5TyAGuA&yEfoHN>G-9s4Clo z{=u(&@HhbIXl_Ol*FKWUtk{dG^2;*fr?Ba3LUlFAYl4`QdA(CdnnDB1cJ54C>;p-A zHmEt&Y0`opFr<-;`U^-hk&K+er4?#|oUG&XEgP-H7t!s^Qji{@7 ztz1Q8%@gU-XvNn8d5sh9z6n;}LZ%y(T0z;3()bfUPMz+BC9K*wE##ExMDd2Um}uv4 z)LZ-Glv-TbT*l%Y*81Z}R7^_>rNYeqw&RN@M5njiOPa z;i>HYk{OB>ee0L{i@t4L^lih|CkJR+nysU0&g8pFk3v~NjL>32YvrdsR)Z=VpV=bY zQx>jx`>$?R`{f}t{0p@Hlh)^|*MugRSJPrOI0xD%c8HG;fTjKY{{ESbaz>g{M?@)A zd{3`oUAI#F`){Qphl4!0f3sW}`@~z&ymU@IuNq5UaonjWzGLDv(i`psb6IU&tN@!_ za?XE_Ua?o+cBdP2C*gYd)P>VhL#S2OSKEFUn>E^3;hNuT&+|<^r-nYCzxR~!?X^c! z$V>P>epvLHQS#bL$%<*Ot7~45k6(WW`mzEgf8FkSV&SflwI|*=;yp6=hziL{TG1}- zZC}uCpKs5rxeUiK3~W<|I!#l>CU;d@Mn9T-304O{z=Z>;R;O06=0k!a{cAWBx8yZV z%BP}5Jbb)vqIo5Q=TPu6*L2rugQ5&Rg># z>UsFo7J76+)K1JDW4?j=vAxCx8og_Qwtz&+K{1^(lond9X#ER&15k~huYha2iHl3tO)XOc`h3SF!N_=~4knOUe)h2@ty72|S=-)q`sDEaC;$>RD7 zOIo16Cf!+5`h%(s@>fSGM!h46@sFPYv=tr+B92JNRmTXAo3&1yuLWMx@ItTS7p4&3 z+rLir6j1WhMu|5qz5#Xfpf(`E=2V9GSEQ_}q~Kj%{{Fcgr4ZJR!3~B0(o;opRhh5t zIbs@;U6I+r{^h0c{NICUTSsFqBvtYk1Al4B41Dn-7>*7rXr8;GD-iFBi#`WU5w1T# zhsU&uxB_}HPnKybSzw^?$s@otL%glwl#Zcw2e(otgyVCTv;{9YD-rPW510+Ozq7r4 zV{pl=x5nq?D|$}-AIhi~QfITqjOHx@kJ++^dKzeEr6~oo=5|Xw=GFwx$CO$n&RKTD zGT9#_t))Sl@_Ir(=BX`I<8Y@2x_O_!-dNA1*~Fq~T*5%tG2&Dhyg)S#!@f?m^R=p@ z=5eidpQ<>JUPw*w&mDi%{SE0%2b$wQhh1MS`>JOD&&?Z;bESi#zct)$vjpcgbeK?= zfv1>T8l4FcR$SpTCiyAL+b6p!K~Rmm8jF^_Y6Cn4`>ZHGh0KbjRgs^D zSuKSlGU{Kn44p{L<0bsnjfP3WHaqrVRL!o z)2&%Lw4lI}TP+6tc2m3%AE9QU!o7cH_GWjrMVXN@v^&&aV$cZbbJ&`qn@ZN# z&#gn}V$Yd1;4RCohpW6-QJ?COmt&+mvDL%+EWqXI(dL!-79Z!c5YG+m9l^N@zm4dX z2n2F~?3eg@)wuzht)>@af<&+oYM)n|*EfOq&~BnvhSj*KCA8e8+E%k6WHzgUc`9ac zx@44a&(~U3_rZaVez#CaOee|{&+Eu3&1~^yc3?9(!7nIVi(l=0Iy+}YkMq`Yq>b(L zzEEtYwbKIODOCIv7wV1U;<~IzV2=~mF!ujaeBS`%3yrk;ei)?DBkGY2gdUROtM^YH zHz2f7Q!p+X1G(}a(U^vA4DkIkJcD;Hlfjj)ed0t_u565Wv!TyIfpWH#sZm~7J<%%y ztXMUt*JE@ao*s26UitWHfb|TbeOCEdCREa@L)DP#i@sigeBfIEb*||c!9pF_wAovr z?kz;V;#IPu9?IWQ=eFO1UAHSC#%*9@V)Ee(@Gx8Eu6NfNx5?_cTYTe*sfqwdIX7j= zv}V0(&Q{-;(D6RZO<6kLIDYS9O68gbpsqQgceZrQ51DElHB)|G8aS6G@hRx*V0hsN zVZ@m(aA&nU2alf;#3z-&MCvNj)T(WM!8Uhl*bFh@{v}O4Ib+du&@OvD#C#oWslix3 z9SE2_I(I`mkBOFdy zhWgZUdY~4{Y(RK&Vxk_3_@El`kteHmewST$dw)jnPT*eFJzB-NP@Z%2%4!|eJ={>^ z-n2hBG>~KMboU#CgGy16*}AKBb^=yxcLdAwe+|iREiJjyRHP6x7m}DEF{IIDeaL*w zZ2Ex&HCZ0?ppQ)G)lNm z@avF5W871*+}m#K@{)Ch-wa!8O1RTzY+coCd~Zclj130YBhi(u6U#(Dql~udqvchtQzFMX*sI;7HfX;Y^R^TecG~Jpz z?4Og@esn05{r}4ZuE3RD)}|k0aj+W1Rqma){HT=G&rGiDeux!Vj-ePk`^vPn{4yNz zJEX}L1*_S0SM+agn;l!Vl|L%RHLZ_4zEr`{J7UC}jcvF>vRjE%IP~p@UheI7vVU2Q zwoZ5wp;GGgRqS#P)S{*&j$$zhkkV`vv7cEV#*+uEsT&ALn6A5?cH&uZ&iE|J?eV!G ze`x;rxOi&J52>`S@%;G;{=HdUVdWgNY07FU%rEK-&ORsUjv9W}cVwM`)-b8}~_*FJ##D-f)B^d*}SO~mACT~m5})~qMS5~0q8 z85U2ku4+T*kpGuE_;w&PXs}H@yzbhzWyQ}+vifXMf)D|^BAL^qW-aQi4watEnVp=X z?MhvZF*B8%W&L&GNJDRpfwN*}tOaOyt#6m{6l{sXf$6QPKmgs|ug)GOZ#`pWRfnkA zC1jDRoIN{}MP}@ummK?S$0-NHD`NhNIvA+jQ)wDXpNG7hO zyYvB0FW%8)LA3hHPCQ7&JeZ3qffC~Qt$SQW@8&0>63&iz7X~4LRV~GL`kQP_0aG@yW>PKJ$Y&@poBBlQdqpXfV znoS2WdUTd!dAh9$RL!^lMA>PI-~`2v1O_irG-T&ZWA5K1Csh$%z;xuR20tPz$*$)q zor{nBeY|_cPsF%2vm{Kr<0(-eF82R9z3{j4p8`#2wWdd2F#F3AA+?#EEa8n{RLK75 zH5|{ycKOoWWE!PtIc5TRD1u?O4he@m1+%VrcG0PDF|)P _HdD`n5<@_AzZ{+=$) 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�e%nZC1!^pP?`Fe-tm;C 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 G0{{TFWc059 literal 0 HcmV?d00001 diff --git a/pr/395/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt b/pr/395/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt new file mode 100644 index 000000000..d7e9c149b --- /dev/null +++ b/pr/395/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt @@ -0,0 +1,98 @@ +// REUSE-IgnoreStart + +Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. +with Reserved Font Name < Fira >, + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/pr/395/docs/static.files/FiraSans-Medium-e1aa3f0a.woff2 b/pr/395/docs/static.files/FiraSans-Medium-e1aa3f0a.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..7a1e5fc548ef28137a32150b6aa50a568cd53d02 GIT binary patch literal 132780 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�|o|A<_G9#bJ&K$mXTSM@kZfZ^-ZYS)6EG~kHJ{! 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

TIWHPC<_9>em(Cvlhuf-BD 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 literal 0 HcmV?d00001 diff --git a/pr/395/docs/static.files/FiraSans-MediumItalic-ccf7e434.woff2 b/pr/395/docs/static.files/FiraSans-MediumItalic-ccf7e434.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2d08f9f7d45fda39315519c4d6ffce5b84454d70 GIT binary patch literal 140588 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�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

%kIaTch%S18n7|ogN{Ilm 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⪻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

(_VuT5-N!zd8q@AJ44ro;*^XcsH^-RBAu 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 literal 0 HcmV?d00001 diff --git a/pr/395/docs/static.files/FiraSans-Regular-0fe48ade.woff2 b/pr/395/docs/static.files/FiraSans-Regular-0fe48ade.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e766e06ccb0d457fcdc8d4428efb796c7772a497 GIT binary patch literal 129188 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

)Thpg9J)x+@B(n?maf9&#`PQNHtKX<@|CQ`> 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 N#!G)s;2JO>~QPMdMZ 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

xz5_8aF@(pQs@zgto+ItjS_s2^}<|M=lFQ2 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,qWUc(1+qXDfx7@=bp~~)(TqS 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 Psah$GwaNdR#Q^{S={DW} literal 0 HcmV?d00001 diff --git a/pr/395/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt b/pr/395/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt new file mode 100644 index 000000000..4b3edc29e --- /dev/null +++ b/pr/395/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt @@ -0,0 +1,103 @@ +// REUSE-IgnoreStart + +Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/), + +with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, +NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, +Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco, +NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic, +Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/pr/395/docs/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 b/pr/395/docs/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..462c34efcd9d6b70b42359ca1a1d9476efe43eeb GIT binary patch literal 44896 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 V7)~)yDZnpu49h_gZvJhn{{a>V-DCg& literal 0 HcmV?d00001 diff --git a/pr/395/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt b/pr/395/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt new file mode 100644 index 000000000..0d2941e14 --- /dev/null +++ b/pr/395/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt @@ -0,0 +1,97 @@ +// REUSE-IgnoreStart + +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/pr/395/docs/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 b/pr/395/docs/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..10b558e0b69a74b8329766fffbd5f64356c7230d GIT binary patch literal 52228 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 z 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 D0=ucW literal 0 HcmV?d00001 diff --git a/pr/395/docs/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 b/pr/395/docs/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..181a07f63bef8f18e42a5a57463e0e60cf8e8035 GIT binary patch literal 81540 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 literal 0 HcmV?d00001 diff --git a/pr/395/docs/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 b/pr/395/docs/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2ae08a7bedfed08cdfea76039c1bb1fa1d6cdf67 GIT binary patch literal 59716 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 tC40Gi`Mi4m<9BpxR%m?w=so8}_Gqj!BM1dG + +Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. +Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + + diff --git a/pr/395/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 b/pr/395/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..0263fc304226d90e224e53053855ad138303b70b GIT binary patch literal 76260 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 G0RRB5qhxRZ literal 0 HcmV?d00001 diff --git a/pr/395/docs/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 b/pr/395/docs/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..dd55f4e95ec9c29fb566d8617104afca31f0eeef GIT binary patch literal 80732 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 literal 0 HcmV?d00001 diff --git a/pr/395/docs/static.files/favicon-044be391.svg b/pr/395/docs/static.files/favicon-044be391.svg new file mode 100644 index 000000000..8b34b5119 --- /dev/null +++ b/pr/395/docs/static.files/favicon-044be391.svg @@ -0,0 +1,24 @@ + + + + + diff --git a/pr/395/docs/static.files/favicon-32x32-eab170b8.png b/pr/395/docs/static.files/favicon-32x32-eab170b8.png new file mode 100644 index 0000000000000000000000000000000000000000..0670c4dabb0f844105d193c3c9dbf66b687fc709 GIT binary patch literal 690 zcmV;j0!{siP)kdg00009a7bBm000DZ z000DZ0m8#+P5=M`WJyFpRCwB@)dP$zW)KD7Z)=-zZCj6VYTLGrpbF|nwS61aZXbf$ z=Ckd+cRNY`Omew8XLV-h&aZ*C*wYYP5oAcHeqdKOS>7m%U2JLaVvCKkyqoN*S8Rjp z)r?owBdKb42Xy<--|~Y_45l*CINjP{9Yd{VX*$pGsby{sF7v55Xns&v)MULpuKyD(DlrKYv@^v7xUv-8jp`UPh_jFG$b3xm>BNOaH!fS@9LRoAGTq1-|twmjch2{ua zA`f7H2+z-ib_rZ=X^ITcl2Z)6BN!#mK&Ch!B63a728hO=A!7tMJYoU$AU1{q!*0<&F@{BBR zT!(XCfJ0jTXPo6Tm3s-WlHcmBnaWvA6HA``*2Gi5IlfdZrJ!fYVv^;&$U(Mum`%09 zrVg{cAr@yj*EN(SnSb3XTA`x5^nz?Cty0){getNotDisplayedElem().appendChild(el.firstElementChild);}if(elemToDisplay===null){addClass(el,"hidden");showMain();return;}el.appendChild(elemToDisplay);hideMain();removeClass(el,"hidden");const mainHeading=elemToDisplay.querySelector(".main-heading");if(mainHeading&&window.searchState.rustdocToolbar){if(window.searchState.rustdocToolbar.parentElement){window.searchState.rustdocToolbar.parentElement.removeChild(window.searchState.rustdocToolbar,);}mainHeading.appendChild(window.searchState.rustdocToolbar);}}function browserSupportsHistoryApi(){return window.history&&typeof window.history.pushState==="function";}function preLoadCss(cssUrl){const link=document.createElement("link");link.href=cssUrl;link.rel="preload";link.as="style";document.getElementsByTagName("head")[0].appendChild(link);}(function(){const isHelpPage=window.location.pathname.endsWith("/help.html");function loadScript(url,errorCallback){const script=document.createElement("script");script.src=url;if(errorCallback!==undefined){script.onerror=errorCallback;}document.head.append(script);}onEachLazy(document.querySelectorAll(".settings-menu"),settingsMenu=>{settingsMenu.querySelector("a").onclick=event=>{if(event.ctrlKey||event.altKey||event.metaKey){return;}window.hideAllModals(false);addClass(settingsMenu,"rotate");event.preventDefault();loadScript(getVar("static-root-path")+getVar("settings-js"));setTimeout(()=>{const themes=getVar("themes").split(",");for(const theme of themes){if(theme!==""){preLoadCss(getVar("root-path")+theme+".css");}}},0);};});window.searchState={rustdocToolbar:document.querySelector("rustdoc-toolbar"),loadingText:"Loading search results...",inputElement:()=>{let el=document.getElementsByClassName("search-input")[0];if(!el){const out=nonnull(nonnull(window.searchState.outputElement()).parentElement);const hdr=document.createElement("div");hdr.className="main-heading search-results-main-heading";const params=window.searchState.getQueryStringParams();const autofocusParam=params.search===""?"autofocus":"";hdr.innerHTML=`

`;out.insertBefore(hdr,window.searchState.outputElement());el=document.getElementsByClassName("search-input")[0];}if(el instanceof HTMLInputElement){return el;}return null;},containerElement:()=>{let el=document.getElementById("search");if(!el){el=document.createElement("section");el.id="search";getNotDisplayedElem().appendChild(el);}return el;},outputElement:()=>{const container=window.searchState.containerElement();if(!container){return null;}let el=container.querySelector(".search-out");if(!el){el=document.createElement("div");el.className="search-out";container.appendChild(el);}return el;},title:document.title,titleBeforeSearch:document.title,timeout:null,currentTab:0,focusedByTab:[null,null,null],clearInputTimeout:()=>{if(window.searchState.timeout!==null){clearTimeout(window.searchState.timeout);window.searchState.timeout=null;}},isDisplayed:()=>{const container=window.searchState.containerElement();if(!container){return false;}return!!container.parentElement&&container.parentElement.id===ALTERNATIVE_DISPLAY_ID;},focus:()=>{const inputElement=window.searchState.inputElement();window.searchState.showResults();if(inputElement){inputElement.focus();requestAnimationFrame(()=>inputElement.focus());}},defocus:()=>{nonnull(window.searchState.inputElement()).blur();},toggle:()=>{if(window.searchState.isDisplayed()){window.searchState.defocus();window.searchState.hideResults();}else{window.searchState.focus();}},showResults:()=>{document.title=window.searchState.title;if(window.searchState.isDisplayed()){return;}const search=window.searchState.containerElement();switchDisplayedElement(search);const btn=document.querySelector("#search-button a");if(browserSupportsHistoryApi()&&btn instanceof HTMLAnchorElement&&window.searchState.getQueryStringParams().search===undefined){history.pushState(null,"",btn.href);}const btnLabel=document.querySelector("#search-button a span.label");if(btnLabel){btnLabel.innerHTML="Exit";}},removeQueryParameters:()=>{document.title=window.searchState.titleBeforeSearch;if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.hash);}},hideResults:()=>{switchDisplayedElement(null);window.searchState.removeQueryParameters();const btnLabel=document.querySelector("#search-button a span.label");if(btnLabel){btnLabel.innerHTML="Search";}},getQueryStringParams:()=>{const params={};window.location.search.substring(1).split("&").map(s=>{const pair=s.split("=").map(x=>x.replace(/\+/g," "));params[decodeURIComponent(pair[0])]=typeof pair[1]==="undefined"?null:decodeURIComponent(pair[1]);});return params;},setup:()=>{let searchLoaded=false;const search_input=window.searchState.inputElement();if(!search_input){return;}function sendSearchForm(){document.getElementsByClassName("search-form")[0].submit();}function loadSearch(){if(!searchLoaded){searchLoaded=true;window.rr_=data=>{window.searchIndex=data;};if(!window.StringdexOnload){window.StringdexOnload=[];}window.StringdexOnload.push(()=>{loadScript(getVar("static-root-path")+getVar("search-js"),sendSearchForm,);});loadScript(getVar("static-root-path")+getVar("stringdex-js"),sendSearchForm);loadScript(resourcePath("search.index/root",".js"),sendSearchForm);}}search_input.addEventListener("focus",()=>{loadSearch();});const btn=document.getElementById("search-button");if(btn){btn.onclick=event=>{if(event.ctrlKey||event.altKey||event.metaKey){return;}event.preventDefault();window.searchState.toggle();loadSearch();};}if(browserSupportsHistoryApi()){const previousTitle=document.title;window.addEventListener("popstate",e=>{const params=window.searchState.getQueryStringParams();document.title=previousTitle;const inputElement=window.searchState.inputElement();if(params.search!==undefined&&inputElement!==null){loadSearch();inputElement.value=params.search;e.preventDefault();window.searchState.showResults();if(params.search===""){window.searchState.focus();}}else{window.searchState.hideResults();}});}window.onpageshow=()=>{const inputElement=window.searchState.inputElement();const qSearch=window.searchState.getQueryStringParams().search;if(qSearch!==undefined&&inputElement!==null){if(inputElement.value===""){inputElement.value=qSearch;}window.searchState.showResults();if(qSearch===""){loadSearch();window.searchState.focus();}}else{window.searchState.hideResults();}};const params=window.searchState.getQueryStringParams();if(params.search!==undefined){window.searchState.setLoadingSearch();loadSearch();}},setLoadingSearch:()=>{const search=window.searchState.outputElement();nonnull(search).innerHTML="

"+window.searchState.loadingText+"

";window.searchState.showResults();},descShards:new Map(),loadDesc:async function({descShard,descIndex}){if(descShard.promise===null){descShard.promise=new Promise((resolve,reject)=>{descShard.resolve=resolve;const ds=descShard;const fname=`${ds.crate}-desc-${ds.shard}-`;const url=resourcePath(`search.desc/${descShard.crate}/${fname}`,".js",);loadScript(url,reject);});}const list=await descShard.promise;return list[descIndex];},loadedDescShard:function(crate,shard,data){this.descShards.get(crate)[shard].resolve(data.split("\n"));},};const toggleAllDocsId="toggle-all-docs";let savedHash="";function handleHashes(ev){if(ev!==null&&window.searchState.isDisplayed()&&ev.newURL){switchDisplayedElement(null);const hash=ev.newURL.slice(ev.newURL.indexOf("#")+1);if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.search+"#"+hash);}const elem=document.getElementById(hash);if(elem){elem.scrollIntoView();}}const pageId=window.location.hash.replace(/^#/,"");if(savedHash!==pageId){savedHash=pageId;if(pageId!==""){expandSection(pageId);}}if(savedHash.startsWith("impl-")){const splitAt=savedHash.indexOf("/");if(splitAt!==-1){const implId=savedHash.slice(0,splitAt);const assocId=savedHash.slice(splitAt+1);const implElems=document.querySelectorAll(`details > summary > section[id^="${implId}"]`,);onEachLazy(implElems,implElem=>{const numbered=/^(.+?)-([0-9]+)$/.exec(implElem.id);if(implElem.id!==implId&&(!numbered||numbered[1]!==implId)){return false;}return onEachLazy(implElem.parentElement.parentElement.querySelectorAll(`[id^="${assocId}"]`),item=>{const numbered=/^(.+?)-([0-9]+)$/.exec(item.id);if(item.id===assocId||(numbered&&numbered[1]===assocId)){openParentDetails(item);item.scrollIntoView();setTimeout(()=>{window.location.replace("#"+item.id);},0);return true;}},);});}}}function onHashChange(ev){hideSidebar();handleHashes(ev);}function openParentDetails(elem){while(elem){if(elem.tagName==="DETAILS"){elem.open=true;}elem=elem.parentElement;}}function expandSection(id){openParentDetails(document.getElementById(id));}function handleEscape(ev){window.searchState.clearInputTimeout();window.searchState.hideResults();ev.preventDefault();window.searchState.defocus();window.hideAllModals(true);}function handleShortcut(ev){const disableShortcuts=getSettingValue("disable-shortcuts")==="true";if(ev.ctrlKey||ev.altKey||ev.metaKey||disableShortcuts){return;}if(document.activeElement&&document.activeElement.tagName==="INPUT"&&document.activeElement.type!=="checkbox"&&document.activeElement.type!=="radio"){switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break;}}else{switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break;case"s":case"S":case"/":ev.preventDefault();window.searchState.focus();break;case"+":ev.preventDefault();expandAllDocs();break;case"-":ev.preventDefault();collapseAllDocs(false);break;case"_":ev.preventDefault();collapseAllDocs(true);break;case"?":showHelp();break;default:break;}}}document.addEventListener("keypress",handleShortcut);document.addEventListener("keydown",handleShortcut);function addSidebarItems(){if(!window.SIDEBAR_ITEMS){return;}const sidebar=document.getElementById("rustdoc-modnav");function block(shortty,id,longty){const filtered=window.SIDEBAR_ITEMS[shortty];if(!filtered){return;}const modpath=hasClass(document.querySelector(".rustdoc"),"mod")?"../":"";const h3=document.createElement("h3");h3.innerHTML=`
${longty}`;const ul=document.createElement("ul");ul.className="block "+shortty;for(const name of filtered){let path;if(shortty==="mod"){path=`${modpath}${name}/index.html`;}else{path=`${modpath}${shortty}.${name}.html`;}let current_page=document.location.href.toString();if(current_page.endsWith("/")){current_page+="index.html";}const link=document.createElement("a");link.href=path;link.textContent=name;const li=document.createElement("li");if(link.href===current_page){li.classList.add("current");}li.appendChild(link);ul.appendChild(li);}sidebar.appendChild(h3);sidebar.appendChild(ul);}if(sidebar){block("primitive","primitives","Primitive Types");block("mod","modules","Modules");block("macro","macros","Macros");block("struct","structs","Structs");block("enum","enums","Enums");block("constant","constants","Constants");block("static","static","Statics");block("trait","traits","Traits");block("fn","functions","Functions");block("type","types","Type Aliases");block("union","unions","Unions");block("foreigntype","foreign-types","Foreign Types");block("keyword","keywords","Keywords");block("attribute","attributes","Attributes");block("attr","attributes","Attribute Macros");block("derive","derives","Derive Macros");block("traitalias","trait-aliases","Trait Aliases");}}window.register_implementors=imp=>{const implementors=document.getElementById("implementors-list");const synthetic_implementors=document.getElementById("synthetic-implementors-list");const inlined_types=new Set();const TEXT_IDX=0;const SYNTHETIC_IDX=1;const TYPES_IDX=2;if(synthetic_implementors){onEachLazy(synthetic_implementors.getElementsByClassName("impl"),el=>{const aliases=el.getAttribute("data-aliases");if(!aliases){return;}aliases.split(",").forEach(alias=>{inlined_types.add(alias);});});}let currentNbImpls=implementors.getElementsByClassName("impl").length;const traitName=document.querySelector(".main-heading h1 > .trait").textContent;const baseIdName="impl-"+traitName+"-";const libs=Object.getOwnPropertyNames(imp);const script=document.querySelector("script[data-ignore-extern-crates]");const ignoreExternCrates=new Set((script?script.getAttribute("data-ignore-extern-crates"):"").split(","),);for(const lib of libs){if(lib===window.currentCrate||ignoreExternCrates.has(lib)){continue;}const structs=imp[lib];struct_loop:for(const struct of structs){const list=struct[SYNTHETIC_IDX]?synthetic_implementors:implementors;if(struct[SYNTHETIC_IDX]){for(const struct_type of struct[TYPES_IDX]){if(inlined_types.has(struct_type)){continue struct_loop;}inlined_types.add(struct_type);}}const code=document.createElement("h3");code.innerHTML=struct[TEXT_IDX];addClass(code,"code-header");onEachLazy(code.getElementsByTagName("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href);}});const currentId=baseIdName+currentNbImpls;const anchor=document.createElement("a");anchor.href="#"+currentId;addClass(anchor,"anchor");const display=document.createElement("div");display.id=currentId;addClass(display,"impl");display.appendChild(anchor);display.appendChild(code);list.appendChild(display);currentNbImpls+=1;}}};if(window.pending_implementors){window.register_implementors(window.pending_implementors);}window.register_type_impls=imp=>{if(!imp||!imp[window.currentCrate]){return;}window.pending_type_impls=undefined;const idMap=new Map();let implementations=document.getElementById("implementations-list");let trait_implementations=document.getElementById("trait-implementations-list");let trait_implementations_header=document.getElementById("trait-implementations");const script=document.querySelector("script[data-self-path]");const selfPath=script?script.getAttribute("data-self-path"):null;const mainContent=document.querySelector("#main-content");const sidebarSection=document.querySelector(".sidebar section");let methods=document.querySelector(".sidebar .block.method");let associatedTypes=document.querySelector(".sidebar .block.associatedtype");let associatedConstants=document.querySelector(".sidebar .block.associatedconstant");let sidebarTraitList=document.querySelector(".sidebar .block.trait-implementation");for(const impList of imp[window.currentCrate]){const types=impList.slice(2);const text=impList[0];const isTrait=impList[1]!==0;const traitName=impList[1];if(types.indexOf(selfPath)===-1){continue;}let outputList=isTrait?trait_implementations:implementations;if(outputList===null){const outputListName=isTrait?"Trait Implementations":"Implementations";const outputListId=isTrait?"trait-implementations-list":"implementations-list";const outputListHeaderId=isTrait?"trait-implementations":"implementations";const outputListHeader=document.createElement("h2");outputListHeader.id=outputListHeaderId;outputListHeader.innerText=outputListName;outputList=document.createElement("div");outputList.id=outputListId;if(isTrait){const link=document.createElement("a");link.href=`#${outputListHeaderId}`;link.innerText="Trait Implementations";const h=document.createElement("h3");h.appendChild(link);trait_implementations=outputList;trait_implementations_header=outputListHeader;sidebarSection.appendChild(h);sidebarTraitList=document.createElement("ul");sidebarTraitList.className="block trait-implementation";sidebarSection.appendChild(sidebarTraitList);mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList);}else{implementations=outputList;if(trait_implementations){mainContent.insertBefore(outputListHeader,trait_implementations_header);mainContent.insertBefore(outputList,trait_implementations_header);}else{const mainContent=document.querySelector("#main-content");mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList);}}}const template=document.createElement("template");template.innerHTML=text;onEachLazy(template.content.querySelectorAll("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href);}});onEachLazy(template.content.querySelectorAll("[id]"),el=>{let i=0;if(idMap.has(el.id)){i=idMap.get(el.id);}else if(document.getElementById(el.id)){i=1;while(document.getElementById(`${el.id}-${2 * i}`)){i=2*i;}while(document.getElementById(`${el.id}-${i}`)){i+=1;}}if(i!==0){const oldHref=`#${el.id}`;const newHref=`#${el.id}-${i}`;el.id=`${el.id}-${i}`;onEachLazy(template.content.querySelectorAll("a[href]"),link=>{if(link.getAttribute("href")===oldHref){link.href=newHref;}});}idMap.set(el.id,i+1);});const templateAssocItems=template.content.querySelectorAll("section.tymethod, "+"section.method, section.associatedtype, section.associatedconstant");if(isTrait){const li=document.createElement("li");const a=document.createElement("a");a.href=`#${template.content.querySelector(".impl").id}`;a.textContent=traitName;li.appendChild(a);sidebarTraitList.append(li);}else{onEachLazy(templateAssocItems,item=>{let block=hasClass(item,"associatedtype")?associatedTypes:(hasClass(item,"associatedconstant")?associatedConstants:(methods));if(!block){const blockTitle=hasClass(item,"associatedtype")?"Associated Types":(hasClass(item,"associatedconstant")?"Associated Constants":("Methods"));const blockClass=hasClass(item,"associatedtype")?"associatedtype":(hasClass(item,"associatedconstant")?"associatedconstant":("method"));const blockHeader=document.createElement("h3");const blockLink=document.createElement("a");blockLink.href="#implementations";blockLink.innerText=blockTitle;blockHeader.appendChild(blockLink);block=document.createElement("ul");block.className=`block ${blockClass}`;const insertionReference=methods||sidebarTraitList;if(insertionReference){const insertionReferenceH=insertionReference.previousElementSibling;sidebarSection.insertBefore(blockHeader,insertionReferenceH);sidebarSection.insertBefore(block,insertionReferenceH);}else{sidebarSection.appendChild(blockHeader);sidebarSection.appendChild(block);}if(hasClass(item,"associatedtype")){associatedTypes=block;}else if(hasClass(item,"associatedconstant")){associatedConstants=block;}else{methods=block;}}const li=document.createElement("li");const a=document.createElement("a");a.innerText=item.id.split("-")[0].split(".")[1];a.href=`#${item.id}`;li.appendChild(a);block.appendChild(li);});}outputList.appendChild(template.content);}for(const list of[methods,associatedTypes,associatedConstants,sidebarTraitList]){if(!list){continue;}const newChildren=Array.prototype.slice.call(list.children);newChildren.sort((a,b)=>{const aI=a.innerText;const bI=b.innerText;return aIbI?1:0;});list.replaceChildren(...newChildren);}};if(window.pending_type_impls){window.register_type_impls(window.pending_type_impls);}function addSidebarCrates(){if(!window.ALL_CRATES){return;}const sidebarElems=document.getElementById("rustdoc-modnav");if(!sidebarElems){return;}const h3=document.createElement("h3");h3.innerHTML="Crates";const ul=document.createElement("ul");ul.className="block crate";for(const crate of window.ALL_CRATES){const link=document.createElement("a");link.href=window.rootPath+crate+"/index.html";link.textContent=crate;const li=document.createElement("li");if(window.rootPath!=="./"&&crate===window.currentCrate){li.className="current";}li.appendChild(link);ul.appendChild(li);}sidebarElems.appendChild(h3);sidebarElems.appendChild(ul);}function expandAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);removeClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hasClass(e,"type-contents-toggle")&&!hasClass(e,"more-examples-toggle")){e.open=true;}});innerToggle.children[0].innerText="Summary";}function collapseAllDocs(collapseImpls){const innerToggle=document.getElementById(toggleAllDocsId);addClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if((collapseImpls||e.parentNode.id!=="implementations-list")||(!hasClass(e,"implementors-toggle")&&!hasClass(e,"type-contents-toggle"))){e.open=false;}});innerToggle.children[0].innerText="Show all";}function toggleAllDocs(ev){const innerToggle=document.getElementById(toggleAllDocsId);if(!innerToggle){return;}if(hasClass(innerToggle,"will-expand")){expandAllDocs();}else{collapseAllDocs(ev!==undefined&&ev.shiftKey);}}(function(){const toggles=document.getElementById(toggleAllDocsId);if(toggles){toggles.onclick=toggleAllDocs;}const hideMethodDocs=getSettingValue("auto-hide-method-docs")==="true";const hideImplementations=getSettingValue("auto-hide-trait-implementations")==="true";const hideLargeItemContents=getSettingValue("auto-hide-large-items")!=="false";function setImplementorsTogglesOpen(id,open){const list=document.getElementById(id);if(list!==null){onEachLazy(list.getElementsByClassName("implementors-toggle"),e=>{e.open=open;});}}if(hideImplementations){setImplementorsTogglesOpen("trait-implementations-list",false);setImplementorsTogglesOpen("blanket-implementations-list",false);}onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hideLargeItemContents&&hasClass(e,"type-contents-toggle")){e.open=true;}if(hideMethodDocs&&hasClass(e,"method-toggle")){e.open=false;}});}());window.rustdoc_add_line_numbers_to_examples=()=>{function generateLine(nb){return`${nb}`;}onEachLazy(document.querySelectorAll(".rustdoc:not(.src) :not(.scraped-example) > .example-wrap > pre > code",),code=>{if(hasClass(code.parentElement.parentElement,"hide-lines")){removeClass(code.parentElement.parentElement,"hide-lines");return;}const lines=code.innerHTML.split("\n");const digits=(lines.length+"").length;code.innerHTML=lines.map((line,index)=>generateLine(index+1)+line).join("\n");addClass(code.parentElement.parentElement,`digits-${digits}`);});};window.rustdoc_remove_line_numbers_from_examples=()=>{onEachLazy(document.querySelectorAll(".rustdoc:not(.src) :not(.scraped-example) > .example-wrap"),x=>addClass(x,"hide-lines"),);};if(getSettingValue("line-numbers")==="true"){window.rustdoc_add_line_numbers_to_examples();}function showSidebar(){window.hideAllModals(false);const sidebar=document.getElementsByClassName("sidebar")[0];addClass(sidebar,"shown");}function hideSidebar(){const sidebar=document.getElementsByClassName("sidebar")[0];removeClass(sidebar,"shown");}window.addEventListener("resize",()=>{if(window.CURRENT_TOOLTIP_ELEMENT){const base=window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE;const force_visible=base.TOOLTIP_FORCE_VISIBLE;hideTooltip(false);if(force_visible){showTooltip(base);base.TOOLTIP_FORCE_VISIBLE=true;}}});const mainElem=document.getElementById(MAIN_ID);if(mainElem){mainElem.addEventListener("click",hideSidebar);}onEachLazy(document.querySelectorAll("a[href^='#']"),el=>{el.addEventListener("click",()=>{expandSection(el.hash.slice(1));hideSidebar();});});onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"),el=>{el.addEventListener("click",e=>{if(!e.target.matches("summary, a, a *")){e.preventDefault();}});});function showTooltip(e){const notable_ty=e.getAttribute("data-notable-ty");if(!window.NOTABLE_TRAITS&¬able_ty){const data=document.getElementById("notable-traits-data");if(data){window.NOTABLE_TRAITS=JSON.parse(data.innerText);}else{throw new Error("showTooltip() called with notable without any notable traits!");}}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE===e){clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);return;}window.hideAllModals(false);const wrapper=Object.assign(document.createElement("div"),{TOOLTIP_BASE:e});if(notable_ty){wrapper.innerHTML="
"+window.NOTABLE_TRAITS[notable_ty]+"
";}else{const ttl=e.getAttribute("title");if(ttl!==null){e.setAttribute("data-title",ttl);e.removeAttribute("title");}const dttl=e.getAttribute("data-title");if(dttl!==null){const titleContent=document.createElement("div");titleContent.className="content";titleContent.appendChild(document.createTextNode(dttl));wrapper.appendChild(titleContent);}}wrapper.className="tooltip popover";const focusCatcher=document.createElement("div");focusCatcher.setAttribute("tabindex","0");focusCatcher.onfocus=hideTooltip;wrapper.appendChild(focusCatcher);const pos=e.getBoundingClientRect();wrapper.style.top=(pos.top+window.scrollY+pos.height)+"px";wrapper.style.left=0;wrapper.style.right="auto";wrapper.style.visibility="hidden";document.body.appendChild(wrapper);const wrapperPos=wrapper.getBoundingClientRect();const finalPos=pos.left+window.scrollX-wrapperPos.width+24;if(finalPos>0){wrapper.style.left=finalPos+"px";}else{wrapper.style.setProperty("--popover-arrow-offset",(wrapperPos.right-pos.right+4)+"px",);}wrapper.style.visibility="";window.CURRENT_TOOLTIP_ELEMENT=wrapper;clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);wrapper.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return;}clearTooltipHoverTimeout(e);};wrapper.onpointerleave=ev=>{if(ev.pointerType!=="mouse"||!(ev.relatedTarget instanceof HTMLElement)){return;}if(!e.TOOLTIP_FORCE_VISIBLE&&!e.contains(ev.relatedTarget)){setTooltipHoverTimeout(e,false);addClass(wrapper,"fade-out");}};}function setTooltipHoverTimeout(element,show){clearTooltipHoverTimeout(element);if(!show&&!window.CURRENT_TOOLTIP_ELEMENT){return;}if(show&&window.CURRENT_TOOLTIP_ELEMENT){return;}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE!==element){return;}element.TOOLTIP_HOVER_TIMEOUT=setTimeout(()=>{if(show){showTooltip(element);}else if(!element.TOOLTIP_FORCE_VISIBLE){hideTooltip(false);}},show?window.RUSTDOC_TOOLTIP_HOVER_MS:window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS);}function clearTooltipHoverTimeout(element){if(element.TOOLTIP_HOVER_TIMEOUT!==undefined){removeClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out");clearTimeout(element.TOOLTIP_HOVER_TIMEOUT);delete element.TOOLTIP_HOVER_TIMEOUT;}}function tooltipBlurHandler(event){if(window.CURRENT_TOOLTIP_ELEMENT&&!window.CURRENT_TOOLTIP_ELEMENT.contains(document.activeElement)&&!window.CURRENT_TOOLTIP_ELEMENT.contains(event.relatedTarget)&&!window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(document.activeElement)&&!window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(event.relatedTarget)){setTimeout(()=>hideTooltip(false),0);}}function hideTooltip(focus){if(window.CURRENT_TOOLTIP_ELEMENT){if(window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE){if(focus){window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus();}window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE=false;}document.body.removeChild(window.CURRENT_TOOLTIP_ELEMENT);clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);window.CURRENT_TOOLTIP_ELEMENT=undefined;}}onEachLazy(document.getElementsByClassName("tooltip"),e=>{e.onclick=()=>{e.TOOLTIP_FORCE_VISIBLE=e.TOOLTIP_FORCE_VISIBLE?false:true;if(window.CURRENT_TOOLTIP_ELEMENT&&!e.TOOLTIP_FORCE_VISIBLE){hideTooltip(true);}else{showTooltip(e);window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex","0");window.CURRENT_TOOLTIP_ELEMENT.focus();window.CURRENT_TOOLTIP_ELEMENT.onblur=tooltipBlurHandler;}return false;};e.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return;}setTooltipHoverTimeout(e,true);};e.onpointermove=ev=>{if(ev.pointerType!=="mouse"){return;}setTooltipHoverTimeout(e,true);};e.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return;}if(!e.TOOLTIP_FORCE_VISIBLE&&window.CURRENT_TOOLTIP_ELEMENT&&!window.CURRENT_TOOLTIP_ELEMENT.contains(ev.relatedTarget)){setTooltipHoverTimeout(e,false);addClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out");}};});const sidebar_menu_toggle=document.getElementsByClassName("sidebar-menu-toggle")[0];if(sidebar_menu_toggle){sidebar_menu_toggle.addEventListener("click",()=>{const sidebar=document.getElementsByClassName("sidebar")[0];if(!hasClass(sidebar,"shown")){showSidebar();}else{hideSidebar();}});}function helpBlurHandler(event){const isInPopover=onEachLazy(document.querySelectorAll(".settings-menu, .help-menu"),menu=>{return menu.contains(document.activeElement)||menu.contains(event.relatedTarget);},);if(!isInPopover){window.hidePopoverMenus();}}function buildHelpMenu(){const book_info=document.createElement("span");const drloChannel=`https://doc.rust-lang.org/${getVar("channel")}`;book_info.className="top";book_info.innerHTML=`You can find more information in \ +the rustdoc book.`;const shortcuts=[["?","Show this help dialog"],["S / /","Focus the search field"],["↑","Move up in search results"],["↓","Move down in search results"],["← / →","Switch result tab (when results focused)"],["⏎","Go to active search result"],["+","Expand all sections"],["-","Collapse all sections"],["_","Collapse all sections, including impl blocks"],].map(x=>"
"+x[0].split(" ").map((y,index)=>((index&1)===0?""+y+"":" "+y+" ")).join("")+"
"+x[1]+"
").join("");const div_shortcuts=document.createElement("div");addClass(div_shortcuts,"shortcuts");div_shortcuts.innerHTML="

Keyboard Shortcuts

"+shortcuts+"
";const infos=[`For a full list of all search features, take a look \ + here.`,"Prefix searches with a type followed by a colon (e.g., 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="

Search Tricks

"+infos;const rustdoc_version=document.createElement("span");rustdoc_version.className="bottom";const rustdoc_version_code=document.createElement("code");rustdoc_version_code.innerText="rustdoc "+getVar("rustdoc-version");rustdoc_version.appendChild(rustdoc_version_code);const container=document.createElement("div");if(!isHelpPage){container.className="popover content";}container.id="help";const side_by_side=document.createElement("div");side_by_side.className="side-by-side";side_by_side.appendChild(div_shortcuts);side_by_side.appendChild(div_infos);container.appendChild(book_info);container.appendChild(side_by_side);container.appendChild(rustdoc_version);if(isHelpPage){const help_section=document.createElement("section");help_section.appendChild(container);nonnull(document.getElementById("main-content")).appendChild(help_section);}else{onEachLazy(document.getElementsByClassName("help-menu"),menu=>{if(menu.offsetWidth!==0){menu.appendChild(container);container.onblur=helpBlurHandler;menu.onblur=helpBlurHandler;menu.children[0].onblur=helpBlurHandler;return true;}});}return container;}window.hideAllModals=switchFocus=>{hideSidebar();window.hidePopoverMenus();hideTooltip(switchFocus);};window.hidePopoverMenus=()=>{onEachLazy(document.querySelectorAll(".settings-menu .popover"),elem=>{elem.style.display="none";});onEachLazy(document.querySelectorAll(".help-menu .popover"),elem=>{elem.parentElement.removeChild(elem);});};function showHelp(){window.hideAllModals(false);onEachLazy(document.querySelectorAll(".help-menu a"),menu=>{if(menu.offsetWidth!==0){menu.focus();return true;}});buildHelpMenu();}if(isHelpPage){buildHelpMenu();}else{onEachLazy(document.querySelectorAll(".help-menu > a"),helpLink=>{helpLink.addEventListener("click",event=>{if(event.ctrlKey||event.altKey||event.metaKey){return;}event.preventDefault();if(document.getElementById("help")){window.hidePopoverMenus();}else{showHelp();}},);});}addSidebarItems();addSidebarCrates();onHashChange(null);window.addEventListener("hashchange",onHashChange);window.searchState.setup();}());(function(){const SIDEBAR_MIN=100;const SIDEBAR_MAX=500;const RUSTDOC_MOBILE_BREAKPOINT=700;const BODY_MIN=400;const SIDEBAR_VANISH_THRESHOLD=SIDEBAR_MIN/2;let sidebarButton=document.getElementById("sidebar-button");const body=document.querySelector(".main-heading");if(!sidebarButton&&body){sidebarButton=document.createElement("div");sidebarButton.id="sidebar-button";const path=`${window.rootPath}${window.currentCrate}/all.html`;sidebarButton.innerHTML=``;body.insertBefore(sidebarButton,body.firstChild);}if(sidebarButton){sidebarButton.addEventListener("click",e=>{removeClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","false");if(window.rustdocToggleSrcSidebar){window.rustdocToggleSrcSidebar();}e.preventDefault();});}let currentPointerId=null;let desiredSidebarSize=null;let pendingSidebarResizingFrame=false;const resizer=document.querySelector(".sidebar-resizer");const sidebar=document.querySelector(".sidebar");if(!resizer||!sidebar){return;}const isSrcPage=hasClass(document.body,"src");const hideSidebar=function(){if(isSrcPage){window.rustdocCloseSourceSidebar();updateLocalStorage("src-sidebar-width",null);document.documentElement.style.removeProperty("--src-sidebar-width");sidebar.style.removeProperty("--src-sidebar-width");resizer.style.removeProperty("--src-sidebar-width");}else{addClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","true");updateLocalStorage("desktop-sidebar-width",null);document.documentElement.style.removeProperty("--desktop-sidebar-width");sidebar.style.removeProperty("--desktop-sidebar-width");resizer.style.removeProperty("--desktop-sidebar-width");}};const showSidebar=function(){if(isSrcPage){window.rustdocShowSourceSidebar();}else{removeClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","false");}};const changeSidebarSize=function(size){if(isSrcPage){updateLocalStorage("src-sidebar-width",size.toString());sidebar.style.setProperty("--src-sidebar-width",size+"px");resizer.style.setProperty("--src-sidebar-width",size+"px");}else{updateLocalStorage("desktop-sidebar-width",size.toString());sidebar.style.setProperty("--desktop-sidebar-width",size+"px");resizer.style.setProperty("--desktop-sidebar-width",size+"px");}};const isSidebarHidden=function(){return isSrcPage?!hasClass(document.documentElement,"src-sidebar-expanded"):hasClass(document.documentElement,"hide-sidebar");};const resize=function(e){if(currentPointerId===null||currentPointerId!==e.pointerId){return;}e.preventDefault();const pos=e.clientX-3;if(pos=SIDEBAR_MIN){if(isSidebarHidden()){showSidebar();}const constrainedPos=Math.min(pos,window.innerWidth-BODY_MIN,SIDEBAR_MAX);changeSidebarSize(constrainedPos);desiredSidebarSize=constrainedPos;if(pendingSidebarResizingFrame!==false){clearTimeout(pendingSidebarResizingFrame);}pendingSidebarResizingFrame=setTimeout(()=>{if(currentPointerId===null||pendingSidebarResizingFrame===false){return;}pendingSidebarResizingFrame=false;document.documentElement.style.setProperty("--resizing-sidebar-width",desiredSidebarSize+"px",);},100);}};window.addEventListener("resize",()=>{if(window.innerWidth=(window.innerWidth-BODY_MIN)){changeSidebarSize(window.innerWidth-BODY_MIN);}else if(desiredSidebarSize!==null&&desiredSidebarSize>SIDEBAR_MIN){changeSidebarSize(desiredSidebarSize);}});const stopResize=function(e){if(currentPointerId===null){return;}if(e){e.preventDefault();}desiredSidebarSize=sidebar.getBoundingClientRect().width;removeClass(resizer,"active");window.removeEventListener("pointermove",resize,false);window.removeEventListener("pointerup",stopResize,false);removeClass(document.documentElement,"sidebar-resizing");document.documentElement.style.removeProperty("--resizing-sidebar-width");if(resizer.releasePointerCapture){resizer.releasePointerCapture(currentPointerId);currentPointerId=null;}};const initResize=function(e){if(currentPointerId!==null||e.altKey||e.ctrlKey||e.metaKey||e.button!==0){return;}if(resizer.setPointerCapture){resizer.setPointerCapture(e.pointerId);if(!resizer.hasPointerCapture(e.pointerId)){resizer.releasePointerCapture(e.pointerId);return;}currentPointerId=e.pointerId;}window.hideAllModals(false);e.preventDefault();window.addEventListener("pointermove",resize,false);window.addEventListener("pointercancel",stopResize,false);window.addEventListener("pointerup",stopResize,false);addClass(resizer,"active");addClass(document.documentElement,"sidebar-resizing");const pos=e.clientX-sidebar.offsetLeft-3;document.documentElement.style.setProperty("--resizing-sidebar-width",pos+"px");desiredSidebarSize=null;};resizer.addEventListener("pointerdown",initResize,false);}());(function(){function copyContentToClipboard(content){if(content===null){return;}const el=document.createElement("textarea");el.value=content;el.setAttribute("readonly","");el.style.position="absolute";el.style.left="-9999px";document.body.appendChild(el);el.select();document.execCommand("copy");document.body.removeChild(el);}function copyButtonAnimation(button){button.classList.add("clicked");if(button.reset_button_timeout!==undefined){clearTimeout(button.reset_button_timeout);}button.reset_button_timeout=setTimeout(()=>{button.reset_button_timeout=undefined;button.classList.remove("clicked");},1000);}const but=document.getElementById("copy-path");if(!but){return;}but.onclick=()=>{const titleElement=document.querySelector("title");const title=titleElement&&titleElement.textContent?titleElement.textContent.replace(" - Rust",""):"";const[item,module]=title.split(" in ");const path=[item];if(module!==undefined){path.unshift(module);}copyContentToClipboard(path.join("::"));copyButtonAnimation(but);};function copyCode(codeElem){if(!codeElem){return;}copyContentToClipboard(codeElem.textContent);}function getExampleWrap(event){const target=event.target;if(target instanceof HTMLElement){let elem=target;while(elem!==null&&!hasClass(elem,"example-wrap")){if(elem===document.body||elem.tagName==="A"||elem.tagName==="BUTTON"||hasClass(elem,"docblock")){return null;}elem=elem.parentElement;}return elem;}else{return null;}}function addCopyButton(event){const elem=getExampleWrap(event);if(elem===null){return;}elem.removeEventListener("mouseover",addCopyButton);const parent=document.createElement("div");parent.className="button-holder";const runButton=elem.querySelector(".test-arrow");if(runButton!==null){parent.appendChild(runButton);}elem.appendChild(parent);const copyButton=document.createElement("button");copyButton.className="copy-button";copyButton.title="Copy code to clipboard";copyButton.addEventListener("click",()=>{copyCode(elem.querySelector("pre > code"));copyButtonAnimation(copyButton);});parent.appendChild(copyButton);if(!elem.parentElement||!elem.parentElement.classList.contains("scraped-example")||!window.updateScrapedExample){return;}const scrapedWrapped=elem.parentElement;window.updateScrapedExample(scrapedWrapped,parent);}function showHideCodeExampleButtons(event){const elem=getExampleWrap(event);if(elem===null){return;}let buttons=elem.querySelector(".button-holder");if(buttons===null){addCopyButton(event);buttons=elem.querySelector(".button-holder");if(buttons===null){return;}}buttons.classList.toggle("keep-visible");}onEachLazy(document.querySelectorAll(".docblock .example-wrap"),elem=>{elem.addEventListener("mouseover",addCopyButton);elem.addEventListener("click",showHideCodeExampleButtons);});}());(function(){document.body.addEventListener("copy",event=>{let target=nonnull(event.target);let isInsideCode=false;while(target&&target!==document.body){if(target.tagName==="CODE"){isInsideCode=true;break;}target=target.parentElement;}if(!isInsideCode){return;}const selection=nonnull(document.getSelection());const text=Array.from({length:selection.rangeCount},(_,i)=>{const fragment=selection.getRangeAt(i).cloneContents();fragment.querySelectorAll("[data-nosnippet]").forEach(el=>el.remove());return fragment.textContent;}).join("");nonnull(event.clipboardData).setData("text/plain",text);event.preventDefault();});}()); \ No newline at end of file diff --git a/pr/395/docs/static.files/normalize-9960930a.css b/pr/395/docs/static.files/normalize-9960930a.css new file mode 100644 index 000000000..469959f13 --- /dev/null +++ b/pr/395/docs/static.files/normalize-9960930a.css @@ -0,0 +1,2 @@ + /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type="button"],[type="reset"],[type="submit"],button{-webkit-appearance:button}[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none} \ No newline at end of file diff --git a/pr/395/docs/static.files/noscript-263c88ec.css b/pr/395/docs/static.files/noscript-263c88ec.css new file mode 100644 index 000000000..e41db77dc --- /dev/null +++ b/pr/395/docs/static.files/noscript-263c88ec.css @@ -0,0 +1 @@ + #main-content .attributes{margin-left:0 !important;}#copy-path,#sidebar-button,.sidebar-resizer{display:none !important;}nav.sub{display:none;}.src .sidebar{display:none;}.notable-traits{display:none;}:root,:root:not([data-theme]){--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--sidebar-border-color:#ddd;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--mobile-sidebar-menu-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--code-example-button-color:#7f7f7f;--code-example-button-hover-color:#595959;--settings-menu-filter:invert(50%);--settings-menu-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--attribute-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);--sidebar-resizer-hover:hsl(207,90%,66%);--sidebar-resizer-active:hsl(207,90%,54%);}@media (prefers-color-scheme:dark){:root,:root:not([data-theme]){--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--sidebar-border-color:#2A2A2A;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--code-example-button-color:#7f7f7f;--code-example-button-hover-color:#a5a5a5;--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--attribute-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--settings-menu-filter:invert(50%);--settings-menu-hover-filter:invert(65%);--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);--sidebar-resizer-hover:hsl(207,30%,54%);--sidebar-resizer-active:hsl(207,90%,54%);}} \ No newline at end of file diff --git a/pr/395/docs/static.files/rust-logo-9a9549ea.svg b/pr/395/docs/static.files/rust-logo-9a9549ea.svg new file mode 100644 index 000000000..62424d8ff --- /dev/null +++ b/pr/395/docs/static.files/rust-logo-9a9549ea.svg @@ -0,0 +1,61 @@ + + + diff --git a/pr/395/docs/static.files/rustdoc-ca0dd0c4.css b/pr/395/docs/static.files/rustdoc-ca0dd0c4.css new file mode 100644 index 000000000..7e6df69c2 --- /dev/null +++ b/pr/395/docs/static.files/rustdoc-ca0dd0c4.css @@ -0,0 +1,86 @@ + :root{--nav-sub-mobile-padding:8px;--search-typename-width:6.75rem;--desktop-sidebar-width:200px;--src-sidebar-width:300px;--desktop-sidebar-z-index:100;--sidebar-elems-left-padding:24px;--clipboard-image:url('data:image/svg+xml,\ +\ +\ +');--copy-path-height:34px;--copy-path-width:33px;--checkmark-image:url('data:image/svg+xml,\ +\ +');--button-left-margin:4px;--button-border-radius:2px;--toolbar-button-border-radius:6px;--code-block-border-radius:6px;--impl-items-indent:0.3em;--docblock-indent:24px;--font-family:"Source Serif 4",NanumBarunGothic,serif;--font-family-code:"Source Code Pro",monospace;--line-number-padding:4px;--line-number-right-margin:20px;--prev-arrow-image:url('data:image/svg+xml,');--next-arrow-image:url('data:image/svg+xml,');--expand-arrow-image:url('data:image/svg+xml,');--collapse-arrow-image:url('data:image/svg+xml,');--hamburger-image:url('data:image/svg+xml,\ + ');}:root.sans-serif{--font-family:"Fira Sans",sans-serif;--font-family-code:"Fira Mono",monospace;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:400;src:local('Fira Sans'),url("FiraSans-Regular-0fe48ade.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Sans';font-style:italic;font-weight:400;src:local('Fira Sans Italic'),url("FiraSans-Italic-81dc35de.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:500;src:local('Fira Sans Medium'),url("FiraSans-Medium-e1aa3f0a.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Sans';font-style:italic;font-weight:500;src:local('Fira Sans Medium Italic'),url("FiraSans-MediumItalic-ccf7e434.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Mono';font-style:normal;font-weight:400;src:local('Fira Mono'),url("FiraMono-Regular-87c26294.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Mono';font-style:normal;font-weight:500;src:local('Fira Mono Medium'),url("FiraMono-Medium-86f75c8c.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:400;src:local('Source Serif 4'),url("SourceSerif4-Regular-6b053e98.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:italic;font-weight:400;src:local('Source Serif 4 Italic'),url("SourceSerif4-It-ca3b17ed.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:500;src:local('Source Serif 4 Semibold'),url("SourceSerif4-Semibold-457a13ac.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:700;src:local('Source Serif 4 Bold'),url("SourceSerif4-Bold-6d4fd4c0.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:400;src:url("SourceCodePro-Regular-8badfe75.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:italic;font-weight:400;src:url("SourceCodePro-It-fc8b9304.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:600;src:url("SourceCodePro-Semibold-aa29a496.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'NanumBarunGothic';src:url("NanumBarunGothic-13b3dcba.ttf.woff2") format("woff2");font-display:swap;unicode-range:U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF;}*{box-sizing:border-box;}body{font:1rem/1.5 var(--font-family);margin:0;position:relative;overflow-wrap:break-word;overflow-wrap:anywhere;font-feature-settings:"kern","liga";background-color:var(--main-background-color);color:var(--main-color);}h1{font-size:1.5rem;}h2{font-size:1.375rem;}h3{font-size:1.25rem;}h1,h2,h3,h4,h5,h6{font-weight:500;}h1,h2,h3,h4{margin:25px 0 15px 0;padding-bottom:6px;}.docblock h3,.docblock h4,h5,h6{margin:15px 0 5px 0;}.docblock>h2:first-child,.docblock>h3:first-child,.docblock>h4:first-child,.docblock>h5:first-child,.docblock>h6:first-child{margin-top:0;}.main-heading h1{margin:0;padding:0;grid-area:main-heading-h1;overflow-wrap:break-word;overflow-wrap:anywhere;}.main-heading{position:relative;display:grid;grid-template-areas:"main-heading-breadcrumbs main-heading-breadcrumbs" "main-heading-h1 main-heading-toolbar" "main-heading-sub-heading main-heading-toolbar";grid-template-columns:minmax(105px,1fr) minmax(0,max-content);grid-template-rows:minmax(25px,min-content) min-content min-content;padding-bottom:6px;margin-bottom:15px;}.search-results-main-heading{grid-template-areas:"main-heading-breadcrumbs main-heading-placeholder" "main-heading-breadcrumbs main-heading-toolbar " "main-heading-h1 main-heading-toolbar ";}.search-results-main-heading nav.sub{grid-area:main-heading-h1;align-items:end;margin:4px 0 8px 0;}.rustdoc-breadcrumbs{grid-area:main-heading-breadcrumbs;line-height:1.25;padding-top:5px;position:relative;z-index:1;}.search-switcher{grid-area:main-heading-breadcrumbs;line-height:1.5;display:flex;color:var(--main-color);align-items:baseline;white-space:nowrap;padding-top:8px;min-height:34px;}.rustdoc-breadcrumbs a{padding:5px 0 7px;}.content h2,.top-doc .docblock>h3,.top-doc .docblock>h4{border-bottom:1px solid var(--headings-border-bottom-color);}h1,h2{line-height:1.25;padding-top:3px;padding-bottom:9px;}h3.code-header{font-size:1.125rem;}h4.code-header{font-size:1rem;}.code-header{font-weight:600;margin:0;padding:0;white-space:pre-wrap;}.structfield,.sub-variant-field{margin:0.6em 0;}#crate-search,h1,h2,h3,h4,h5,h6,.sidebar,rustdoc-topbar,.search-input,.search-results .result-name,.item-table dt>a,.out-of-band,.sub-heading,span.since,a.src,rustdoc-toolbar,summary.hideme,.scraped-example-list,.rustdoc-breadcrumbs,.search-switcher,ul.all-items{font-family:"Fira Sans",Arial,NanumBarunGothic,sans-serif;}#toggle-all-docs,a.anchor,.section-header a,#src-sidebar a,.rust a,.sidebar h2 a,.sidebar h3 a,rustdoc-topbar h2 a,h1 a,.search-results a,.search-results li,.stab,.result-name i{color:var(--main-color);}span.enum,a.enum,span.struct,a.struct,span.union,a.union,span.primitive,a.primitive,span.type,a.type,span.foreigntype,a.foreigntype{color:var(--type-link-color);}span.trait,a.trait,span.traitalias,a.traitalias{color:var(--trait-link-color);}span.associatedtype,a.associatedtype,span.constant,a.constant,span.static,a.static{color:var(--assoc-item-link-color);}span.fn,a.fn,span.method,a.method,span.tymethod,a.tymethod{color:var(--function-link-color);}span.attr,a.attr,span.derive,a.derive,span.macro,a.macro{color:var(--macro-link-color);}span.mod,a.mod{color:var(--mod-link-color);}span.keyword,a.keyword{color:var(--keyword-link-color);}span.attribute,a.attribute{color:var(--attribute-link-color);}a{color:var(--link-color);text-decoration:none;}ol,ul{padding-left:24px;}ul ul,ol ul,ul ol,ol ol{margin-bottom:.625em;}p,.docblock>.warning{margin:0 0 .75em 0;}p:last-child,.docblock>.warning:last-child{margin:0;}button{padding:1px 6px;cursor:pointer;}button#toggle-all-docs{padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.rustdoc{display:flex;flex-direction:row;flex-wrap:nowrap;}main{position:relative;flex-grow:1;padding:10px 15px 40px 45px;min-width:0;}.src main{padding:15px;}.width-limiter{max-width:960px;margin-right:auto;}details:not(.toggle) summary{margin-bottom:.6em;}code,pre,.code-header,.type-signature{font-family:var(--font-family-code);}.docblock code,.item-table dd code{border-radius:3px;padding:0 0.125em;}.docblock pre code,.item-table dd pre code{padding:0;}pre{padding:14px;line-height:1.5;}pre.item-decl{overflow-x:auto;}.item-decl .type-contents-toggle{contain:initial;}.src .content pre{padding:20px;padding-left:16px;}img{max-width:100%;}.logo-container{line-height:0;display:block;}.rust-logo{filter:var(--rust-logo-filter);}.sidebar{font-size:0.875rem;flex:0 0 var(--desktop-sidebar-width);width:var(--desktop-sidebar-width);overflow-y:scroll;overscroll-behavior:contain;position:sticky;height:100vh;top:0;left:0;z-index:var(--desktop-sidebar-z-index);border-right:solid 1px var(--sidebar-border-color);}.rustdoc.src .sidebar{flex-basis:50px;width:50px;overflow-x:hidden;overflow-y:hidden;}.hide-sidebar .sidebar,.hide-sidebar .sidebar-resizer{display:none;}.sidebar-resizer{touch-action:none;width:9px;cursor:ew-resize;z-index:calc(var(--desktop-sidebar-z-index) + 1);position:fixed;height:100%;left:var(--desktop-sidebar-width);display:flex;align-items:center;justify-content:flex-start;color:var(--right-side-color);}.sidebar-resizer::before{content:"";border-right:dotted 2px currentColor;width:2px;height:12px;}.sidebar-resizer::after{content:"";border-right:dotted 2px currentColor;width:2px;height:16px;}.rustdoc.src .sidebar-resizer{left:49px;}.src-sidebar-expanded .src .sidebar-resizer{left:var(--src-sidebar-width);}.sidebar-resizing{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;}.sidebar-resizing *{cursor:ew-resize !important;}.sidebar-resizing .sidebar{position:fixed;border-right:solid 2px var(--sidebar-resizer-active);}.sidebar-resizing>body{padding-left:var(--resizing-sidebar-width);}.sidebar-resizer:hover,.sidebar-resizer:active,.sidebar-resizer:focus,.sidebar-resizer.active{width:10px;margin:0;left:calc(var(--desktop-sidebar-width) - 1px);border-left:solid 1px var(--sidebar-resizer-hover);color:var(--sidebar-resizer-hover);}.src-sidebar-expanded .rustdoc.src .sidebar-resizer:hover,.src-sidebar-expanded .rustdoc.src .sidebar-resizer:active,.src-sidebar-expanded .rustdoc.src .sidebar-resizer:focus,.src-sidebar-expanded .rustdoc.src .sidebar-resizer.active{left:calc(var(--src-sidebar-width) - 1px);}@media (pointer:coarse){.sidebar-resizer{display:none !important;}.sidebar{border-right:none;}}.sidebar-resizer.active{padding:0 140px;width:calc(140px + 140px + 9px + 2px);margin-left:-140px;border-left:none;color:var(--sidebar-resizer-active);}.sidebar,rustdoc-topbar,.sidebar-menu-toggle,#src-sidebar{background-color:var(--sidebar-background-color);}.src .sidebar>*{visibility:hidden;}.src-sidebar-expanded .src .sidebar{overflow-y:auto;flex-basis:var(--src-sidebar-width);width:var(--src-sidebar-width);}.src-sidebar-expanded .src .sidebar>*{visibility:visible;}#all-types{margin-top:1em;}*{scrollbar-width:initial;scrollbar-color:var(--scrollbar-color);}.sidebar{scrollbar-width:thin;scrollbar-color:var(--scrollbar-color);}::-webkit-scrollbar{width:12px;}.sidebar::-webkit-scrollbar{width:8px;}::-webkit-scrollbar-track{-webkit-box-shadow:inset 0;background-color:var(--scrollbar-track-background-color);}.sidebar::-webkit-scrollbar-track{background-color:var(--scrollbar-track-background-color);}::-webkit-scrollbar-thumb,.sidebar::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb-background-color);}.hidden{display:none !important;}.logo-container>img{height:48px;width:48px;}ul.block,.block li,.block ul{padding:0;margin:0;list-style:none;}.block ul a{padding-left:1rem;}.sidebar-elems a,.sidebar>h2 a{display:block;padding:0.25rem;margin-right:0.25rem;border-left:solid var(--sidebar-elems-left-padding) transparent;margin-left:calc(-0.25rem - var(--sidebar-elems-left-padding));background-clip:border-box;}.hide-toc #rustdoc-toc,.hide-toc .in-crate{display:none;}.hide-modnav #rustdoc-modnav{display:none;}.sidebar h2{text-wrap:balance;overflow-wrap:anywhere;padding:0;margin:0.7rem 0;}.sidebar h3{text-wrap:balance;overflow-wrap:anywhere;font-size:1.125rem;padding:0;margin:0;}.sidebar-elems,.sidebar>.version,.sidebar>h2{padding-left:var(--sidebar-elems-left-padding);}.sidebar a{color:var(--sidebar-link-color);}.sidebar .current,.sidebar .current a,.sidebar-crate a.logo-container:hover+h2 a,.sidebar a:hover:not(.logo-container){background-color:var(--sidebar-current-link-background-color);}.sidebar-elems .block{margin-bottom:2em;}.sidebar-elems .block li a{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}.sidebar-crate{display:flex;align-items:center;justify-content:center;margin:14px 32px 1rem;row-gap:10px;column-gap:32px;flex-wrap:wrap;}.sidebar-crate h2{flex-grow:1;margin:0 -8px;align-self:start;}.sidebar-crate .logo-container{margin:0 calc(-16px - var(--sidebar-elems-left-padding));padding:0 var(--sidebar-elems-left-padding);text-align:center;}.sidebar-crate .logo-container img{margin-top:-16px;border-top:solid 16px transparent;box-sizing:content-box;position:relative;background-clip:border-box;z-index:1;}.sidebar-crate h2 a{display:block;border-left:solid var(--sidebar-elems-left-padding) transparent;background-clip:border-box;margin:0 calc(-24px + 0.25rem) 0 calc(-0.2rem - var(--sidebar-elems-left-padding));padding:calc((16px - 0.57rem ) / 2 ) 0.25rem;padding-left:0.2rem;}.sidebar-crate h2 .version{display:block;font-weight:normal;font-size:1rem;overflow-wrap:break-word;}.sidebar-crate+.version{margin-top:-1rem;margin-bottom:1rem;}rustdoc-topbar{display:none;}.rustdoc .example-wrap{display:flex;position:relative;margin-bottom:10px;}.rustdoc .example-wrap>pre,.rustdoc .scraped-example .src-line-numbers,.rustdoc .scraped-example .src-line-numbers>pre{border-radius:6px;}.rustdoc .scraped-example{position:relative;}.rustdoc .example-wrap:last-child{margin-bottom:0px;}.rustdoc .example-wrap pre{margin:0;flex-grow:1;}.scraped-example:not(.expanded) .example-wrap{max-height:calc(1.5em * 5 + 10px);}.more-scraped-examples .scraped-example:not(.expanded) .example-wrap{max-height:calc(1.5em * 10 + 10px);}.rustdoc:not(.src) .scraped-example:not(.expanded) .src-line-numbers,.rustdoc:not(.src) .scraped-example:not(.expanded) .src-line-numbers>pre,.rustdoc:not(.src) .scraped-example:not(.expanded) pre.rust{padding-bottom:0;overflow:auto hidden;}.rustdoc:not(.src) .scraped-example .src-line-numbers{padding-top:0;}.rustdoc:not(.src) .scraped-example.expanded .src-line-numbers{padding-bottom:0;}.rustdoc:not(.src) .example-wrap pre{overflow:auto;}.example-wrap code{position:relative;}.example-wrap pre code span{display:inline;}.example-wrap.digits-1{--example-wrap-digits-count:1ch;}.example-wrap.digits-2{--example-wrap-digits-count:2ch;}.example-wrap.digits-3{--example-wrap-digits-count:3ch;}.example-wrap.digits-4{--example-wrap-digits-count:4ch;}.example-wrap.digits-5{--example-wrap-digits-count:5ch;}.example-wrap.digits-6{--example-wrap-digits-count:6ch;}.example-wrap.digits-7{--example-wrap-digits-count:7ch;}.example-wrap.digits-8{--example-wrap-digits-count:8ch;}.example-wrap.digits-9{--example-wrap-digits-count:9ch;}.example-wrap .expansion{position:relative;display:inline;}.example-wrap .expansion>input{display:block;position:absolute;appearance:none;content:'↕';left:-20px;top:0;border:1px solid var(--border-color);border-radius:4px;cursor:pointer;color:var(--main-color);padding:0 2px;line-height:20px;}.example-wrap .expansion>input::after{content:"↕";}.example-wrap .expansion .expanded{display:none;color:var(--main-color);}.example-wrap .expansion>input:checked~.expanded,.example-wrap .expansion>input:checked~* .expanded{display:inherit;}.example-wrap .expansion>input:checked~.original,.example-wrap .expansion>input:checked~* .original{display:none;}.example-wrap [data-nosnippet]{width:calc(var(--example-wrap-digits-count) + var(--line-number-padding) * 2);}.example-wrap pre>code{padding-left:calc(var(--example-wrap-digits-count) + var(--line-number-padding) * 2 + var(--line-number-right-margin));}.src .example-wrap .expansion [data-nosnippet]{position:initial;margin-left:calc((var(--example-wrap-digits-count) + var(--line-number-padding) * 2 + var(--line-number-right-margin)) * -1);}.example-wrap [data-nosnippet]{color:var(--src-line-numbers-span-color);text-align:right;display:inline-block;margin-right:var(--line-number-right-margin);-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;padding:0 var(--line-number-padding);position:absolute;left:0;}.example-wrap pre>code{position:relative;display:block;}:root.word-wrap-source-code .example-wrap pre>code{word-break:break-all;white-space:pre-wrap;}:root.word-wrap-source-code .example-wrap pre>code *{word-break:break-all;}.example-wrap [data-nosnippet]:target{border-right:none;}.example-wrap .line-highlighted[data-nosnippet]{background-color:var(--src-line-number-highlighted-background-color);}.example-wrap.hide-lines [data-nosnippet]{display:none;}.search-loading{text-align:center;}.item-table dd{overflow-wrap:break-word;overflow-wrap:anywhere;}.docblock :not(pre)>code,.item-table dd code{white-space:pre-wrap;}.top-doc .docblock h2{font-size:1.375rem;}.top-doc .docblock h3{font-size:1.25rem;}.top-doc .docblock h4,.top-doc .docblock h5{font-size:1.125rem;}.top-doc .docblock h6{font-size:1rem;}.docblock h5{font-size:1rem;}.docblock h6{font-size:0.875rem;}.docblock{margin-left:var(--docblock-indent);position:relative;}.docblock>:not(.more-examples-toggle):not(.example-wrap){max-width:100%;overflow-x:auto;}.sub-heading{font-size:1rem;flex-grow:0;grid-area:main-heading-sub-heading;line-height:1.25;padding-bottom:4px;}.main-heading rustdoc-toolbar,.main-heading .out-of-band{grid-area:main-heading-toolbar;}rustdoc-toolbar{display:flex;flex-direction:row;flex-wrap:nowrap;min-height:60px;}.docblock code,.item-table dd code,pre,.rustdoc.src .example-wrap,.example-wrap .src-line-numbers{background-color:var(--code-block-background-color);border-radius:var(--code-block-border-radius);text-decoration:inherit;}#main-content{position:relative;}.docblock table{margin:.5em 0;border-collapse:collapse;}.docblock table td,.docblock table th{padding:.5em;border:1px solid var(--border-color);}.docblock table tbody tr:nth-child(2n){background:var(--table-alt-row-background-color);}.docblock .stab,.item-table dd .stab,.docblock p code{display:inline-block;}.docblock li{margin-bottom:.4em;}.docblock li p:not(:last-child){margin-bottom:.3em;}div.where{white-space:pre-wrap;font-size:0.875rem;}.item-info{display:block;margin-left:var(--docblock-indent);}.impl-items>.item-info{margin-left:calc(var(--docblock-indent) + var(--impl-items-indent));}.item-info code{font-size:0.875rem;}#main-content>.item-info{margin-left:0;}nav.sub{flex-grow:1;flex-flow:row nowrap;display:flex;align-items:start;margin-top:4px;}.search-form{position:relative;display:flex;height:34px;flex-grow:1;}.src nav.sub{margin:0 0 -10px 0;}.section-header{display:block;position:relative;}.section-header:hover>.anchor,.impl:hover>.anchor,.trait-impl:hover>.anchor,.variant:hover>.anchor{display:initial;}.anchor{display:none;position:absolute;left:-0.5em;background:none !important;}.anchor.field{left:-5px;}.section-header>.anchor{left:-15px;padding-right:8px;}h2.section-header>.anchor{padding-right:6px;}a.doc-anchor{color:var(--main-color);display:none;position:absolute;left:-17px;padding-right:10px;padding-left:3px;}*:hover>.doc-anchor{display:block;}.top-doc>.docblock>*:first-child>.doc-anchor{display:none !important;}.main-heading a:hover,.example-wrap .rust a:hover:not([data-nosnippet]),.all-items a:hover,.docblock a:not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),.item-table dd a:not(.scrape-help):not(.tooltip):hover,.item-info a{text-decoration:underline;}.crate.block li.current a{font-weight:500;}table,.item-table{overflow-wrap:break-word;}.item-table{padding:0;margin:0;width:100%;}.item-table>dt{padding-right:1.25rem;}.item-table>dd{margin-inline-start:0;margin-left:0;}#crate-search-div{position:relative;min-width:0;margin-top:-1px;}#crate-search{padding:0 23px 0 4px;max-width:100%;text-overflow:ellipsis;border:1px solid var(--border-color);border-radius:4px;outline:none;cursor:pointer;-moz-appearance:none;-webkit-appearance:none;text-indent:0.01px;background-color:var(--main-background-color);color:inherit;line-height:1.5;font-weight:500;}#crate-search:hover,#crate-search:focus{border-color:var(--crate-search-hover-border);}#crate-search-div::after{pointer-events:none;width:100%;height:100%;position:absolute;top:0;left:0;content:"";background-repeat:no-repeat;background-size:20px;background-position:calc(100% - 2px) 56%;background-image:url('data:image/svg+xml, \ + ');filter:var(--crate-search-div-filter);}#crate-search-div:hover::after,#crate-search-div:focus-within::after{filter:var(--crate-search-div-hover-filter);}#crate-search>option{font-size:1rem;}.search-input{-webkit-appearance:none;outline:none;border:1px solid var(--border-color);border-radius:2px;padding:8px;font-size:1rem;flex-grow:1;background-color:var(--button-background-color);color:var(--search-color);max-width:100%;}.search-input:focus{border-color:var(--search-input-focused-border-color);}.search-results{display:none;}.search-results.active{display:block;margin:0;padding:0;}.search-results>a{display:grid;grid-template-areas:"search-result-name search-result-desc" "search-result-type-signature search-result-type-signature";grid-template-columns:.6fr .4fr;margin-left:2px;margin-right:2px;border-bottom:1px solid var(--search-result-border-color);column-gap:1em;}.search-results>a>div.desc{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;grid-area:search-result-desc;}.search-results a:hover,.search-results a:focus{background-color:var(--search-result-link-focus-background-color);}.search-results .result-name{display:flex;align-items:center;justify-content:start;grid-area:search-result-name;}.search-results .result-name .alias{color:var(--search-results-alias-color);}.search-results .result-name .grey{color:var(--search-results-grey-color);}.search-results .result-name .typename{color:var(--search-results-grey-color);font-size:0.875rem;width:var(--search-typename-width);}.search-results .result-name .path{word-break:break-all;max-width:calc(100% - var(--search-typename-width));display:inline-block;}.search-results .result-name .path>*{display:inline;}.search-results .type-signature{grid-area:search-result-type-signature;white-space:pre-wrap;}.popover{position:absolute;top:100%;right:0;z-index:calc(var(--desktop-sidebar-z-index) + 1);margin-top:7px;border-radius:3px;border:1px solid var(--border-color);background-color:var(--main-background-color);color:var(--main-color);--popover-arrow-offset:11px;}.popover::before{content:'';position:absolute;right:var(--popover-arrow-offset);border:solid var(--border-color);border-width:1px 1px 0 0;background-color:var(--main-background-color);padding:4px;transform:rotate(-45deg);top:-5px;}.setting-line{margin:1.2em 0.6em;}.setting-radio input,.setting-check input{margin-right:0.3em;height:1.2rem;width:1.2rem;border:2px solid var(--settings-input-border-color);outline:none;-webkit-appearance:none;cursor:pointer;}.setting-radio input{border-radius:50%;}.setting-radio span,.setting-check span{padding-bottom:1px;}.setting-radio{margin-top:0.1em;margin-bottom:0.1em;min-width:3.8em;padding:0.3em;display:inline-flex;align-items:center;cursor:pointer;}.setting-radio+.setting-radio{margin-left:0.5em;}.setting-check{margin-right:20px;display:flex;align-items:center;cursor:pointer;}.setting-check input{flex-shrink:0;}.setting-radio input:checked{box-shadow:inset 0 0 0 3px var(--main-background-color);background-color:var(--settings-input-color);}.setting-check input:checked{background-color:var(--settings-input-color);border-width:1px;content:url('data:image/svg+xml,\ + \ + ');}.setting-radio input:focus,.setting-check input:focus{box-shadow:0 0 1px 1px var(--settings-input-color);}.setting-radio input:checked:focus{box-shadow:inset 0 0 0 3px var(--main-background-color),0 0 2px 2px var(--settings-input-color);}.setting-radio input:hover,.setting-check input:hover{border-color:var(--settings-input-color) !important;}#settings.popover{--popover-arrow-offset:196px;top:calc(100% - 16px);}#help.popover{max-width:600px;--popover-arrow-offset:115px;top:calc(100% - 16px);}#help dt{float:left;clear:left;margin-right:0.5rem;}#help dd{margin-bottom:0.5rem;}#help span.top,#help span.bottom{text-align:center;display:block;font-size:1.125rem;padding:0 0.5rem;text-wrap-style:balance;}#help span.top{margin:10px 0;border-bottom:1px solid var(--border-color);padding-bottom:4px;margin-bottom:6px;}#help span.bottom{clear:both;border-top:1px solid var(--border-color);}.side-by-side{display:flex;margin-bottom:20px;}.side-by-side>div{width:50%;padding:0 20px 0 17px;}.item-info .stab{display:block;padding:3px;margin-bottom:5px;}.item-table dt .stab{margin-left:0.3125em;}.stab{padding:0 2px;font-size:0.875rem;font-weight:normal;color:var(--main-color);background-color:var(--stab-background-color);width:fit-content;white-space:pre-wrap;border-radius:3px;display:inline;vertical-align:baseline;}.stab.portability>code{background:none;color:var(--stab-code-color);}.stab .emoji,.item-info .stab::before{font-size:1.25rem;}.stab .emoji{margin-right:0.3rem;}.item-info .stab::before{content:"\0";width:0;display:inline-block;color:transparent;}.emoji{text-shadow:1px 0 0 black,-1px 0 0 black,0 1px 0 black,0 -1px 0 black;}.since{font-weight:normal;font-size:initial;}.rightside{padding-left:12px;float:right;}.rightside:not(a),.out-of-band,.sub-heading,rustdoc-toolbar{color:var(--right-side-color);}pre.rust{tab-size:4;-moz-tab-size:4;}pre.rust .kw{color:var(--code-highlight-kw-color);}pre.rust .kw-2{color:var(--code-highlight-kw-2-color);}pre.rust .lifetime{color:var(--code-highlight-lifetime-color);}pre.rust .prelude-ty{color:var(--code-highlight-prelude-color);}pre.rust .prelude-val{color:var(--code-highlight-prelude-val-color);}pre.rust .string{color:var(--code-highlight-string-color);}pre.rust .number{color:var(--code-highlight-number-color);}pre.rust .bool-val{color:var(--code-highlight-literal-color);}pre.rust .self{color:var(--code-highlight-self-color);}pre.rust .attr{color:var(--code-highlight-attribute-color);}pre.rust .macro,pre.rust .macro-nonterminal{color:var(--code-highlight-macro-color);}pre.rust .question-mark{font-weight:bold;color:var(--code-highlight-question-mark-color);}pre.rust .comment{color:var(--code-highlight-comment-color);}pre.rust .doccomment{color:var(--code-highlight-doc-comment-color);}.rustdoc.src .example-wrap pre.rust a:not([data-nosnippet]){background:var(--codeblock-link-background);}.example-wrap.compile_fail,.example-wrap.should_panic{border-left:2px solid var(--codeblock-error-color);}.ignore.example-wrap{border-left:2px solid var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover,.example-wrap.should_panic:hover{border-left:2px solid var(--codeblock-error-hover-color);}.example-wrap.ignore:hover{border-left:2px solid var(--codeblock-ignore-hover-color);}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip{color:var(--codeblock-error-color);}.example-wrap.ignore .tooltip{color:var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover .tooltip,.example-wrap.should_panic:hover .tooltip{color:var(--codeblock-error-hover-color);}.example-wrap.ignore:hover .tooltip{color:var(--codeblock-ignore-hover-color);}.example-wrap .tooltip{position:absolute;display:block;left:-25px;top:5px;margin:0;line-height:1;}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip,.example-wrap.ignore .tooltip{font-weight:bold;font-size:1.25rem;}.content .docblock .warning{border-left:2px solid var(--warning-border-color);padding:14px;position:relative;overflow-x:visible !important;}.content .docblock .warning::before{color:var(--warning-border-color);content:"ⓘ";position:absolute;left:-25px;top:5px;font-weight:bold;font-size:1.25rem;}.top-doc>.docblock>.warning:first-child::before{top:20px;}.example-wrap>a.test-arrow,.example-wrap .button-holder{visibility:hidden;position:absolute;top:4px;right:4px;z-index:1;}a.test-arrow{height:var(--copy-path-height);padding:6px 4px 0 11px;}a.test-arrow::before{content:url('data:image/svg+xml,');}.example-wrap .button-holder{display:flex;}@media not (pointer:coarse){.example-wrap:hover>a.test-arrow,.example-wrap:hover>.button-holder{visibility:visible;}}.example-wrap .button-holder.keep-visible{visibility:visible;}.example-wrap .button-holder>*{background:var(--main-background-color);cursor:pointer;border-radius:var(--button-border-radius);height:var(--copy-path-height);width:var(--copy-path-width);border:0;color:var(--code-example-button-color);}.example-wrap .button-holder>*:hover{color:var(--code-example-button-hover-color);}.example-wrap .button-holder>*:not(:first-child){margin-left:var(--button-left-margin);}.example-wrap .button-holder .copy-button{padding:2px 0 0 4px;}.example-wrap .button-holder .copy-button::before,.example-wrap .test-arrow::before,.example-wrap .button-holder .prev::before,.example-wrap .button-holder .next::before,.example-wrap .button-holder .expand::before{filter:var(--copy-path-img-filter);}.example-wrap .button-holder .copy-button::before{content:var(--clipboard-image);}.example-wrap .button-holder .copy-button:hover::before,.example-wrap .test-arrow:hover::before{filter:var(--copy-path-img-hover-filter);}.example-wrap .button-holder .copy-button.clicked::before{content:var(--checkmark-image);padding-right:5px;}.example-wrap .button-holder .prev,.example-wrap .button-holder .next,.example-wrap .button-holder .expand{line-height:0px;}.example-wrap .button-holder .prev::before{content:var(--prev-arrow-image);}.example-wrap .button-holder .next::before{content:var(--next-arrow-image);}.example-wrap .button-holder .expand::before{content:var(--expand-arrow-image);}.example-wrap .button-holder .expand.collapse::before{content:var(--collapse-arrow-image);}.code-attribute{font-weight:300;color:var(--code-attribute-color);}.item-spacer{width:100%;height:12px;display:block;}.main-heading span.since::before{content:"Since ";}.sub-variant h4{font-size:1rem;font-weight:400;margin-top:0;margin-bottom:0;}.sub-variant{margin-left:24px;margin-bottom:40px;}.sub-variant>.sub-variant-field{margin-left:24px;}@keyframes targetfadein{from{background-color:var(--main-background-color);}10%{background-color:var(--target-border-color);}to{background-color:var(--target-background-color);}}:target:not([data-nosnippet]){background-color:var(--target-background-color);border-right:3px solid var(--target-border-color);}a.tooltip{font-family:var(--font-family);}.code-header a.tooltip{color:inherit;margin-right:15px;position:relative;}.code-header a.tooltip:hover{color:var(--link-color);}a.tooltip:hover::after{position:absolute;top:calc(100% - 10px);left:-15px;right:-15px;height:20px;content:"\00a0";}@media not (prefers-reduced-motion){:target{animation:0.65s cubic-bezier(0,0,0.1,1.0) 0.1s targetfadein;}.fade-out{opacity:0;transition:opacity 0.45s cubic-bezier(0,0,0.1,1.0);}}.popover.tooltip .content{margin:0.25em 0.5em;}.popover.tooltip .content pre,.popover.tooltip .content code{background:transparent;margin:0;padding:0;font-size:1.25rem;white-space:pre-wrap;}.popover.tooltip .content>h3:first-child{margin:0 0 5px 0;}.search-failed{text-align:center;margin-top:20px;display:none;}.search-failed.active{display:block;}.search-failed>ul{text-align:left;max-width:570px;margin-left:auto;margin-right:auto;}#search-tabs{margin-top:0.25rem;display:flex;flex-direction:row;gap:1px;margin-bottom:4px;}#search-tabs button{text-align:center;font-size:1.125rem;border:0;border-top:2px solid;flex:1;line-height:1.5;color:inherit;}#search-tabs button:not(.selected){background-color:var(--search-tab-button-not-selected-background);border-top-color:var(--search-tab-button-not-selected-border-top-color);}#search-tabs button:hover,#search-tabs button.selected{background-color:var(--search-tab-button-selected-background);border-top-color:var(--search-tab-button-selected-border-top-color);}#search-tabs .count{font-size:1rem;font-variant-numeric:tabular-nums;color:var(--search-tab-title-count-color);position:relative;}#search-tabs .count.loading{color:transparent;}.search-form.loading::after{width:18px;height:18px;border-radius:18px;content:url('data:image/svg+xml,\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + ');position:absolute;right:8px;top:8px;filter:var(--settings-menu-filter);}#search .error code{border-radius:3px;background-color:var(--search-error-code-background-color);}.search-corrections{font-weight:normal;}#src-sidebar{width:100%;overflow:auto;}#src-sidebar div.files>a:hover,details.dir-entry summary:hover,#src-sidebar div.files>a:focus,details.dir-entry summary:focus{background-color:var(--src-sidebar-background-hover);}#src-sidebar div.files>a.selected{background-color:var(--src-sidebar-background-selected);}.src-sidebar-title{position:sticky;top:0;display:flex;padding:8px 8px 0 48px;margin-bottom:7px;background:var(--sidebar-background-color);border-bottom:1px solid var(--border-color);}#search-button,.settings-menu,.help-menu,button#toggle-all-docs{margin-left:var(--button-left-margin);display:flex;line-height:1.25;min-width:14px;}#sidebar-button{display:none;line-height:0;}.hide-sidebar #sidebar-button,.src #sidebar-button{display:flex;margin-right:4px;position:fixed;margin-top:25px;left:6px;height:34px;width:34px;z-index:calc(var(--desktop-sidebar-z-index) + 1);}.hide-sidebar #sidebar-button{left:6px;background-color:var(--main-background-color);}.src #sidebar-button{margin-top:0;top:8px;left:8px;border-color:var(--border-color);}.hide-sidebar .src #sidebar-button{position:static;}#search-button>a,.settings-menu>a,.help-menu>a,#sidebar-button>a,button#toggle-all-docs{display:flex;align-items:center;justify-content:center;flex-direction:column;}#search-button>a,.settings-menu>a,.help-menu>a,button#toggle-all-docs{border:1px solid transparent;border-radius:var(--button-border-radius);color:var(--main-color);}#search-button>a,.settings-menu>a,.help-menu>a,button#toggle-all-docs{width:80px;border-radius:var(--toolbar-button-border-radius);}#search-button>a,.settings-menu>a,.help-menu>a{min-width:0;}#sidebar-button>a{border:solid 1px transparent;border-radius:var(--button-border-radius);background-color:var(--button-background-color);width:33px;}.src #sidebar-button>a{background-color:var(--sidebar-background-color);border-color:var(--border-color);}#search-button>a:hover,#search-button>a:focus-visible,.settings-menu>a:hover,.settings-menu>a:focus-visible,.help-menu>a:hover,#help-menu>a:focus-visible,#sidebar-button>a:hover,#sidebar-button>a:focus-visible,#copy-path:hover,#copy-path:focus-visible,button#toggle-all-docs:hover,button#toggle-all-docs:focus-visible{border-color:var(--settings-button-border-focus);text-decoration:none;}#search-button>a::before{content:url('data:image/svg+xml,\ + \ + Search\ + ');width:18px;height:18px;filter:var(--settings-menu-filter);}.settings-menu>a::before{content:url('data:image/svg+xml,\ + \ + ');width:18px;height:18px;filter:var(--settings-menu-filter);}button#toggle-all-docs::before{content:url('data:image/svg+xml,\ + ');width:18px;height:18px;filter:var(--settings-menu-filter);}.help-menu>a::before{content:url('data:image/svg+xml,\ + \ + \ + \ + \ + ');width:18px;height:18px;filter:var(--settings-menu-filter);}.help-menu>a{width:74px;}.help-menu>a>.label{padding-right:1px;}#toggle-all-docs:not(.will-expand)>.label{padding-left:1px;}#search-button>a::before,button#toggle-all-docs::before,.help-menu>a::before,.settings-menu>a::before{filter:var(--settings-menu-filter);margin:8px;}@media not (pointer:coarse){#search-button>a:hover::before,button#toggle-all-docs:hover::before,.help-menu>a:hover::before,.settings-menu>a:hover::before{filter:var(--settings-menu-hover-filter);}}button[disabled]#toggle-all-docs{opacity:0.25;border:solid 1px var(--main-background-color);background-size:cover;}button[disabled]#toggle-all-docs:hover{border:solid 1px var(--main-background-color);cursor:not-allowed;}rustdoc-toolbar span.label{font-size:1rem;flex-grow:1;padding-bottom:4px;}#sidebar-button>a::before{content:url('data:image/svg+xml,\ + \ + \ + ');width:22px;height:22px;}#copy-path{color:var(--copy-path-button-color);background:var(--main-background-color);height:var(--copy-path-height);width:var(--copy-path-width);margin-left:10px;padding:0;padding-left:2px;border:solid 1px transparent;border-radius:var(--button-border-radius);font-size:0;}#copy-path::before{filter:var(--copy-path-img-filter);content:var(--clipboard-image);}#copy-path:hover::before{filter:var(--copy-path-img-hover-filter);}#copy-path.clicked::before{content:var(--checkmark-image);}@keyframes rotating{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}.settings-menu.rotate>a img{animation:rotating 2s linear infinite;}kbd{display:inline-block;padding:3px 5px;font:15px monospace;line-height:10px;vertical-align:middle;border:solid 1px var(--border-color);border-radius:3px;color:var(--kbd-color);background-color:var(--kbd-background);box-shadow:inset 0 -1px 0 var(--kbd-box-shadow-color);}ul.all-items>li{list-style:none;}details.dir-entry{padding-left:4px;}details.dir-entry>summary{margin:0 0 0 -4px;padding:0 0 0 4px;cursor:pointer;}details.dir-entry div.folders,details.dir-entry div.files{padding-left:23px;}details.dir-entry a{display:block;}details.toggle{contain:layout;position:relative;}details.big-toggle{contain:inline-size;}details.toggle>summary.hideme{cursor:pointer;font-size:1rem;}details.toggle>summary{list-style:none;outline:none;}details.toggle>summary::-webkit-details-marker,details.toggle>summary::marker{display:none;}details.toggle>summary.hideme>span{margin-left:9px;}details.toggle>summary::before{background:url('data:image/svg+xml,\ + ');content:"";cursor:pointer;width:16px;height:16px;display:inline-block;vertical-align:middle;opacity:.5;filter:var(--toggle-filter);}details.toggle>summary.hideme>span,.more-examples-toggle summary,.more-examples-toggle .hide-more{color:var(--toggles-color);}details.toggle>summary::after{content:"Expand";overflow:hidden;width:0;height:0;position:absolute;}details.toggle>summary.hideme::after{content:"";}details.toggle>summary:focus::before,details.toggle>summary:hover::before{opacity:1;}details.toggle>summary:focus-visible::before{outline:1px dotted #000;outline-offset:1px;}details.non-exhaustive{margin-bottom:8px;}details.toggle>summary.hideme::before{position:relative;}details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;top:4px;}.impl-items>details.toggle>summary:not(.hideme)::before,#main-content>.methods>details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;}.impl-items>*:not(.item-info),.implementors-toggle>.docblock,#main-content>.methods>:not(.item-info),.impl>.item-info,.impl>.docblock,.impl+.docblock{margin-left:var(--impl-items-indent);}details.big-toggle>summary:not(.hideme)::before{left:-34px;top:9px;}details.toggle[open] >summary.hideme{position:absolute;}details.toggle[open] >summary.hideme>span{display:none;}details.toggle[open] >summary::before{background:url('data:image/svg+xml,\ + ');}details.toggle[open] >summary::after{content:"Collapse";}details.toggle:not([open])>summary .docblock{max-height:calc(1.5em + 0.75em);overflow-y:hidden;}details.toggle:not([open])>summary .docblock>:first-child{max-width:100%;overflow:hidden;width:fit-content;white-space:nowrap;position:relative;padding-right:1em;}details.toggle:not([open])>summary .docblock>:first-child::after{content:"…";position:absolute;right:0;top:0;bottom:0;z-index:1;background-color:var(--main-background-color);font:1rem/1.5 "Source Serif 4",NanumBarunGothic,serif;padding-left:0.2em;}details.toggle:not([open])>summary .docblock>div:first-child::after{padding-top:calc(1.5em + 0.75em - 1.2rem);}details.toggle>summary .docblock{margin-top:0.75em;}.docblock summary>*{display:inline-block;}.docblock>.example-wrap:first-child .tooltip{margin-top:16px;}.src #sidebar-button>a::before,.sidebar-menu-toggle::before{content:var(--hamburger-image);opacity:0.75;filter:var(--mobile-sidebar-menu-filter);}.src #sidebar-button>a:hover{background:var(--main-background-color);}.sidebar-menu-toggle:hover::before,.sidebar-menu-toggle:active::before,.sidebar-menu-toggle:focus::before{opacity:1;}@media (max-width:850px){#search-tabs .count{display:block;}.side-by-side{flex-direction:column-reverse;}.side-by-side>div{width:auto;}.main-heading{grid-template-areas:"main-heading-breadcrumbs main-heading-toolbar" "main-heading-h1 main-heading-toolbar" "main-heading-sub-heading main-heading-toolbar";}.search-results-main-heading{display:grid;grid-template-areas:"main-heading-breadcrumbs main-heading-toolbar" "main-heading-breadcrumbs main-heading-toolbar" "main-heading-h1 main-heading-toolbar";}rustdoc-toolbar{margin-top:-10px;display:grid;grid-template-areas:"x settings help" "search summary summary";grid-template-rows:35px 1fr;}.search-results-main-heading rustdoc-toolbar{display:grid;grid-template-areas:"settings help" "search search";}.search-results-main-heading #toggle-all-docs{display:none;}rustdoc-toolbar .settings-menu span.label,rustdoc-toolbar .help-menu span.label{display:none;}rustdoc-toolbar .settings-menu{grid-area:settings;}rustdoc-toolbar .help-menu{grid-area:help;}rustdoc-toolbar .settings-menu{grid-area:settings;}rustdoc-toolbar #search-button{grid-area:search;}rustdoc-toolbar #toggle-all-docs{grid-area:summary;}rustdoc-toolbar .settings-menu,rustdoc-toolbar .help-menu{height:35px;}rustdoc-toolbar .settings-menu>a,rustdoc-toolbar .help-menu>a{border-radius:2px;text-align:center;width:34px;padding:5px 0;}rustdoc-toolbar .settings-menu>a:before,rustdoc-toolbar .help-menu>a:before{margin:0 4px;}#settings.popover{top:16px;--popover-arrow-offset:58px;}#help.popover{top:16px;--popover-arrow-offset:16px;}}@media (max-width:700px){:root{--impl-items-indent:0.7em;}*[id]{scroll-margin-top:45px;}#copy-path{width:0;visibility:hidden;}rustdoc-topbar span.label,html:not(.hide-sidebar) .rustdoc:not(.src) rustdoc-toolbar .settings-menu>a,html:not(.hide-sidebar) .rustdoc:not(.src) rustdoc-toolbar .help-menu>a{display:none;}rustdoc-topbar .settings-menu>a,rustdoc-topbar .help-menu>a{width:33px;line-height:0;}rustdoc-topbar .settings-menu>a:hover,rustdoc-topbar .help-menu>a:hover{border:none;background:var(--main-background-color);border-radius:0;}#settings.popover{top:32px;--popover-arrow-offset:48px;}#help.popover{top:32px;--popover-arrow-offset:12px;}.rustdoc{display:block;}html:not(.hide-sidebar) main{padding-left:15px;padding-top:0px;}.sidebar .logo-container,.sidebar .location,.sidebar-resizer{display:none;}.sidebar{position:fixed;top:45px;left:-1000px;z-index:11;height:calc(100vh - 45px);border-right:none;width:100%;}.sidebar-elems .block li a{white-space:wrap;}.src main,.rustdoc.src .sidebar{top:0;padding:0;height:100vh;border:0;}html .src main{padding:18px 0;}.src .search-form{margin-left:40px;}.src .main-heading{margin-left:8px;}.hide-sidebar .search-form{margin-left:32px;}.hide-sidebar .src .search-form{margin-left:0;}.sidebar.shown,.src-sidebar-expanded .src .sidebar,.rustdoc:not(.src) .sidebar:focus-within{left:0;}rustdoc-topbar>h2{padding-bottom:0;margin:auto;overflow:hidden;font-size:24px;white-space:nowrap;text-overflow:ellipsis;text-align:center;}rustdoc-topbar .logo-container>img{max-width:35px;max-height:35px;margin:5px 0 5px 20px;}rustdoc-topbar{display:flex;flex-direction:row;position:sticky;z-index:10;height:45px;width:100%;left:0;top:0;}.hide-sidebar rustdoc-topbar{display:none;}.sidebar-menu-toggle{width:41px;min-width:41px;border:none;line-height:0;}.hide-sidebar .sidebar-menu-toggle{display:none;}.sidebar-elems{margin-top:1em;}.anchor{display:none !important;}#main-content>details.toggle>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}#sidebar-button>a::before{content:url('data:image/svg+xml,\ + \ + \ + \ + \ + \ + \ + ');width:22px;height:22px;}.sidebar-menu-toggle:hover{background:var(--main-background-color);}.search-results>a,.search-results>a>div{display:block;}.search-results>a{padding:5px 0px;}.search-results>a>div.desc,.item-table dd{padding-left:2em;}.search-results .result-name{display:block;}.search-results .result-name .typename{width:initial;margin-right:0;}.search-results .result-name .typename,.search-results .result-name .path{display:inline;}.src-sidebar-expanded .src .sidebar{position:fixed;max-width:100vw;width:100vw;}.src .src-sidebar-title{padding-top:0;}details.implementors-toggle:not(.top-doc)>summary{margin-left:10px;}.impl-items>details.toggle>summary:not(.hideme)::before,#main-content>.methods>details.toggle>summary:not(.hideme)::before{left:-20px;}summary>.item-info{margin-left:10px;}.impl-items>.item-info{margin-left:calc(var(--impl-items-indent) + 10px);}.src nav.sub{margin:0 0 -25px 0;padding:var(--nav-sub-mobile-padding);}html:not(.src-sidebar-expanded) .src #sidebar-button>a{background-color:var(--main-background-color);}html:not(.src-sidebar-expanded) .src #sidebar-button>a:hover,html:not(.src-sidebar-expanded) .src #sidebar-button>a:focus-visible{background-color:var(--sidebar-background-color);}}@media (min-width:701px){.scraped-example-title{position:absolute;z-index:10;background:var(--main-background-color);bottom:8px;right:5px;padding:2px 4px;box-shadow:0 0 4px var(--main-background-color);}.item-table:not(.reexports){display:grid;grid-template-columns:33% 67%;}.item-table>dt,.item-table>dd{overflow-wrap:anywhere;}.item-table>dt{grid-column-start:1;}.item-table>dd{grid-column-start:2;}}@media print{:root{--docblock-indent:0;}nav.sidebar,nav.sub,.out-of-band,a.src,#copy-path,details.toggle[open] >summary::before,details.toggle>summary::before,details.toggle.top-doc>summary{display:none;}main{padding:10px;}}@media (max-width:464px){:root{--docblock-indent:12px;}.docblock code{overflow-wrap:break-word;overflow-wrap:anywhere;}nav.sub{flex-direction:column;}.search-form{align-self:stretch;}}.variant,.implementors-toggle>summary,.impl,#implementors-list>.docblock,.impl-items>section,.impl-items>.toggle>summary,.methods>section,.methods>.toggle>summary{margin-bottom:0.75em;}.variants>.docblock,.implementors-toggle>.docblock,.impl-items>.toggle[open]:not(:last-child),.methods>.toggle[open]:not(:last-child),.implementors-toggle[open]:not(:last-child){margin-bottom:2em;}#trait-implementations-list .impl-items>.toggle:not(:last-child),#synthetic-implementations-list .impl-items>.toggle:not(:last-child),#blanket-implementations-list .impl-items>.toggle:not(:last-child){margin-bottom:1em;}.scraped-example-list .scrape-help{margin-left:10px;padding:0 4px;font-weight:normal;font-size:12px;position:relative;bottom:1px;border:1px solid var(--scrape-example-help-border-color);border-radius:50px;color:var(--scrape-example-help-color);}.scraped-example-list .scrape-help:hover{border-color:var(--scrape-example-help-hover-border-color);color:var(--scrape-example-help-hover-color);}.scraped-example:not(.expanded) .example-wrap::before,.scraped-example:not(.expanded) .example-wrap::after{content:" ";width:100%;height:5px;position:absolute;z-index:1;}.scraped-example:not(.expanded) .example-wrap::before{top:0;background:linear-gradient(to bottom,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example:not(.expanded) .example-wrap::after{bottom:0;background:linear-gradient(to top,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example:not(.expanded){width:100%;overflow-y:hidden;margin-bottom:0;}.scraped-example:not(.expanded){overflow-x:hidden;}.scraped-example .rust span.highlight{background:var(--scrape-example-code-line-highlight);}.scraped-example .rust span.highlight.focus{background:var(--scrape-example-code-line-highlight-focus);}.more-examples-toggle{max-width:calc(100% + 25px);margin-top:10px;margin-left:-25px;}.more-examples-toggle .hide-more{margin-left:25px;cursor:pointer;}.more-scraped-examples{margin-left:25px;position:relative;}.toggle-line{position:absolute;top:5px;bottom:0;right:calc(100% + 10px);padding:0 4px;cursor:pointer;}.toggle-line-inner{min-width:2px;height:100%;background:var(--scrape-example-toggle-line-background);}.toggle-line:hover .toggle-line-inner{background:var(--scrape-example-toggle-line-hover-background);}.more-scraped-examples .scraped-example,.example-links{margin-top:20px;}.more-scraped-examples .scraped-example:first-child{margin-top:5px;}.example-links ul{margin-bottom:0;}:root[data-theme="light"],:root:not([data-theme]){--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--sidebar-border-color:#ddd;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--mobile-sidebar-menu-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--code-example-button-color:#7f7f7f;--code-example-button-hover-color:#595959;--settings-menu-filter:invert(50%);--settings-menu-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--attribute-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);--sidebar-resizer-hover:hsl(207,90%,66%);--sidebar-resizer-active:hsl(207,90%,54%);}:root[data-theme="dark"]{--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--sidebar-border-color:#999;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--code-example-button-color:#7f7f7f;--code-example-button-hover-color:#a5a5a5;--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--attribute-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--settings-menu-filter:invert(50%);--settings-menu-hover-filter:invert(65%);--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);--sidebar-resizer-hover:hsl(207,30%,54%);--sidebar-resizer-active:hsl(207,90%,54%);}:root[data-theme="ayu"]{--main-background-color:#0f1419;--main-color:#c5c5c5;--settings-input-color:#ffb454;--settings-input-border-color:#999;--settings-button-color:#fff;--settings-button-border-focus:#e0e0e0;--sidebar-background-color:#14191f;--sidebar-background-color-hover:rgba(70,70,70,0.33);--sidebar-border-color:#5c6773;--code-block-background-color:#191f26;--scrollbar-track-background-color:transparent;--scrollbar-thumb-background-color:#5c6773;--scrollbar-color:#5c6773 #24292f;--headings-border-bottom-color:#5c6773;--border-color:#5c6773;--button-background-color:#141920;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#5c6773;--copy-path-button-color:#fff;--copy-path-img-filter:invert(70%);--copy-path-img-hover-filter:invert(100%);--code-example-button-color:#b2b2b2;--code-example-button-hover-color:#fff;--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ffa0a5;--trait-link-color:#39afd7;--assoc-item-link-color:#39afd7;--function-link-color:#fdd687;--macro-link-color:#a37acc;--keyword-link-color:#39afd7;--attribute-link-color:#39afd7;--mod-link-color:#39afd7;--link-color:#39afd7;--sidebar-link-color:#53b1db;--sidebar-current-link-background-color:transparent;--search-result-link-focus-background-color:#3c3c3c;--search-result-border-color:#aaa3;--search-color:#fff;--search-error-code-background-color:#4f4c4c;--search-results-alias-color:#c5c5c5;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:none;--search-tab-button-not-selected-background:transparent !important;--search-tab-button-selected-border-top-color:none;--search-tab-button-selected-background:#141920 !important;--settings-menu-filter:invert(70%);--settings-menu-hover-filter:invert(100%);--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ff7733;--code-highlight-kw-2-color:#ff7733;--code-highlight-lifetime-color:#ff7733;--code-highlight-prelude-color:#69f2df;--code-highlight-prelude-val-color:#ff7733;--code-highlight-number-color:#b8cc52;--code-highlight-string-color:#b8cc52;--code-highlight-literal-color:#ff7733;--code-highlight-attribute-color:#e6e1cf;--code-highlight-self-color:#36a3d9;--code-highlight-macro-color:#a37acc;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#788797;--code-highlight-doc-comment-color:#a1ac88;--src-line-numbers-span-color:#5c6773;--src-line-number-highlighted-background-color:rgba(255,236,164,0.06);--target-background-color:rgba(255,236,164,0.06);--target-border-color:rgba(255,180,76,0.85);--kbd-color:#c5c5c5;--kbd-background:#314559;--kbd-box-shadow-color:#5c6773;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg) brightness(94%) contrast(94%);--crate-search-div-hover-filter:invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg) brightness(113%) contrast(76%);--crate-search-hover-border:#e0e0e0;--src-sidebar-background-selected:#14191f;--src-sidebar-background-hover:#14191f;--table-alt-row-background-color:#191f26;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(15,20,25,1);--scrape-example-code-wrapper-background-end:rgba(15,20,25,0);--sidebar-resizer-hover:hsl(34,50%,33%);--sidebar-resizer-active:hsl(34,100%,66%);}:root[data-theme="ayu"] h1,:root[data-theme="ayu"] h2,:root[data-theme="ayu"] h3,:root[data-theme="ayu"] h4,:where(:root[data-theme="ayu"]) h1 a,:root[data-theme="ayu"] .sidebar h2 a,:root[data-theme="ayu"] .sidebar h3 a{color:#fff;}:root[data-theme="ayu"] .docblock code{color:#ffb454;}:root[data-theme="ayu"] .docblock a>code{color:#39AFD7 !important;}:root[data-theme="ayu"] .code-header,:root[data-theme="ayu"] .docblock pre>code,:root[data-theme="ayu"] pre,:root[data-theme="ayu"] pre>code,:root[data-theme="ayu"] .item-info code,:root[data-theme="ayu"] .rustdoc.source .example-wrap{color:#e6e1cf;}:root[data-theme="ayu"] .sidebar .current,:root[data-theme="ayu"] .sidebar .current a,:root[data-theme="ayu"] .sidebar a:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:hover,:root[data-theme="ayu"] details.dir-entry summary:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:focus,:root[data-theme="ayu"] details.dir-entry summary:focus,:root[data-theme="ayu"] #src-sidebar div.files>a.selected{color:#ffb44c;}:root[data-theme="ayu"] .sidebar-elems .location{color:#ff7733;}:root[data-theme="ayu"] a[data-nosnippet].line-highlighted{color:#708090;padding-right:7px;border-right:1px solid #ffb44c;}:root[data-theme="ayu"] .search-results a:hover,:root[data-theme="ayu"] .search-results a:focus{color:#fff !important;background-color:#3c3c3c;}:root[data-theme="ayu"] .search-results a{color:#0096cf;}:root[data-theme="ayu"] .search-results a div.desc{color:#c5c5c5;}:root[data-theme="ayu"] .result-name .primitive>i,:root[data-theme="ayu"] .result-name .keyword>i{color:#788797;}:root[data-theme="ayu"] #search-tabs>button.selected{border-bottom:1px solid #ffb44c !important;border-top:none;}:root[data-theme="ayu"] #search-tabs>button:not(.selected){border:none;background-color:transparent !important;}:root[data-theme="ayu"] #search-tabs>button:hover{border-bottom:1px solid rgba(242,151,24,0.3);}:root[data-theme="ayu"] .settings-menu>a img,:root[data-theme="ayu"] #sidebar-button>a::before{filter:invert(100);} \ No newline at end of file diff --git a/pr/395/docs/static.files/scrape-examples-2bbcccac.js b/pr/395/docs/static.files/scrape-examples-2bbcccac.js new file mode 100644 index 000000000..c90f297f9 --- /dev/null +++ b/pr/395/docs/static.files/scrape-examples-2bbcccac.js @@ -0,0 +1 @@ +"use strict";(function(){const DEFAULT_MAX_LINES=5;const HIDDEN_MAX_LINES=10;function scrollToLoc(elt,loc,isHidden){const lines=elt.querySelectorAll("[data-nosnippet]");let scrollOffset;const maxLines=isHidden?HIDDEN_MAX_LINES:DEFAULT_MAX_LINES;if(loc[1]-loc[0]>maxLines){const line=Math.max(0,loc[0]-1);scrollOffset=lines[line].offsetTop;}else{const halfHeight=elt.offsetHeight/2;const offsetTop=lines[loc[0]].offsetTop;const lastLine=lines[loc[1]];const offsetBot=lastLine.offsetTop+lastLine.offsetHeight;const offsetMid=(offsetTop+offsetBot)/2;scrollOffset=offsetMid-halfHeight;}nonnull(lines[0].parentElement).scrollTo(0,scrollOffset);nonnull(elt.querySelector(".rust")).scrollTo(0,scrollOffset);}function createScrapeButton(parent,className,content){const button=document.createElement("button");button.className=className;button.title=content;parent.insertBefore(button,parent.firstChild);return button;}window.updateScrapedExample=(example,buttonHolder)=>{let locIndex=0;const highlights=Array.prototype.slice.call(example.querySelectorAll(".highlight"));const link=nonnull(example.querySelector(".scraped-example-title a"));let expandButton=null;if(!example.classList.contains("expanded")){expandButton=createScrapeButton(buttonHolder,"expand","Show all");}const isHidden=nonnull(example.parentElement).classList.contains("more-scraped-examples");const locs=example.locs;if(locs.length>1){const next=createScrapeButton(buttonHolder,"next","Next usage");const prev=createScrapeButton(buttonHolder,"prev","Previous usage");const onChangeLoc=changeIndex=>{removeClass(highlights[locIndex],"focus");changeIndex();scrollToLoc(example,locs[locIndex][0],isHidden);addClass(highlights[locIndex],"focus");const url=locs[locIndex][1];const title=locs[locIndex][2];link.href=url;link.innerHTML=title;};prev.addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex-1+locs.length)%locs.length;});});next.addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex+1)%locs.length;});});}if(expandButton){expandButton.addEventListener("click",()=>{if(hasClass(example,"expanded")){removeClass(example,"expanded");removeClass(expandButton,"collapse");expandButton.title="Show all";scrollToLoc(example,locs[0][0],isHidden);}else{addClass(example,"expanded");addClass(expandButton,"collapse");expandButton.title="Show single example";}});}};function setupLoc(example,isHidden){const locs_str=nonnull(example.attributes.getNamedItem("data-locs")).textContent;const locs=JSON.parse(nonnull(nonnull(locs_str)));example.locs=locs;scrollToLoc(example,locs[0][0],isHidden);}const firstExamples=document.querySelectorAll(".scraped-example-list > .scraped-example");onEachLazy(firstExamples,el=>setupLoc(el,false));onEachLazy(document.querySelectorAll(".more-examples-toggle"),toggle=>{onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"),button=>{button.addEventListener("click",()=>{toggle.open=false;});});const moreExamples=toggle.querySelectorAll(".scraped-example");toggle.querySelector("summary").addEventListener("click",()=>{setTimeout(()=>{onEachLazy(moreExamples,el=>setupLoc(el,true));});},{once:true});});})(); \ No newline at end of file diff --git a/pr/395/docs/static.files/search-8d3311b9.js b/pr/395/docs/static.files/search-8d3311b9.js new file mode 100644 index 000000000..5da8debd8 --- /dev/null +++ b/pr/395/docs/static.files/search-8d3311b9.js @@ -0,0 +1,5 @@ +"use strict";const initSearch=async function(Stringdex,RoaringBitmap,hooks){if(!Array.prototype.toSpliced){Array.prototype.toSpliced=function(){const me=this.slice();Array.prototype.splice.apply(me,arguments);return me;};}async function onEachBtwnAsync(arr,func,funcBtwn){let skipped=true;for(const value of arr){if(!skipped){funcBtwn(value);}skipped=await func(value);}}const yieldToBrowser=typeof window!=="undefined"&&window.requestIdleCallback?function(){return new Promise((resolve,_reject)=>{window.requestIdleCallback(resolve);});}:function(){return new Promise((resolve,_reject)=>{setTimeout(resolve,0);});};const timeout=function(ms){return new Promise((resolve,_reject)=>{setTimeout(resolve,ms);});};if(!Promise.withResolvers){Promise.withResolvers=()=>{let resolve,reject;const promise=new Promise((res,rej)=>{resolve=res;reject=rej;});return{promise,resolve,reject};};}const itemTypes=["keyword","primitive","mod","externcrate","import","struct","enum","fn","type","static","trait","impl","tymethod","method","structfield","variant","macro","associatedtype","constant","associatedconstant","union","foreigntype","existential","attr","derive","traitalias","generic","attribute",];const TY_PRIMITIVE=1;const TY_GENERIC=26;const TY_IMPORT=4;const TY_TRAIT=10;const TY_FN=7;const TY_METHOD=13;const TY_TYMETHOD=12;const TY_ASSOCTYPE=17;const ROOT_PATH=typeof window!=="undefined"?window.rootPath:"../";const UNBOXING_LIMIT=5;const REGEX_IDENT=/\p{ID_Start}\p{ID_Continue}*|_\p{ID_Continue}+/uy;const REGEX_INVALID_TYPE_FILTER=/[^a-z]/ui;const MAX_RESULTS=200;const NO_TYPE_FILTER=-1;const editDistanceState={current:[],prev:[],prevPrev:[],calculate:function calculate(a,b,limit){if(a.lengthlimit){return limit+1;}while(b.length>0&&b[0]===a[0]){a=a.substring(1);b=b.substring(1);}while(b.length>0&&b[b.length-1]===a[a.length-1]){a=a.substring(0,a.length-1);b=b.substring(0,b.length-1);}if(b.length===0){return minDist;}const aLength=a.length;const bLength=b.length;for(let i=0;i<=bLength;++i){this.current[i]=0;this.prev[i]=i;this.prevPrev[i]=Number.MAX_VALUE;}for(let i=1;i<=aLength;++i){this.current[0]=i;const aIdx=i-1;for(let j=1;j<=bLength;++j){const bIdx=j-1;const substitutionCost=a[aIdx]===b[bIdx]?0:1;this.current[j]=Math.min(this.prev[j]+1,this.current[j-1]+1,this.prev[j-1]+substitutionCost,);if((i>1)&&(j>1)&&(a[aIdx]===b[bIdx-1])&&(a[aIdx-1]===b[bIdx])){this.current[j]=Math.min(this.current[j],this.prevPrev[j-2]+1,);}}const prevPrevTmp=this.prevPrev;this.prevPrev=this.prev;this.prev=this.current;this.current=prevPrevTmp;}const distance=this.prev[bLength];return distance<=limit?distance:(limit+1);},};function editDistance(a,b,limit){return editDistanceState.calculate(a,b,limit);}function isEndCharacter(c){return"=,>-])".indexOf(c)!==-1;}function isFnLikeTy(ty){return ty===TY_FN||ty===TY_METHOD||ty===TY_TYMETHOD;}function isSeparatorCharacter(c){return c===","||c==="=";}function isReturnArrow(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="->";}function skipWhitespace(parserState){while(parserState.pos0){const c=parserState.userQuery[pos-1];if(c===lookingFor){return true;}else if(c!==" "){break;}pos-=1;}return false;}function isLastElemGeneric(elems,parserState){return(elems.length>0&&elems[elems.length-1].generics.length>0)||prevIs(parserState,">");}function getFilteredNextElem(query,parserState,elems,isInGenerics){const start=parserState.pos;if(parserState.userQuery[parserState.pos]===":"&&!isPathStart(parserState)){throw["Expected type filter before ",":"];}getNextElem(query,parserState,elems,isInGenerics);if(parserState.userQuery[parserState.pos]===":"&&!isPathStart(parserState)){if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",];}if(elems.length===0){throw["Expected type filter before ",":"];}else if(query.literalSearch){throw["Cannot use quotes on type filter"];}const typeFilterElem=elems.pop();checkExtraTypeFilterCharacters(start,parserState);parserState.typeFilter=typeFilterElem.normalizedPathLast;parserState.pos+=1;parserState.totalElems-=1;query.literalSearch=false;getNextElem(query,parserState,elems,isInGenerics);}}function getItemsBefore(query,parserState,elems,endChar){let foundStopChar=true;let foundSeparator=false;const oldTypeFilter=parserState.typeFilter;parserState.typeFilter=null;const oldIsInBinding=parserState.isInBinding;parserState.isInBinding=null;let hofParameters=null;let extra="";if(endChar===">"){extra="<";}else if(endChar==="]"){extra="[";}else if(endChar===")"){extra="(";}else if(endChar===""){extra="->";}else{extra=endChar;}while(parserState.pos"," after ","="];}hofParameters=[...elems];elems.length=0;parserState.pos+=2;foundStopChar=true;foundSeparator=false;continue;}else if(c===" "){parserState.pos+=1;continue;}else if(isSeparatorCharacter(c)){parserState.pos+=1;foundStopChar=true;foundSeparator=true;continue;}else if(c===":"&&isPathStart(parserState)){throw["Unexpected ","::",": paths cannot start with ","::"];}else if(isEndCharacter(c)){throw["Unexpected ",c," after ",extra];}if(!foundStopChar){let extra=[];if(isLastElemGeneric(query.elems,parserState)){extra=[" after ",">"];}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"];}if(endChar!==""){throw["Expected ",",",", ","=",", or ",endChar,...extra,", found ",c,];}throw["Expected ",","," or ","=",...extra,", found ",c,];}const posBefore=parserState.pos;getFilteredNextElem(query,parserState,elems,endChar!=="");if(endChar!==""&&parserState.pos>=parserState.length){throw["Unclosed ",extra];}if(posBefore===parserState.pos){parserState.pos+=1;}foundStopChar=false;}if(parserState.pos>=parserState.length&&endChar!==""){throw["Unclosed ",extra];}parserState.pos+=1;if(hofParameters){foundSeparator=false;if([...elems,...hofParameters].some(x=>x.bindingName)||parserState.isInBinding){throw["Unexpected ","="," within ","->"];}const hofElem=makePrimitiveElement("->",{generics:hofParameters,bindings:new Map([["output",[...elems]]]),typeFilter:null,});elems.length=0;elems[0]=hofElem;}parserState.typeFilter=oldTypeFilter;parserState.isInBinding=oldIsInBinding;return{foundSeparator};}function getNextElem(query,parserState,elems,isInGenerics){const generics=[];const handleRefOrPtr=(chr,name)=>{if(parserState.typeFilter!==null&&parserState.typeFilter!=="primitive"){throw["Invalid search type: primitive ",chr," and ",parserState.typeFilter," both specified",];}parserState.typeFilter=null;parserState.pos+=1;let c=parserState.userQuery[parserState.pos];while(c===" "&&parserState.pos"){generics[0].typeFilter=typeFilter;elems.push(generics[0]);}else{if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive ",name," and ",typeFilter," both specified",];}parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1;}elems.push(makePrimitiveElement(name,{bindingName,generics}));}}else if(parserState.userQuery[parserState.pos]==="&"){handleRefOrPtr("&","reference");}else if(parserState.userQuery[parserState.pos]==="*"){handleRefOrPtr("*","pointer");}else{const isStringElem=parserState.userQuery[start]==="\"";if(isStringElem){start+=1;getStringElem(query,parserState,isInGenerics);end=parserState.pos-1;}else{end=getIdentEndPosition(parserState);}if(parserState.pos=end){throw["Found generics without a path"];}parserState.pos+=1;getItemsBefore(query,parserState,generics,">");}else if(parserState.pos=end){throw["Found generics without a path"];}if(parserState.isInBinding){throw["Unexpected ","("," after ","="];}parserState.pos+=1;const typeFilter=parserState.typeFilter;parserState.typeFilter=null;getItemsBefore(query,parserState,generics,")");skipWhitespace(parserState);if(isReturnArrow(parserState)){parserState.pos+=2;skipWhitespace(parserState);getFilteredNextElem(query,parserState,generics,isInGenerics);generics[generics.length-1].bindingName=makePrimitiveElement("output");}else{generics.push(makePrimitiveElement(null,{bindingName:makePrimitiveElement("output"),typeFilter:null,}));}parserState.typeFilter=typeFilter;}if(isStringElem){skipWhitespace(parserState);}if(start>=end&&generics.length===0){return;}if(parserState.userQuery[parserState.pos]==="="){if(parserState.isInBinding){throw["Cannot write ","="," twice in a binding"];}if(!isInGenerics){throw["Type parameter ","="," must be within generics list"];}const name=parserState.userQuery.slice(start,end).trim();if(name==="!"){throw["Type parameter ","="," key cannot be ","!"," never type"];}if(name.includes("!")){throw["Type parameter ","="," key cannot be ","!"," macro"];}if(name.includes("::")){throw["Type parameter ","="," key cannot contain ","::"," path"];}if(name.includes(":")){throw["Type parameter ","="," key cannot contain ",":"," type"];}parserState.isInBinding={name,generics};}else{elems.push(createQueryElement(query,parserState,parserState.userQuery.slice(start,end),generics,isInGenerics,),);}}}function checkExtraTypeFilterCharacters(start,parserState){const query=parserState.userQuery.slice(start,parserState.pos).trim();const match=query.match(REGEX_INVALID_TYPE_FILTER);if(match){throw["Unexpected ",match[0]," in type filter (before ",":",")",];}}function createQueryElement(query,parserState,name,generics,isInGenerics){const path=name.trim();if(path.length===0&&generics.length===0){throw["Unexpected ",parserState.userQuery[parserState.pos]];}if(query.literalSearch&&parserState.totalElems-parserState.genericsElems>0){throw["Cannot have more than one element if you use quotes"];}const typeFilter=parserState.typeFilter;parserState.typeFilter=null;if(name.trim()==="!"){if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive never type ","!"," and ",typeFilter," both specified",];}if(generics.length!==0){throw["Never type ","!"," does not accept generic parameters",];}const bindingName=parserState.isInBinding;parserState.isInBinding=null;return makePrimitiveElement("never",{bindingName});}const quadcolon=/::\s*::/.exec(path);if(path.startsWith("::")){throw["Paths cannot start with ","::"];}else if(quadcolon!==null){throw["Unexpected ",quadcolon[0]];}const pathSegments=path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/).map(x=>x.toLowerCase());if(pathSegments.length===0||(pathSegments.length===1&&pathSegments[0]==="")){if(generics.length>0||prevIs(parserState,">")){throw["Found generics without a path"];}else{throw["Unexpected ",parserState.userQuery[parserState.pos]];}}for(const[i,pathSegment]of pathSegments.entries()){if(pathSegment==="!"){if(i!==0){throw["Never type ","!"," is not associated item"];}pathSegments[i]="never";}}parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1;}const bindingName=parserState.isInBinding;parserState.isInBinding=null;const bindings=new Map();const pathLast=pathSegments[pathSegments.length-1];return{name:name.trim(),id:null,fullPath:pathSegments,pathWithoutLast:pathSegments.slice(0,pathSegments.length-1),pathLast,normalizedPathLast:pathLast.replace(/_/g,""),generics:generics.filter(gen=>{if(gen.bindingName!==null&&gen.bindingName.name!==null){if(gen.name!==null){gen.bindingName.generics.unshift(gen);}bindings.set(gen.bindingName.name.toLowerCase().replace(/_/g,""),gen.bindingName.generics,);return false;}return true;}),bindings,typeFilter,bindingName,};}function makePrimitiveElement(name,extra){return Object.assign({name,id:null,fullPath:[name],pathWithoutLast:[],pathLast:name,normalizedPathLast:name,generics:[],bindings:new Map(),typeFilter:"primitive",bindingName:null,},extra);}function getStringElem(query,parserState,isInGenerics){if(isInGenerics){throw["Unexpected ","\""," in generics"];}else if(query.literalSearch){throw["Cannot have more than one literal search element"];}else if(parserState.totalElems-parserState.genericsElems>0){throw["Cannot use literal search when there is more than one element"];}parserState.pos+=1;const start=parserState.pos;const end=getIdentEndPosition(parserState);if(parserState.pos>=parserState.length){throw["Unclosed ","\""];}else if(parserState.userQuery[end]!=="\""){throw["Unexpected ",parserState.userQuery[end]," in a string element"];}else if(start===end){throw["Cannot have empty string element"];}parserState.pos+=1;query.literalSearch=true;}function getIdentEndPosition(parserState){let afterIdent=consumeIdent(parserState);let end=parserState.pos;let macroExclamation=-1;while(parserState.pos0){throw["Unexpected ",c," after ",parserState.userQuery[parserState.pos-1]," (not a valid identifier)"];}else{throw["Unexpected ",c," (not a valid identifier)"];}parserState.pos+=1;afterIdent=consumeIdent(parserState);end=parserState.pos;}if(macroExclamation!==-1){if(parserState.typeFilter===null){parserState.typeFilter="macro";}else if(parserState.typeFilter!=="macro"){throw["Invalid search type: macro ","!"," and ",parserState.typeFilter," both specified",];}end=macroExclamation;}return end;}function isSpecialStartCharacter(c){return"<\"".indexOf(c)!==-1;}function isPathStart(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="::";}function consumeIdent(parserState){REGEX_IDENT.lastIndex=parserState.pos;const match=parserState.userQuery.match(REGEX_IDENT);if(match){parserState.pos+=match[0].length;return true;}return false;}function isPathSeparator(c){return c===":"||c===" ";}function removeIdxListAsc(a,idxList){if(idxList.length===0){return;}let removed=0;let i=idxList[0];let nextToRemove=idxList[0];while(i>1];this.offset+=1;this.elemCount+=1;return sign?-value:value;}next(){const c=this.string.charCodeAt(this.offset);if(c>=48&&c<64){this.offset+=1;return this.backrefQueue[c-48];}if(c===96){this.offset+=1;return this.cons(0);}const result=this.cons(this.decode());this.backrefQueue.unshift(result);if(this.backrefQueue.length>16){this.backrefQueue.pop();}return result;}}const EMPTY_STRING_ARRAY=[];const EMPTY_GENERICS_ARRAY=[];const EMPTY_BINDINGS_ARRAY=[];const EMPTY_BINDINGS_MAP=new Map();function itemTypeFromName(typename){if(typename===null){return NO_TYPE_FILTER;}const index=itemTypes.findIndex(i=>i===typename);if(index<0){throw["Unknown type filter ",typename];}return index;}class DocSearch{constructor(rootPath,database){this.rootPath=rootPath;this.database=database;this.utf8decoder=new TextDecoder();this.TYPES_POOL=new Map();}getTypeNameIds(){if(this.typeNameIds){return this.typeNameIds;}const nn=this.database.getData("normalizedName");if(!nn){return{typeNameIdOfOutput:-1,typeNameIdOfFnPtr:-1,typeNameIdOfFn:-1,typeNameIdOfFnMut:-1,typeNameIdOfFnOnce:-1,typeNameIdOfArray:-1,typeNameIdOfSlice:-1,typeNameIdOfArrayOrSlice:-1,typeNameIdOfTuple:-1,typeNameIdOfUnit:-1,typeNameIdOfTupleOrUnit:-1,typeNameIdOfReference:-1,typeNameIdOfPointer:-1,typeNameIdOfHof:-1,typeNameIdOfNever:-1,};}return this.getTypeNameIdsAsync(nn);}async getTypeNameIdsAsync(nn){const[output,fn,fnMut,fnOnce,hof,array,slice,arrayOrSlice,tuple,unit,tupleOrUnit,reference,pointer,never,]=await Promise.all([nn.search("output"),nn.search("fn"),nn.search("fnmut"),nn.search("fnonce"),nn.search("->"),nn.search("array"),nn.search("slice"),nn.search("[]"),nn.search("tuple"),nn.search("unit"),nn.search("()"),nn.search("reference"),nn.search("pointer"),nn.search("never"),]);const first=async(trie,ty,modulePath)=>{if(trie){for(const id of trie.matches().entries()){const pathData=await this.getPathData(id);if(pathData&&pathData.ty===ty&&pathData.modulePath===modulePath){return id;}}}return-1;};const typeNameIdOfOutput=await first(output,TY_ASSOCTYPE,"");const typeNameIdOfFnPtr=await first(fn,TY_PRIMITIVE,"");const typeNameIdOfFn=await first(fn,TY_TRAIT,"core::ops");const typeNameIdOfFnMut=await first(fnMut,TY_TRAIT,"core::ops");const typeNameIdOfFnOnce=await first(fnOnce,TY_TRAIT,"core::ops");const typeNameIdOfArray=await first(array,TY_PRIMITIVE,"");const typeNameIdOfSlice=await first(slice,TY_PRIMITIVE,"");const typeNameIdOfArrayOrSlice=await first(arrayOrSlice,TY_PRIMITIVE,"");const typeNameIdOfTuple=await first(tuple,TY_PRIMITIVE,"");const typeNameIdOfUnit=await first(unit,TY_PRIMITIVE,"");const typeNameIdOfTupleOrUnit=await first(tupleOrUnit,TY_PRIMITIVE,"");const typeNameIdOfReference=await first(reference,TY_PRIMITIVE,"");const typeNameIdOfPointer=await first(pointer,TY_PRIMITIVE,"");const typeNameIdOfHof=await first(hof,TY_PRIMITIVE,"");const typeNameIdOfNever=await first(never,TY_PRIMITIVE,"");this.typeNameIds={typeNameIdOfOutput,typeNameIdOfFnPtr,typeNameIdOfFn,typeNameIdOfFnMut,typeNameIdOfFnOnce,typeNameIdOfArray,typeNameIdOfSlice,typeNameIdOfArrayOrSlice,typeNameIdOfTuple,typeNameIdOfUnit,typeNameIdOfTupleOrUnit,typeNameIdOfReference,typeNameIdOfPointer,typeNameIdOfHof,typeNameIdOfNever,};return this.typeNameIds;}static parseQuery(userQuery){function newParsedQuery(userQuery){return{userQuery,elems:[],returned:[],foundElems:0,totalElems:0,literalSearch:false,hasReturnArrow:false,error:null,correction:null,proposeCorrectionFrom:null,proposeCorrectionTo:null,typeFingerprint:new Uint32Array(4),};}function parseInput(query,parserState){let foundStopChar=true;while(parserState.pos"){if(isReturnArrow(parserState)){query.hasReturnArrow=true;break;}throw["Unexpected ",c," (did you mean ","->","?)"];}else if(parserState.pos>0){throw["Unexpected ",c," after ",parserState.userQuery[parserState.pos-1]];}throw["Unexpected ",c];}else if(c===" "){skipWhitespace(parserState);continue;}if(!foundStopChar){let extra=EMPTY_STRING_ARRAY;if(isLastElemGeneric(query.elems,parserState)){extra=[" after ",">"];}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"];}if(parserState.typeFilter!==null){throw["Expected ",","," or ","->",...extra,", found ",c,];}throw["Expected ",",",", ",":"," or ","->",...extra,", found ",c,];}const before=query.elems.length;getFilteredNextElem(query,parserState,query.elems,false);if(query.elems.length===before){parserState.pos+=1;}foundStopChar=false;}if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",];}while(parserState.pos{const ty=itemTypeFromName(elem.typeFilter);if(ty===TY_GENERIC&&elem.generics.length!==0){throw["Generic type parameter ",elem.name," does not accept generic parameters",];}for(const generic of elem.generics){checkTypeFilter(generic);}for(const constraints of elem.bindings.values()){for(const constraint of constraints){checkTypeFilter(constraint);}}};for(const elem of query.elems){checkTypeFilter(elem);}for(const elem of query.returned){checkTypeFilter(elem);}}catch(err){query=newParsedQuery(userQuery);if(Array.isArray(err)&&err.every(elem=>typeof elem==="string")){query.error=err;}else{throw err;}return query;}if(!query.literalSearch){query.literalSearch=parserState.totalElems>1;}query.foundElems=query.elems.length+query.returned.length;query.totalElems=parserState.totalElems;return query;}async getName(id){const ni=this.database.getData("name");if(!ni){return null;}const name=await ni.at(id);return name===undefined||name===null?null:this.utf8decoder.decode(name);}async getDesc(id){const di=this.database.getData("desc");if(!di){return null;}const desc=await di.at(id);return desc===undefined||desc===null?null:this.utf8decoder.decode(desc);}async getAliasTarget(id){const ai=this.database.getData("alias");if(!ai){return null;}const bytes=await ai.at(id);if(bytes===undefined||bytes===null||bytes.length===0){return null;}else{const encoded=this.utf8decoder.decode(bytes);const decoded=JSON.parse(encoded);return decoded;}}async getEntryData(id){const ei=this.database.getData("entry");if(!ei){return null;}const encoded=this.utf8decoder.decode(await ei.at(id));if(encoded===""||encoded===undefined||encoded===null){return null;}const raw=JSON.parse(encoded);return{krate:raw[0],ty:raw[1],modulePath:raw[2]===0?null:raw[2]-1,exactModulePath:raw[3]===0?null:raw[3]-1,parent:raw[4]===0?null:raw[4]-1,traitParent:raw[5]===0?null:raw[5]-1,deprecated:raw[6]===1?true:false,associatedItemDisambiguator:raw.length===7?null:raw[7],};}async getPathData(id){const pi=this.database.getData("path");if(!pi){return null;}const encoded=this.utf8decoder.decode(await pi.at(id));if(encoded===""||encoded===undefined||encoded===null){return null;}const raw=JSON.parse(encoded);return{ty:raw[0],modulePath:raw[1],exactModulePath:raw[2]===0||raw[2]===undefined?raw[1]:raw[2],};}async getFunctionData(id){const fi=this.database.getData("function");if(!fi){return null;}const encoded=this.utf8decoder.decode(await fi.at(id));if(encoded===""||encoded===undefined||encoded===null){return null;}const raw=JSON.parse(encoded);const parser=new VlqHexDecoder(raw[0],async functionSearchType=>{if(typeof functionSearchType==="number"){return null;}const INPUTS_DATA=0;const OUTPUT_DATA=1;let inputs_;let output_;if(typeof functionSearchType[INPUTS_DATA]==="number"){inputs_=Promise.all([this.buildItemSearchType(functionSearchType[INPUTS_DATA]),]);}else{inputs_=this.buildItemSearchTypeAll(functionSearchType[INPUTS_DATA]);}if(functionSearchType.length>1){if(typeof functionSearchType[OUTPUT_DATA]==="number"){output_=Promise.all([this.buildItemSearchType(functionSearchType[OUTPUT_DATA]),]);}else{output_=this.buildItemSearchTypeAll(functionSearchType[OUTPUT_DATA]);}}else{output_=Promise.resolve(EMPTY_GENERICS_ARRAY);}const where_clause_=[];const l=functionSearchType.length;for(let i=2;i{const[name,path]=entry!==null&&entry[field]!==null?await Promise.all([this.getName(entry[field]),this.getPathData(entry[field])]):[null,null];if(name!==null&&path!==null){return{name,path};}return null;};const[moduleName,modulePathData,exactModuleName,exactModulePathData,parent,traitParent,crateOrNull,]=await Promise.all([entry&&entry.modulePath!==null?this.getName(entry.modulePath):null,entry&&entry.modulePath!==null?this.getPathData(entry.modulePath):null,entry&&entry.exactModulePath!==null?this.getName(entry.exactModulePath):null,entry&&entry.exactModulePath!==null?this.getPathData(entry.exactModulePath):null,buildParentLike("parent"),buildParentLike("traitParent"),entry?this.getName(entry.krate):"",]);const crate=crateOrNull===null?"":crateOrNull;const name=name_===null?"":name_;const normalizedName=(name.indexOf("_")===-1?name:name.replace(/_/g,"")).toLowerCase();const modulePath=modulePathData===null||moduleName===null?"":(modulePathData.modulePath===""?moduleName:`${modulePathData.modulePath}::${moduleName}`);return{id,crate,ty:entry?entry.ty:nonnull(path).ty,name,normalizedName,modulePath,exactModulePath:exactModulePathData===null||exactModuleName===null?modulePath:(exactModulePathData.exactModulePath===""?exactModuleName:`${exactModulePathData.exactModulePath}::${exactModuleName}`),entry,path,functionData,deprecated:entry?entry.deprecated:false,parent,traitParent,};}async buildItemSearchTypeAll(types){return types&&types.length>0?await Promise.all(types.map(type=>this.buildItemSearchType(type))):EMPTY_GENERICS_ARRAY;}async buildItemSearchType(type){const PATH_INDEX_DATA=0;const GENERICS_DATA=1;const BINDINGS_DATA=2;let id,generics;let bindings;if(typeof type==="number"){id=type;generics=EMPTY_GENERICS_ARRAY;bindings=EMPTY_BINDINGS_MAP;}else{id=type[PATH_INDEX_DATA];generics=await this.buildItemSearchTypeAll(type[GENERICS_DATA]);if(type[BINDINGS_DATA]&&type[BINDINGS_DATA].length>0){bindings=new Map((await Promise.all(type[BINDINGS_DATA].map(async binding=>{const[assocType,constraints]=binding;const[k,v]=await Promise.all([this.buildItemSearchType(assocType).then(t=>t.id),this.buildItemSearchTypeAll(constraints),]);return k===null?EMPTY_BINDINGS_ARRAY:[[k,v]];},))).flat());}else{bindings=EMPTY_BINDINGS_MAP;}}let result;if(id<0){result={id,name:"",ty:TY_GENERIC,path:null,exactPath:null,generics,bindings,unboxFlag:true,};}else if(id===0){result={id:null,name:"",ty:TY_GENERIC,path:null,exactPath:null,generics,bindings,unboxFlag:true,};}else{const[name,path,type]=await Promise.all([this.getName(id-1),this.getPathData(id-1),this.getTypeData(id-1),]);if(path===undefined||path===null||type===undefined||type===null){return{id:null,name:"",ty:TY_GENERIC,path:null,exactPath:null,generics,bindings,unboxFlag:true,};}result={id:id-1,name,ty:path.ty,path:path.modulePath,exactPath:path.exactModulePath===null?path.modulePath:path.exactModulePath,generics,bindings,unboxFlag:type.searchUnbox,};}const cr=this.TYPES_POOL.get(result.id);if(cr){if(cr.generics.length===result.generics.length&&cr.generics!==result.generics&&cr.generics.every((x,i)=>result.generics[i]===x)){result.generics=cr.generics;}if(cr.bindings.size===result.bindings.size&&cr.bindings!==result.bindings){let ok=true;for(const[k,v]of cr.bindings.entries()){const v2=result.bindings.get(k);if(!v2){ok=false;break;}if(v!==v2&&v.length===v2.length&&v.every((x,i)=>v2[i]===x)){result.bindings.set(k,v);}else if(v!==v2){ok=false;break;}}if(ok){result.bindings=cr.bindings;}}if(cr.ty===result.ty&&cr.path===result.path&&cr.bindings===result.bindings&&cr.generics===result.generics&&cr.ty===result.ty&&cr.name===result.name&&cr.unboxFlag===result.unboxFlag){return cr;}}this.TYPES_POOL.set(result.id,result);return result;}async execQuery(parsedQuery,filterCrates,currentCrate){const queryLen=parsedQuery.elems.reduce((acc,next)=>acc+next.pathLast.length,0)+parsedQuery.returned.reduce((acc,next)=>acc+next.pathLast.length,0);const maxEditDistance=Math.floor(queryLen/3);const buildHrefAndPath=item=>{let displayPath;let href;let traitPath=null;const type=itemTypes[item.ty];const name=item.name;let path=item.modulePath;let exactPath=item.exactModulePath;if(type==="mod"){displayPath=path+"::";href=this.rootPath+path.replace(/::/g,"/")+"/"+name+"/index.html";}else if(type==="import"){displayPath=item.modulePath+"::";href=this.rootPath+item.modulePath.replace(/::/g,"/")+"/index.html#reexport."+name;}else if(type==="primitive"||type==="keyword"||type==="attribute"){displayPath="";exactPath="";href=this.rootPath+path.replace(/::/g,"/")+"/"+type+"."+name+".html";}else if(type==="externcrate"){displayPath="";href=this.rootPath+name+"/index.html";}else if(item.parent){const myparent=item.parent;let anchor=type+"."+name;const parentType=itemTypes[myparent.path.ty];let pageType=parentType;let pageName=myparent.name;exactPath=`${myparent.path.exactModulePath}::${myparent.name}`;if(parentType==="primitive"){displayPath=myparent.name+"::";exactPath=myparent.name;}else if(type==="structfield"&&parentType==="variant"){const enumNameIdx=item.modulePath.lastIndexOf("::");const enumName=item.modulePath.substr(enumNameIdx+2);path=item.modulePath.substr(0,enumNameIdx);displayPath=path+"::"+enumName+"::"+myparent.name+"::";anchor="variant."+myparent.name+".field."+name;pageType="enum";pageName=enumName;}else{displayPath=path+"::"+myparent.name+"::";}if(item.entry&&item.entry.associatedItemDisambiguator!==null){anchor=item.entry.associatedItemDisambiguator+"/"+anchor;}href=this.rootPath+path.replace(/::/g,"/")+"/"+pageType+"."+pageName+".html#"+anchor;}else{displayPath=item.modulePath+"::";href=this.rootPath+item.modulePath.replace(/::/g,"/")+"/"+type+"."+name+".html";}if(item.traitParent){const tparent=item.traitParent;traitPath=`${tparent.path.exactModulePath}::${tparent.name}::${name}`;}return[displayPath,href,`${exactPath}::${name}`,traitPath];};function pathSplitter(path){const tmp=""+path.replace(/::/g,"::");if(tmp.endsWith("")){return tmp.slice(0,tmp.length-6);}return tmp;}const formatDisplayTypeSignature=async(obj,typeInfo,elems,returned)=>{const typeNameIds=await this.getTypeNameIds();const objType=obj.type;if(!objType){return{type:[],mappedNames:new Map(),whereClause:new Map()};}let fnInputs=null;let fnOutput=null;let mgens=null;if(typeInfo!=="elems"&&typeInfo!=="returned"){fnInputs=unifyFunctionTypes(objType.inputs,elems,objType.where_clause,null,mgensScratch=>{fnOutput=unifyFunctionTypes(objType.output,returned,objType.where_clause,mgensScratch,mgensOut=>{mgens=mgensOut;return true;},0,typeNameIds,);return!!fnOutput;},0,typeNameIds,);}else{const highlighted=unifyFunctionTypes(typeInfo==="elems"?objType.inputs:objType.output,typeInfo==="elems"?elems:returned,objType.where_clause,null,mgensOut=>{mgens=mgensOut;return true;},0,typeNameIds,);if(typeInfo==="elems"){fnInputs=highlighted;}else{fnOutput=highlighted;}}if(!fnInputs){fnInputs=objType.inputs;}if(!fnOutput){fnOutput=objType.output;}const mappedNames=new Map();const whereClause=new Map();const fnParamNames=obj.paramNames||[];const queryParamNames=[];const remapQuery=queryElem=>{if(queryElem.id!==null&&queryElem.id<0){queryParamNames[-1-queryElem.id]=queryElem.name;}if(queryElem.generics.length>0){queryElem.generics.forEach(remapQuery);}if(queryElem.bindings.size>0){[...queryElem.bindings.values()].flat().forEach(remapQuery);}};elems.forEach(remapQuery);returned.forEach(remapQuery);const pushText=(fnType,result)=>{if(!!(result.length%2)===!!fnType.highlighted){result.push("");}else if(result.length===0&&!!fnType.highlighted){result.push("");result.push("");}result[result.length-1]+=fnType.name;};const writeHof=async(fnType,result)=>{const hofOutput=fnType.bindings.get(typeNameIds.typeNameIdOfOutput)||[];const hofInputs=fnType.generics;pushText(fnType,result);pushText({name:" (",highlighted:false},result);let needsComma=false;for(const fnType of hofInputs){if(needsComma){pushText({name:", ",highlighted:false},result);}needsComma=true;await writeFn(fnType,result);}pushText({name:hofOutput.length===0?")":") -> ",highlighted:false,},result);if(hofOutput.length>1){pushText({name:"(",highlighted:false},result);}needsComma=false;for(const fnType of hofOutput){if(needsComma){pushText({name:", ",highlighted:false},result);}needsComma=true;await writeFn(fnType,result);}if(hofOutput.length>1){pushText({name:")",highlighted:false},result);}};const writeSpecialPrimitive=async(fnType,result)=>{if(fnType.id===typeNameIds.typeNameIdOfArray||fnType.id===typeNameIds.typeNameIdOfSlice||fnType.id===typeNameIds.typeNameIdOfTuple||fnType.id===typeNameIds.typeNameIdOfUnit){const[ob,sb]=fnType.id===typeNameIds.typeNameIdOfArray||fnType.id===typeNameIds.typeNameIdOfSlice?["[","]"]:["(",")"];pushText({name:ob,highlighted:fnType.highlighted},result);await onEachBtwnAsync(fnType.generics,nested=>writeFn(nested,result),()=>pushText({name:", ",highlighted:false},result),);pushText({name:sb,highlighted:fnType.highlighted},result);return true;}else if(fnType.id===typeNameIds.typeNameIdOfReference){pushText({name:"&",highlighted:fnType.highlighted},result);let prevHighlighted=false;await onEachBtwnAsync(fnType.generics,async value=>{prevHighlighted=!!value.highlighted;await writeFn(value,result);},value=>pushText({name:" ",highlighted:prevHighlighted&&value.highlighted,},result),);return true;}else if(fnType.id===typeNameIds.typeNameIdOfPointer){pushText({name:"*",highlighted:fnType.highlighted},result);if(fnType.generics.length<2){pushText({name:"const ",highlighted:fnType.highlighted},result);}let prevHighlighted=false;await onEachBtwnAsync(fnType.generics,async value=>{prevHighlighted=!!value.highlighted;await writeFn(value,result);},value=>pushText({name:" ",highlighted:prevHighlighted&&value.highlighted,},result),);return true;}else if(fnType.id===typeNameIds.typeNameIdOfFn||fnType.id===typeNameIds.typeNameIdOfFnMut||fnType.id===typeNameIds.typeNameIdOfFnOnce||fnType.id===typeNameIds.typeNameIdOfFnPtr){await writeHof(fnType,result);return true;}else if(fnType.id===typeNameIds.typeNameIdOfNever){pushText({name:"!",highlighted:fnType.highlighted},result);return true;}return false;};const writeFn=async(fnType,result)=>{if(fnType.id!==null&&fnType.id<0){if(fnParamNames[-1-fnType.id]===""){const generics=fnType.generics.length>0?fnType.generics:objType.where_clause[-1-fnType.id];for(const nested of generics){await writeFn(nested,result);}return;}else if(mgens){for(const[queryId,fnId]of mgens){if(fnId===fnType.id){mappedNames.set(queryParamNames[-1-queryId],fnParamNames[-1-fnType.id],);}}}pushText({name:fnParamNames[-1-fnType.id],highlighted:!!fnType.highlighted,},result);const where=[];await onEachBtwnAsync(fnType.generics,nested=>writeFn(nested,where),()=>pushText({name:" + ",highlighted:false},where),);if(where.length>0){whereClause.set(fnParamNames[-1-fnType.id],where);}}else{if(fnType.ty===TY_PRIMITIVE){if(await writeSpecialPrimitive(fnType,result)){return;}}else if(fnType.ty===TY_TRAIT&&(fnType.id===typeNameIds.typeNameIdOfFn||fnType.id===typeNameIds.typeNameIdOfFnMut||fnType.id===typeNameIds.typeNameIdOfFnOnce||fnType.id===typeNameIds.typeNameIdOfFnPtr)){await writeHof(fnType,result);return;}else if(fnType.name===""&&fnType.bindings.size===0&&fnType.generics.length!==0){pushText({name:"impl ",highlighted:false},result);if(fnType.generics.length>1){pushText({name:"(",highlighted:false},result);}await onEachBtwnAsync(fnType.generics,value=>writeFn(value,result),()=>pushText({name:", ",highlighted:false},result),);if(fnType.generics.length>1){pushText({name:")",highlighted:false},result);}return;}pushText(fnType,result);let hasBindings=false;if(fnType.bindings.size>0){await onEachBtwnAsync(await Promise.all([...fnType.bindings.entries()].map(async([key,values])=>[await this.getName(key),values],)),async([name,values])=>{if(values.length===1&&values[0].id<0&&`${fnType.name}::${name}`===fnParamNames[-1-values[0].id]){for(const value of values){await writeFn(value,[]);}return true;}if(!hasBindings){hasBindings=true;pushText({name:"<",highlighted:false},result);}pushText({name,highlighted:false},result);pushText({name:values.length!==1?"=(":"=",highlighted:false,},result);await onEachBtwnAsync(values||[],value=>writeFn(value,result),()=>pushText({name:" + ",highlighted:false},result),);if(values.length!==1){pushText({name:")",highlighted:false},result);}},()=>pushText({name:", ",highlighted:false},result),);}if(fnType.generics.length>0){pushText({name:hasBindings?", ":"<",highlighted:false},result);}await onEachBtwnAsync(fnType.generics,value=>writeFn(value,result),()=>pushText({name:", ",highlighted:false},result),);if(hasBindings||fnType.generics.length>0){pushText({name:">",highlighted:false},result);}}};const type=[];await onEachBtwnAsync(fnInputs,fnType=>writeFn(fnType,type),()=>pushText({name:", ",highlighted:false},type),);pushText({name:" -> ",highlighted:false},type);await onEachBtwnAsync(fnOutput,fnType=>writeFn(fnType,type),()=>pushText({name:", ",highlighted:false},type),);return{type,mappedNames,whereClause};};const transformResults=(results,typeInfo,duplicates)=>{const out=[];const traitImplIdxMap=new Map();for(const result of results){const item=result.item;if(item.id!==-1){const res=buildHrefAndPath(item);const obj=Object.assign({parent:item.parent?{path:item.parent.path.modulePath,exactPath:item.parent.path.exactModulePath||item.parent.path.modulePath,name:item.parent.name,ty:item.parent.path.ty,}:undefined,type:item.functionData&&item.functionData.functionSignature?item.functionData.functionSignature:undefined,paramNames:item.functionData&&item.functionData.paramNames?item.functionData.paramNames:undefined,dist:result.dist,path_dist:result.path_dist,index:result.index,desc:this.getDesc(result.id),item,displayPath:pathSplitter(res[0]),fullPath:"",traitPath:null,href:"",displayTypeSignature:null,},result);let ty=obj.item.ty;if(ty===TY_TYMETHOD){ty=TY_METHOD;}obj.fullPath=res[2]+"|"+ty;if(res[3]){obj.traitPath=res[3]+"|"+obj.item.ty;}if(duplicates.has(obj.fullPath)){continue;}if(obj.traitPath&&duplicates.has(obj.traitPath)){continue;}if(obj.item.ty===TY_IMPORT&&duplicates.has(res[2])){continue;}if(duplicates.has(res[2]+"|"+TY_IMPORT)){continue;}duplicates.add(obj.fullPath);duplicates.add(res[2]);if(typeInfo!==null){obj.displayTypeSignature=formatDisplayTypeSignature(obj,typeInfo,result.elems,result.returned,);}obj.href=res[1];if(obj.traitPath){let list=traitImplIdxMap.get(obj.traitPath);if(list===undefined){list=[];}list.push(out.length);traitImplIdxMap.set(obj.traitPath,list);}else{const toRemoveList=traitImplIdxMap.get(obj.fullPath);if(toRemoveList){removeIdxListAsc(out,toRemoveList);}traitImplIdxMap.delete(obj.fullPath);}out.push(obj);if(out.length>=MAX_RESULTS){break;}}}return out;};const sortAndTransformResults=async function*(results,typeInfo,preferredCrate,duplicates){const userQuery=parsedQuery.userQuery;const normalizedUserQuery=parsedQuery.userQuery.toLowerCase();const isMixedCase=normalizedUserQuery!==userQuery;const result_list=[];for(const result of results.values()){if(!result){continue;}const item=result.item;if(filterCrates!==null&&item.crate!==filterCrates){continue;}if(item){result_list.push(result);}else{continue;}}result_list.sort((aaa,bbb)=>{const aai=aaa.item;const bbi=bbb.item;let a;let b;if(typeInfo===null){if(isMixedCase){a=Number(aai.name!==userQuery);b=Number(bbi.name!==userQuery);if(a!==b){return a-b;}}a=Number(aai.normalizedName!==normalizedUserQuery);b=Number(bbi.normalizedName!==normalizedUserQuery);if(a!==b){return a-b;}a=Number(aaa.index<0);b=Number(bbb.index<0);if(a!==b){return a-b;}}a=Number(aaa.path_dist);b=Number(bbb.path_dist);if(a!==b){return a-b;}a=Number(aaa.index);b=Number(bbb.index);if(a!==b){return a-b;}a=Number(aaa.dist);b=Number(bbb.dist);if(a!==b){return a-b;}a=Number(aaa.is_alias);b=Number(bbb.is_alias);if(a!==b){return a-b;}a=Number(aai.deprecated);b=Number(bbi.deprecated);if(a!==b){return a-b;}a=Number(aai.crate!==preferredCrate);b=Number(bbi.crate!==preferredCrate);if(a!==b){return a-b;}a=Number(aai.normalizedName.length);b=Number(bbi.normalizedName.length);if(a!==b){return a-b;}let aw=aai.normalizedName;let bw=bbi.normalizedName;if(aw!==bw){return(aw>bw?+1:-1);}const di=this.database.getData("desc");if(di){a=Number(di.isEmpty(aaa.id));b=Number(di.isEmpty(bbb.id));if(a!==b){return a-b;}}a=Number(aai.ty);b=Number(bbi.ty);if(a!==b){return a-b;}const ap=aai.modulePath;const bp=bbi.modulePath;aw=ap===undefined?"":ap;bw=bp===undefined?"":bp;if(aw!==bw){return(aw>bw?+1:-1);}return 0;});const transformed_result_list=transformResults(result_list,typeInfo,duplicates);yield*transformed_result_list;return transformed_result_list.length;}.bind(this);function unifyFunctionTypes(fnTypesIn,queryElems,whereClause,mgensIn,solutionCb,unboxingDepth,typeNameIds,){if(unboxingDepth>=UNBOXING_LIMIT){return null;}const mgens=mgensIn===null?null:new Map(mgensIn);if(queryElems.length===0){return solutionCb(mgens)?fnTypesIn:null;}if(!fnTypesIn||fnTypesIn.length===0){return null;}const ql=queryElems.length;const fl=fnTypesIn.length;if(ql===1&&queryElems[0].generics.length===0&&queryElems[0].bindings.size===0){const queryElem=queryElems[0];for(const[i,fnType]of fnTypesIn.entries()){if(!unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgens,typeNameIds,)){continue;}if(fnType.id!==null&&fnType.id<0&&queryElem.id!==null&&queryElem.id<0){if(mgens&&mgens.has(queryElem.id)&&mgens.get(queryElem.id)!==fnType.id){continue;}const mgensScratch=new Map(mgens);mgensScratch.set(queryElem.id,fnType.id);if(!solutionCb||solutionCb(mgensScratch)){const highlighted=[...fnTypesIn];highlighted[i]=Object.assign({highlighted:true,},fnType,{generics:whereClause[-1-fnType.id],});return highlighted;}}else if(solutionCb(mgens?new Map(mgens):null)){const highlighted=[...fnTypesIn];highlighted[i]=Object.assign({highlighted:true,},fnType,{generics:unifyGenericTypes(fnType.generics,queryElem.generics,whereClause,mgens?new Map(mgens):null,solutionCb,unboxingDepth,typeNameIds,)||fnType.generics,});return highlighted;}}for(const[i,fnType]of fnTypesIn.entries()){if(!unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth+1,typeNameIds,)){continue;}if(fnType.id<0){const highlightedGenerics=unifyFunctionTypes(whereClause[(-fnType.id)-1],queryElems,whereClause,mgens,solutionCb,unboxingDepth+1,typeNameIds,);if(highlightedGenerics){const highlighted=[...fnTypesIn];highlighted[i]=Object.assign({highlighted:true,},fnType,{generics:highlightedGenerics,});return highlighted;}}else{const highlightedGenerics=unifyFunctionTypes([...Array.from(fnType.bindings.values()).flat(),...fnType.generics],queryElems,whereClause,mgens?new Map(mgens):null,solutionCb,unboxingDepth+1,typeNameIds,);if(highlightedGenerics){const highlighted=[...fnTypesIn];highlighted[i]=Object.assign({},fnType,{generics:highlightedGenerics,bindings:new Map([...fnType.bindings.entries()].map(([k,v])=>{return[k,highlightedGenerics.splice(0,v.length)];})),});return highlighted;}}}return null;}const fnTypes=fnTypesIn.slice();const flast=fl-1;const qlast=ql-1;const queryElem=queryElems[qlast];let queryElemsTmp=null;for(let i=flast;i>=0;i-=1){const fnType=fnTypes[i];if(!unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgens,typeNameIds,)){continue;}let mgensScratch;if(fnType.id!==null&&queryElem.id!==null&&fnType.id<0){mgensScratch=new Map(mgens);if(mgensScratch.has(queryElem.id)&&mgensScratch.get(queryElem.id)!==fnType.id){continue;}mgensScratch.set(queryElem.id,fnType.id);}else{mgensScratch=mgens;}fnTypes[i]=fnTypes[flast];fnTypes.length=flast;if(!queryElemsTmp){queryElemsTmp=queryElems.slice(0,qlast);}let unifiedGenerics=[];let unifiedGenericsMgens=null;const passesUnification=unifyFunctionTypes(fnTypes,queryElemsTmp,whereClause,mgensScratch,mgensScratch=>{if(fnType.generics.length===0&&queryElem.generics.length===0&&fnType.bindings.size===0&&queryElem.bindings.size===0){return solutionCb(mgensScratch);}const solution=unifyFunctionTypeCheckBindings(fnType,queryElem,whereClause,mgensScratch,unboxingDepth,typeNameIds,);if(!solution){return false;}const simplifiedGenerics=solution.simplifiedGenerics;for(const simplifiedMgens of solution.mgens){unifiedGenerics=unifyGenericTypes(simplifiedGenerics,queryElem.generics,whereClause,simplifiedMgens,solutionCb,unboxingDepth,typeNameIds,);if(unifiedGenerics!==null){unifiedGenericsMgens=simplifiedMgens;return true;}}return false;},unboxingDepth,typeNameIds,);if(passesUnification){passesUnification.length=fl;passesUnification[flast]=passesUnification[i];passesUnification[i]=Object.assign({},fnType,{highlighted:true,generics:unifiedGenerics,bindings:new Map([...fnType.bindings.entries()].map(([k,v])=>{return[k,queryElem.bindings.has(k)?unifyFunctionTypes(v,queryElem.bindings.get(k),whereClause,unifiedGenericsMgens,solutionCb,unboxingDepth,typeNameIds,):unifiedGenerics.splice(0,v.length)];})),});return passesUnification;}fnTypes[flast]=fnTypes[i];fnTypes[i]=fnType;fnTypes.length=fl;}for(let i=flast;i>=0;i-=1){const fnType=fnTypes[i];if(!unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth+1,typeNameIds,)){continue;}const generics=fnType.id!==null&&fnType.id<0?whereClause[(-fnType.id)-1]:fnType.generics;const bindings=fnType.bindings?Array.from(fnType.bindings.values()).flat():[];const passesUnification=unifyFunctionTypes(fnTypes.toSpliced(i,1,...bindings,...generics),queryElems,whereClause,mgens,solutionCb,unboxingDepth+1,typeNameIds,);if(passesUnification){const highlightedGenerics=passesUnification.slice(i,i+generics.length+bindings.length,);const highlightedFnType=Object.assign({},fnType,{generics:highlightedGenerics,bindings:new Map([...fnType.bindings.entries()].map(([k,v])=>{return[k,highlightedGenerics.splice(0,v.length)];})),});return passesUnification.toSpliced(i,generics.length+bindings.length,highlightedFnType,);}}return null;}function unifyGenericTypes(fnTypesIn,queryElems,whereClause,mgensIn,solutionCb,unboxingDepth,typeNameIds,){if(unboxingDepth>=UNBOXING_LIMIT){return null;}const mgens=mgensIn===null?null:new Map(mgensIn);if(queryElems.length===0){return solutionCb(mgens)?fnTypesIn:null;}if(!fnTypesIn||fnTypesIn.length===0){return null;}const fnType=fnTypesIn[0];const queryElem=queryElems[0];if(unifyFunctionTypeIsMatchCandidate(fnType,queryElem,mgens,typeNameIds,)){if(fnType.id!==null&&fnType.id<0&&queryElem.id!==null&&queryElem.id<0){if(!mgens||!mgens.has(queryElem.id)||mgens.get(queryElem.id)===fnType.id){const mgensScratch=new Map(mgens);mgensScratch.set(queryElem.id,fnType.id);const fnTypesRemaining=unifyGenericTypes(fnTypesIn.slice(1),queryElems.slice(1),whereClause,mgensScratch,solutionCb,unboxingDepth,typeNameIds,);if(fnTypesRemaining){const highlighted=[fnType,...fnTypesRemaining];highlighted[0]=Object.assign({highlighted:true,},fnType,{generics:whereClause[-1-fnType.id],});return highlighted;}}}else{let unifiedGenerics;const fnTypesRemaining=unifyGenericTypes(fnTypesIn.slice(1),queryElems.slice(1),whereClause,mgens,mgensScratch=>{const solution=unifyFunctionTypeCheckBindings(fnType,queryElem,whereClause,mgensScratch,unboxingDepth,typeNameIds,);if(!solution){return false;}const simplifiedGenerics=solution.simplifiedGenerics;for(const simplifiedMgens of solution.mgens){unifiedGenerics=unifyGenericTypes(simplifiedGenerics,queryElem.generics,whereClause,simplifiedMgens,solutionCb,unboxingDepth,typeNameIds,);if(unifiedGenerics!==null){return true;}}},unboxingDepth,typeNameIds,);if(fnTypesRemaining){const highlighted=[fnType,...fnTypesRemaining];highlighted[0]=Object.assign({highlighted:true,},fnType,{generics:unifiedGenerics||fnType.generics,});return highlighted;}}}if(unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth+1,typeNameIds,)){let highlightedRemaining;if(fnType.id!==null&&fnType.id<0){const highlightedGenerics=unifyFunctionTypes(whereClause[(-fnType.id)-1],[queryElem],whereClause,mgens,mgensScratch=>{const hl=unifyGenericTypes(fnTypesIn.slice(1),queryElems.slice(1),whereClause,mgensScratch,solutionCb,unboxingDepth,typeNameIds,);if(hl){highlightedRemaining=hl;}return hl;},unboxingDepth+1,typeNameIds,);if(highlightedGenerics){return[Object.assign({highlighted:true,},fnType,{generics:highlightedGenerics,}),...highlightedRemaining];}}else{const highlightedGenerics=unifyGenericTypes([...Array.from(fnType.bindings.values()).flat(),...fnType.generics,],[queryElem],whereClause,mgens,mgensScratch=>{const hl=unifyGenericTypes(fnTypesIn.slice(1),queryElems.slice(1),whereClause,mgensScratch,solutionCb,unboxingDepth,typeNameIds,);if(hl){highlightedRemaining=hl;}return hl;},unboxingDepth+1,typeNameIds,);if(highlightedGenerics){return[Object.assign({},fnType,{generics:highlightedGenerics,bindings:new Map([...fnType.bindings.entries()].map(([k,v])=>{return[k,highlightedGenerics.splice(0,v.length)];})),}),...highlightedRemaining];}}}return null;}const unifyFunctionTypeIsMatchCandidate=(fnType,queryElem,mgensIn,typeNameIds)=>{if(!typePassesFilter(queryElem.typeFilter,fnType.ty)){return false;}if(fnType.id!==null&&fnType.id<0&&queryElem.id!==null&&queryElem.id<0){if(mgensIn&&mgensIn.has(queryElem.id)&&mgensIn.get(queryElem.id)!==fnType.id){return false;}return true;}else{if(queryElem.id===typeNameIds.typeNameIdOfArrayOrSlice&&(fnType.id===typeNameIds.typeNameIdOfSlice||fnType.id===typeNameIds.typeNameIdOfArray)){}else if(queryElem.id===typeNameIds.typeNameIdOfTupleOrUnit&&(fnType.id===typeNameIds.typeNameIdOfTuple||fnType.id===typeNameIds.typeNameIdOfUnit)){}else if(queryElem.id===typeNameIds.typeNameIdOfHof&&(fnType.id===typeNameIds.typeNameIdOfFn||fnType.id===typeNameIds.typeNameIdOfFnMut||fnType.id===typeNameIds.typeNameIdOfFnOnce||fnType.id===typeNameIds.typeNameIdOfFnPtr)){}else if(fnType.id!==queryElem.id||queryElem.id===null){return false;}if((fnType.generics.length+fnType.bindings.size)===0&&queryElem.generics.length!==0){return false;}if(fnType.bindings.size0){const fnTypePath=fnType.path!==undefined&&fnType.path!==null?fnType.path.split("::"):[];if(queryElemPathLength>fnTypePath.length){return false;}let i=0;for(const path of fnTypePath){if(path===queryElem.pathWithoutLast[i]){i+=1;if(i>=queryElemPathLength){break;}}}if(i0){let mgensSolutionSet=[mgensIn];for(const[name,constraints]of queryElem.bindings.entries()){if(mgensSolutionSet.length===0){return false;}if(!fnType.bindings.has(name)){return false;}const fnTypeBindings=fnType.bindings.get(name);mgensSolutionSet=mgensSolutionSet.flatMap(mgens=>{const newSolutions=[];unifyFunctionTypes(fnTypeBindings,constraints,whereClause,mgens,newMgens=>{newSolutions.push(newMgens);return false;},unboxingDepth,typeNameIds,);return newSolutions;});}if(mgensSolutionSet.length===0){return false;}const binds=Array.from(fnType.bindings.entries()).flatMap(entry=>{const[name,constraints]=entry;if(queryElem.bindings.has(name)){return[];}else{return constraints;}});if(simplifiedGenerics.length>0){simplifiedGenerics=[...binds,...simplifiedGenerics];}else{simplifiedGenerics=binds;}return{simplifiedGenerics,mgens:mgensSolutionSet};}return{simplifiedGenerics,mgens:[mgensIn]};}function unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens,unboxingDepth,typeNameIds,){if(unboxingDepth>=UNBOXING_LIMIT){return false;}if(fnType.id!==null&&fnType.id<0){if(!whereClause){return false;}return checkIfInList(whereClause[(-fnType.id)-1],queryElem,whereClause,mgens,unboxingDepth,typeNameIds,);}else if(fnType.unboxFlag&&(fnType.generics.length>0||fnType.bindings.size>0)){const simplifiedGenerics=[...fnType.generics,...Array.from(fnType.bindings.values()).flat(),];return checkIfInList(simplifiedGenerics,queryElem,whereClause,mgens,unboxingDepth,typeNameIds,);}return false;}function containsTypeFromQuery(elems,list,where_clause,typeNameIds){if(!list)return false;for(const ty of elems){if(ty.id!==null&&ty.id<0){continue;}if(checkIfInList(list,ty,where_clause,null,0,typeNameIds)){return true;}}return false;}function checkIfInList(list,elem,whereClause,mgens,unboxingDepth,typeNameIds){for(const entry of list){if(checkType(entry,elem,whereClause,mgens,unboxingDepth,typeNameIds)){return true;}}return false;}const checkType=(row,elem,whereClause,mgens,unboxingDepth,typeNameIds)=>{if(unboxingDepth>=UNBOXING_LIMIT){return false;}if(row.id!==null&&elem.id!==null&&row.id>0&&elem.id>0&&elem.pathWithoutLast.length===0&&row.generics.length===0&&elem.generics.length===0&&row.bindings.size===0&&elem.bindings.size===0&&elem.id!==typeNameIds.typeNameIdOfArrayOrSlice&&elem.id!==typeNameIds.typeNameIdOfHof&&elem.id!==typeNameIds.typeNameIdOfTupleOrUnit){return row.id===elem.id&&typePassesFilter(elem.typeFilter,row.ty);}else{return unifyFunctionTypes([row],[elem],whereClause,mgens,()=>true,unboxingDepth,typeNameIds,);}};const checkTypeMgensForConflict=mgens=>{if(!mgens){return true;}const fnTypes=new Set();for(const[_qid,fid]of mgens){if(fnTypes.has(fid)){return false;}fnTypes.add(fid);}return true;};function checkPath(contains,path){if(contains.length===0){return 0;}const maxPathEditDistance=parsedQuery.literalSearch?0:Math.floor(contains.reduce((acc,next)=>acc+next.length,0)/3,);let ret_dist=maxPathEditDistance+1;const length=path.length;const clength=contains.length;pathiter:for(let i=length-clength;i>=0;i-=1){let dist_total=0;for(let x=0;xmaxPathEditDistance){continue pathiter;}dist_total+=dist;}}ret_dist=Math.min(ret_dist,Math.round(dist_total/clength));}return ret_dist>maxPathEditDistance?null:ret_dist;}function checkRowPath(contains,row){if(contains.length===0){return 0;}const path=row.modulePath.split("::");if(row.parent&&row.parent.name){path.push(row.parent.name.toLowerCase());}return checkPath(contains,path);}function typePassesFilter(filter,type){if(filter<=NO_TYPE_FILTER||filter===type)return true;const name=itemTypes[type];switch(itemTypes[filter]){case"constant":return name==="associatedconstant";case"fn":return name==="method"||name==="tymethod";case"type":return name==="primitive"||name==="associatedtype";case"trait":return name==="traitalias";}return false;}const innerRunNameQuery=async function*(currentCrate){const index=this.database.getData("normalizedName");if(!index){return;}const idDuplicates=new Set();const pathDuplicates=new Set();let count=0;const prefixResults=[];const normalizedUserQuery=parsedQuery.userQuery.replace(/[_"]/g,"").toLowerCase();const handleAlias=async(name,alias,dist,index)=>{return{id:alias,dist,path_dist:0,index,alias:name,is_alias:true,elems:[],returned:[],item:nonnull(await this.getRow(alias,false)),};};const flush=async function*(data){const satr=sortAndTransformResults(await Promise.all(data),null,currentCrate,pathDuplicates,);data.length=0;for await(const processed of satr){yield processed;count+=1;if((count&0x7F)===0){await yieldToBrowser();}if(count>=MAX_RESULTS){return true;}}return false;};const aliasResults=await index.search(normalizedUserQuery);if(aliasResults){for(const id of aliasResults.matches().entries()){const[name,alias]=await Promise.all([this.getName(id),this.getAliasTarget(id),]);if(name!==null&&alias!==null&&!idDuplicates.has(id)&&name.replace(/[_"]/g,"").toLowerCase()===normalizedUserQuery){prefixResults.push(handleAlias(name,alias,0,0));idDuplicates.add(id);}}}if(parsedQuery.error!==null||parsedQuery.elems.length===0){yield*flush(prefixResults);return;}const elem=parsedQuery.elems[0];const typeFilter=itemTypeFromName(elem.typeFilter);const handleNameSearch=async id=>{const row=await this.getRow(id,false);if(!row||!row.entry){return null;}if(!typePassesFilter(typeFilter,row.ty)||(filterCrates!==null&&row.crate!==filterCrates)){return null;}let pathDist=0;if(elem.fullPath.length>1){pathDist=checkRowPath(elem.pathWithoutLast,row);if(pathDist===null){return null;}}if(parsedQuery.literalSearch){return row.name.toLowerCase()===elem.pathLast?{id,dist:0,path_dist:0,index:0,elems:[],returned:[],is_alias:false,item:row,}:null;}else{return{id,dist:editDistance(row.normalizedName,elem.normalizedPathLast,maxEditDistance,),path_dist:pathDist,index:row.normalizedName.indexOf(elem.normalizedPathLast),elems:[],returned:[],is_alias:false,item:row,};}};if(elem.normalizedPathLast===""){const l=index.length;for(let id=0;id{let i=0;const l=idx.length;while(i{if(!elem){return empty_postings_list;}const typeFilter=itemTypeFromName(elem.typeFilter);const[searchResults,upla,uplb]=await Promise.all([index.search(elem.normalizedPathLast),unpackPostingsListAll(elem.generics,polarity),unpackPostingsListBindings(elem.bindings,polarity),]);const typePromises=[];if(typeFilter!==TY_GENERIC&&searchResults){for(const id of searchResults.matches().entries()){typePromises.push(Promise.all([this.getName(id),this.getTypeData(id),this.getPathData(id),]).then(([name,typeData,pathData])=>[id,name,typeData,pathData]));}}const types=(await Promise.all(typePromises)).filter(([_id,name,ty,path])=>name!==null&&name.toLowerCase()===elem.pathLast&&ty&&!ty[polarity].every(bitmap=>{return bitmap.isEmpty();})&&path&&path.ty!==TY_ASSOCTYPE&&(elem.pathWithoutLast.length===0||checkPath(elem.pathWithoutLast,path.modulePath.split("::"),)===0),);if(types.length===0){const areGenericsAllowed=typeFilter===TY_GENERIC||(typeFilter===-1&&(parsedQuery.totalElems>1||parsedQuery.hasReturnArrow)&&elem.pathWithoutLast.length===0&&elem.generics.length===0&&elem.bindings.size===0);if(typeFilter!==TY_GENERIC&&(elem.name.length>=3||!areGenericsAllowed)){let chosenName=null;let chosenType=[];let chosenPath=[];let chosenId=[];let chosenDist=Number.MAX_SAFE_INTEGER;const levResults=index.searchLev(elem.normalizedPathLast);for await(const searchResults of levResults){for(const id of searchResults.matches().entries()){const[name,ty,path]=await Promise.all([this.getName(id),this.getTypeData(id),this.getPathData(id),]);if(name!==null&&ty!==null&&path!==null&&!ty[polarity].every(bitmap=>{return bitmap.isEmpty();})&&path.ty!==TY_ASSOCTYPE){let dist=editDistance(name,elem.pathLast,maxEditDistance,);if(elem.pathWithoutLast.length!==0){const pathDist=checkPath(elem.pathWithoutLast,path.modulePath.split("::"),);dist+=pathDist===null?Number.MAX_SAFE_INTEGER:pathDist;}if(name===chosenName){chosenId.push(id);chosenType.push(ty);chosenPath.push(path);}else if(dist{const p1=!pathData1?"":pathData1.modulePath;const p2=!pathData2?"":pathData2.modulePath;const n1=name1===null?"":name1;const n2=name2===null?"":name2;if(p1.length!==p2.length){return p1.length>p2.length?+1:-1;}if(n1.length!==n2.length){return n1.length>n2.length?+1:-1;}if(n1!==n2){return n1>n2?+1:-1;}if(p1!==p2){return p1>p2?+1:-1;}return 0;});const results=[];for(const[id,_name,typeData]of types){if(!typeData||typeData[polarity].every(bitmap=>{return bitmap.isEmpty();})){continue;}for(const{invertedIndex:genericsIdx,queryElem:generics}of upla){for(const{invertedIndex:bindingsIdx,queryElem:bindings}of uplb){results.push({invertedIndex:intersectInvertedIndexes(typeData[polarity],genericsIdx,bindingsIdx,),queryElem:{name:elem.name,id,typeFilter,generics,bindings,fullPath:elem.fullPath,pathLast:elem.pathLast,normalizedPathLast:elem.normalizedPathLast,pathWithoutLast:elem.pathWithoutLast,},});if((results.length&0x7F)===0){await yieldToBrowser();}}}}return results;};const unpackPostingsListAll=async(elems,polarity)=>{if(!elems||elems.length===0){return nested_everything_postings_list;}const[firstPostingsList,remainingAll]=await Promise.all([unpackPostingsList(elems[0],polarity),unpackPostingsListAll(elems.slice(1),polarity),]);const results=[];for(const{invertedIndex:firstIdx,queryElem:firstElem,}of firstPostingsList){for(const{invertedIndex:remainingIdx,queryElem:remainingElems,}of remainingAll){results.push({invertedIndex:intersectInvertedIndexes(firstIdx,remainingIdx),queryElem:[firstElem,...remainingElems],});if((results.length&0x7F)===0){await yieldToBrowser();}}}return results;};const unpackPostingsListBindings=async(elems,polarity)=>{if(!elems){return[{invertedIndex:everything_inverted_index,queryElem:new Map(),}];}const firstKey=elems.keys().next().value;if(firstKey===undefined){return[{invertedIndex:everything_inverted_index,queryElem:new Map(),}];}const firstList=elems.get(firstKey);if(firstList===undefined){return[{invertedIndex:everything_inverted_index,queryElem:new Map(),}];}elems.delete(firstKey);const[firstKeyIds,firstPostingsList,remainingAll]=await Promise.all([index.search(firstKey),unpackPostingsListAll(firstList,polarity),unpackPostingsListBindings(elems,polarity),]);if(!firstKeyIds){elems.set(firstKey,firstList);return[{invertedIndex:empty_inverted_index,queryElem:new Map(),}];}const results=[];for(const keyId of firstKeyIds.matches().entries()){for(const{invertedIndex:firstIdx,queryElem:firstElem,}of firstPostingsList){for(const{invertedIndex:remainingIdx,queryElem:remainingElems,}of remainingAll){const elems=new Map(remainingElems);elems.set(keyId,firstElem);results.push({invertedIndex:intersectInvertedIndexes(firstIdx,remainingIdx),queryElem:elems,});if((results.length&0x7F)===0){await yieldToBrowser();}}}}elems.set(firstKey,firstList);if(results.length===0){return[{invertedIndex:empty_inverted_index,queryElem:new Map(),}];}return results;};const[allInputs,allOutput,typeNameIds]=await Promise.all([unpackPostingsListAll(inputs,"invertedFunctionInputsIndex"),unpackPostingsListAll(output,"invertedFunctionOutputIndex"),this.getTypeNameIds(),]);let checkCounter=0;const queryPlan=[];for(const{invertedIndex:inputsIdx,queryElem:inputs}of allInputs){for(const{invertedIndex:outputIdx,queryElem:output}of allOutput){const invertedIndex=intersectInvertedIndexes(inputsIdx,outputIdx);for(const[size,bitmap]of invertedIndex.entries()){checkCounter+=1;if((checkCounter&0x7F)===0){await yieldToBrowser();}if(!queryPlan[size]){queryPlan[size]=[];}queryPlan[size].push({bitmap,inputs,output,});}}}const resultPromises=[];const dedup=new Set();let resultCounter=0;const isReturnTypeQuery=inputs.length===0;const pushToBottom=[];plan:for(const queryStep of queryPlan){for(const{bitmap,inputs,output}of queryStep){for(const id of bitmap.entries()){checkCounter+=1;if((checkCounter&0x7F)===0){await yieldToBrowser();}resultPromises.push(this.getFunctionData(id).then(async fnData=>{if(!fnData||!fnData.functionSignature){return null;}checkCounter+=1;if((checkCounter&0x7F)===0){await yieldToBrowser();}const functionSignature=fnData.functionSignature;if(!unifyFunctionTypes(functionSignature.inputs,inputs,functionSignature.where_clause,null,mgens=>{return!!unifyFunctionTypes(functionSignature.output,output,functionSignature.where_clause,mgens,checkTypeMgensForConflict,0,typeNameIds,);},0,typeNameIds,)){return null;}const item=await this.getRow(id,true);if(!item){return null;}const result={id,dist:fnData.elemCount,path_dist:0,index:-1,elems:inputs,returned:output,is_alias:false,item,};const entry=item.entry;if((entry&&!isFnLikeTy(entry.ty))||(isReturnTypeQuery&&functionSignature&&containsTypeFromQuery(output,functionSignature.inputs,functionSignature.where_clause,typeNameIds,))){pushToBottom.push(result);return null;}return result;}));}}for await(const result of sortAndTransformResults(await Promise.all(resultPromises),typeInfo,currentCrate,dedup,)){if(resultCounter>=MAX_RESULTS){break plan;}yield result;resultCounter+=1;}resultPromises.length=0;}if(resultCounter>=MAX_RESULTS){return;}for await(const result of sortAndTransformResults(await Promise.all(pushToBottom),typeInfo,currentCrate,dedup,)){if(resultCounter>=MAX_RESULTS){break;}yield result;resultCounter+=1;}}.bind(this);if(parsedQuery.foundElems===1&&!parsedQuery.hasReturnArrow){const{promise:donePromise,resolve:doneResolve,reject:doneReject,}=Promise.withResolvers();const doneTimeout=timeout(250);return{"in_args":(async function*(){await Promise.race([donePromise,doneTimeout]);yield*innerRunTypeQuery(parsedQuery.elems,[],"elems",currentCrate);})(),"returned":(async function*(){await Promise.race([donePromise,doneTimeout]);yield*innerRunTypeQuery([],parsedQuery.elems,"returned",currentCrate);})(),"others":(async function*(){try{yield*innerRunNameQuery(currentCrate);doneResolve(null);}catch(e){doneReject(e);throw e;}})(),"query":parsedQuery,};}else if(parsedQuery.error!==null){return{"in_args":(async function*(){})(),"returned":(async function*(){})(),"others":innerRunNameQuery(currentCrate),"query":parsedQuery,};}else{const typeInfo=parsedQuery.elems.length===0?"returned":(parsedQuery.returned.length===0?"elems":"sig");return{"in_args":(async function*(){})(),"returned":(async function*(){})(),"others":parsedQuery.foundElems===0?(async function*(){})():innerRunTypeQuery(parsedQuery.elems,parsedQuery.returned,typeInfo,currentCrate,),"query":parsedQuery,};}}}let docSearch;const longItemTypes=["keyword","primitive type","module","extern crate","re-export","struct","enum","function","type alias","static","trait","","trait method","method","struct field","enum variant","macro","assoc type","constant","assoc const","union","foreign type","existential type","attribute macro","derive macro","trait alias","","attribute",];let currentResults;function printTab(nb){let iter=0;let foundCurrentTab=false;let foundCurrentResultSet=false;onEachLazy(document.getElementById("search-tabs").childNodes,elem=>{if(nb===iter){addClass(elem,"selected");foundCurrentTab=true;onEachLazy(document.querySelectorAll(".search-form",),form=>{if(hasClass(elem.firstElementChild,"loading")){addClass(form,"loading");}else{removeClass(form,"loading");}});}else{removeClass(elem,"selected");}iter+=1;});const isTypeSearch=(nb>0||iter===1);iter=0;onEachLazy(document.getElementById("results").childNodes,elem=>{if(nb===iter){addClass(elem,"active");foundCurrentResultSet=true;}else{removeClass(elem,"active");}iter+=1;});if(foundCurrentTab&&foundCurrentResultSet){searchState.currentTab=nb;const correctionsElem=document.getElementsByClassName("search-corrections");if(isTypeSearch){removeClass(correctionsElem[0],"hidden");}else{addClass(correctionsElem[0],"hidden");}}else if(nb!==0){printTab(0);}}function buildUrl(search,filterCrates){let extra="?search="+encodeURIComponent(search);if(filterCrates!==null){extra+="&filter-crate="+encodeURIComponent(filterCrates);}return getNakedUrl()+extra+window.location.hash;}function getFilterCrates(){const elem=document.getElementById("crate-search");if(elem&&elem.value!=="all crates"){return elem.value;}return null;}function nextTab(direction){const next=(searchState.currentTab+direction+3)%searchState.focusedByTab.length;window.searchState.focusedByTab[searchState.currentTab]=document.activeElement;printTab(next);focusSearchResult();}function focusSearchResult(){const target=searchState.focusedByTab[searchState.currentTab]||document.querySelectorAll(".search-results.active a").item(0)||document.querySelectorAll("#search-tabs button").item(searchState.currentTab);searchState.focusedByTab[searchState.currentTab]=null;if(target&&target instanceof HTMLElement){target.focus();}}async function addTab(results,query,display,finishedCallback,isTypeSearch){const extraClass=display?" active":"";let output=document.createElement("ul");output.className="search-results "+extraClass;let count=0;const descList=[];const addNextResultToOutput=async obj=>{count+=1;const name=obj.item.name;const type=itemTypes[obj.item.ty];const longType=longItemTypes[obj.item.ty];const typeName=longType.length!==0?`${longType}`:"?";const link=document.createElement("a");link.className="result-"+type;link.href=obj.href;const resultName=document.createElement("span");resultName.className="result-name";resultName.insertAdjacentHTML("beforeend",`${typeName}`);link.appendChild(resultName);let alias=" ";if(obj.alias!==undefined){alias=`
\ +${obj.alias} - see \ +
`;}resultName.insertAdjacentHTML("beforeend",`
${alias}\ +${obj.displayPath}${name}\ +
`);const description=document.createElement("div");description.className="desc";obj.desc.then(desc=>{if(desc!==null){description.insertAdjacentHTML("beforeend",desc);}});descList.push(obj.desc);if(obj.displayTypeSignature){const{type,mappedNames,whereClause}=await obj.displayTypeSignature;const displayType=document.createElement("div");type.forEach((value,index)=>{if(index%2!==0){const highlight=document.createElement("strong");highlight.appendChild(document.createTextNode(value));displayType.appendChild(highlight);}else{displayType.appendChild(document.createTextNode(value));}});if(mappedNames.size>0||whereClause.size>0){let addWhereLineFn=()=>{const line=document.createElement("div");line.className="where";line.appendChild(document.createTextNode("where"));displayType.appendChild(line);addWhereLineFn=()=>{};};for(const[qname,name]of mappedNames){if(name===qname){continue;}addWhereLineFn();const line=document.createElement("div");line.className="where";line.appendChild(document.createTextNode(` ${qname} matches `));const lineStrong=document.createElement("strong");lineStrong.appendChild(document.createTextNode(name));line.appendChild(lineStrong);displayType.appendChild(line);}for(const[name,innerType]of whereClause){if(innerType.length<=1){continue;}addWhereLineFn();const line=document.createElement("div");line.className="where";line.appendChild(document.createTextNode(` ${name}: `));innerType.forEach((value,index)=>{if(index%2!==0){const highlight=document.createElement("strong");highlight.appendChild(document.createTextNode(value));line.appendChild(highlight);}else{line.appendChild(document.createTextNode(value));}});displayType.appendChild(line);}}displayType.className="type-signature";link.appendChild(displayType);}link.appendChild(description);output.appendChild(link);results.next().then(async nextResult=>{if(nextResult.value){addNextResultToOutput(nextResult.value);}else{await Promise.all(descList);yieldToBrowser().then(()=>{finishedCallback(count,output);});}});};const firstResult=await results.next();let correctionOutput="";if(query.correction!==null&&isTypeSearch){const orig=query.returned.length>0?query.returned[0].name:query.elems[0].name;correctionOutput="

"+`Type "${orig}" not found. `+"Showing results for closest type name "+`"${query.correction}" instead.

`;}if(query.proposeCorrectionFrom!==null&&isTypeSearch){const orig=query.proposeCorrectionFrom;const targ=query.proposeCorrectionTo;correctionOutput="

"+`Type "${orig}" not found and used as generic parameter. `+`Consider searching for "${targ}" instead.

`;}if(firstResult.value){if(correctionOutput!==""){const h3=document.createElement("h3");h3.innerHTML=correctionOutput;output.appendChild(h3);}await addNextResultToOutput(firstResult.value);}else{output=document.createElement("div");if(correctionOutput!==""){const h3=document.createElement("h3");h3.innerHTML=correctionOutput;output.appendChild(h3);}output.className="search-failed"+extraClass;const dlroChannel=`https://doc.rust-lang.org/${getVar("channel")}`;if(query.userQuery!==""){output.innerHTML+="No results :(
"+"Try on DuckDuckGo?

"+"Or try looking in one of these:";}output.innerHTML+="Example searches:";yieldToBrowser().then(()=>finishedCallback(0,output));}return output;}function makeTab(tabNb,text,results,query,isTypeSearch,goToFirst){const isCurrentTab=window.searchState.currentTab===tabNb;const tabButton=document.createElement("button");tabButton.appendChild(document.createTextNode(text));tabButton.className=isCurrentTab?"selected":"";const tabCount=document.createElement("span");tabCount.className="count loading";tabCount.innerHTML="\u{2007}(\u{2007})\u{2007}\u{2007}";tabButton.appendChild(tabCount);return[tabButton,addTab(results,query,isCurrentTab,(count,output)=>{const search=window.searchState.outputElement();const error=query.error;if(count===0&&error!==null&&search){error.forEach((value,index)=>{value=value.split("<").join("<").split(">").join(">");if(index%2!==0){error[index]=`${value.replaceAll(" ", " ")}`;}else{error[index]=value;}});const errorReport=document.createElement("h3");errorReport.className="error";errorReport.innerHTML=`Query parser error: "${error.join("")}".`;search.insertBefore(errorReport,search.firstElementChild);}else if(goToFirst||(count===1&&getSettingValue("go-to-only-result")==="true")){window.onunload=()=>{};window.searchState.removeQueryParameters();const a=output.querySelector("a");if(a){a.click();return;}}const fmtNbElems=count<10?`\u{2007}(${count})\u{2007}\u{2007}`:count<100?`\u{2007}(${count})\u{2007}`:`\u{2007}(${count})`;tabCount.innerHTML=fmtNbElems;tabCount.className="count";printTab(window.searchState.currentTab);},isTypeSearch),];}async function showResults(docSearch,results,goToFirst,filterCrates){const search=window.searchState.outputElement();if(!search){return;}let crates="";const crateNames=await docSearch.getCrateNameList();if(crateNames.length>1){crates=" in 
"+"
";}nonnull(document.querySelector(".search-switcher")).innerHTML=`Search results${crates}`;const tabs=[];searchState.currentTab=0;if(results.query.error!==null){tabs.push(makeTab(0,"In Names",results.others,results.query,false,goToFirst));}else if(results.query.foundElems<=1&&results.query.returned.length===0&&!results.query.hasReturnArrow){tabs.push(makeTab(0,"In Names",results.others,results.query,false,goToFirst));tabs.push(makeTab(1,"In Parameters",results.in_args,results.query,true,false));tabs.push(makeTab(2,"In Return Types",results.returned,results.query,true,false));}else{const signatureTabTitle=results.query.elems.length===0?"In Function Return Types":results.query.returned.length===0?"In Function Parameters":"In Function Signatures";tabs.push(makeTab(0,signatureTabTitle,results.others,results.query,true,goToFirst));}const tabsElem=document.createElement("div");tabsElem.id="search-tabs";const resultsElem=document.createElement("div");resultsElem.id="results";search.innerHTML="";for(const[tabNb,[tab,output]]of tabs.entries()){tabsElem.appendChild(tab);const isCurrentTab=window.searchState.currentTab===tabNb;const placeholder=document.createElement("div");placeholder.className=isCurrentTab?"search-results active":"search-results";placeholder.innerHTML="Loading...";output.then(output=>{if(placeholder.parentElement){placeholder.parentElement.replaceChild(output,placeholder);}});resultsElem.appendChild(placeholder);}if(window.searchState.rustdocToolbar){nonnull(nonnull(window.searchState.containerElement()).querySelector(".main-heading"),).appendChild(window.searchState.rustdocToolbar);}const crateSearch=document.getElementById("crate-search");if(crateSearch){crateSearch.addEventListener("input",updateCrate);}search.appendChild(tabsElem);search.appendChild(resultsElem);window.searchState.showResults();window.searchState.focusedByTab=[null,null,null];let i=0;for(const elem of tabsElem.childNodes){const j=i;elem.onclick=()=>printTab(j);window.searchState.focusedByTab[i]=null;i+=1;}printTab(0);}function updateSearchHistory(url){const btn=document.querySelector("#search-button a");if(btn instanceof HTMLAnchorElement){btn.href=url;}if(!browserSupportsHistoryApi()){return;}const params=searchState.getQueryStringParams();if(!history.state&¶ms.search===undefined){history.pushState(null,"",url);}else{history.replaceState(null,"",url);}}async function search(forced){const query=DocSearch.parseQuery(nonnull(window.searchState.inputElement()).value.trim());let filterCrates=getFilterCrates();if(!forced&&query.userQuery===currentResults){if(query.userQuery.length>0){putBackSearch();}return;}currentResults=query.userQuery;searchState.setLoadingSearch();const params=searchState.getQueryStringParams();if(filterCrates===null&¶ms["filter-crate"]!==undefined){filterCrates=params["filter-crate"];}if(filterCrates!==null&&(await docSearch.getCrateNameList()).indexOf(filterCrates)===-1){filterCrates=null;}searchState.title="\""+query.userQuery+"\" Search - Rust";updateSearchHistory(buildUrl(query.userQuery,filterCrates));await showResults(docSearch,await docSearch.execQuery(query,filterCrates,window.currentCrate),params.go_to_first,filterCrates);}function onSearchSubmit(e){e.preventDefault();searchState.clearInputTimeout();search();}function putBackSearch(){const search_input=window.searchState.inputElement();if(!search_input){return;}if(search_input.value!==""&&!searchState.isDisplayed()){searchState.showResults();if(browserSupportsHistoryApi()){history.replaceState(null,"",buildUrl(search_input.value,getFilterCrates()));}document.title=searchState.title;}}function registerSearchEvents(){const params=searchState.getQueryStringParams();const inputElement=nonnull(window.searchState.inputElement());if(inputElement.value===""){inputElement.value=params.search||"";}const searchAfter500ms=()=>{searchState.clearInputTimeout();window.searchState.timeout=setTimeout(search,500);};inputElement.onkeyup=searchAfter500ms;inputElement.oninput=searchAfter500ms;if(inputElement.form){inputElement.form.onsubmit=onSearchSubmit;}inputElement.onchange=e=>{if(e.target!==document.activeElement){return;}searchState.clearInputTimeout();setTimeout(search,0);};inputElement.onpaste=inputElement.onchange;searchState.outputElement().addEventListener("keydown",e=>{if(!(e instanceof KeyboardEvent)){return;}if(e.altKey||e.ctrlKey||e.shiftKey||e.metaKey){return;}if(e.which===38){const previous=document.activeElement.previousElementSibling;if(previous){previous.focus();}else{searchState.focus();}e.preventDefault();}else if(e.which===40){const next=document.activeElement.nextElementSibling;if(next){next.focus();}const rect=document.activeElement.getBoundingClientRect();if(window.innerHeight-rect.bottom{if(e.which===40){focusSearchResult();e.preventDefault();}});inputElement.addEventListener("focus",()=>{putBackSearch();});}function updateCrate(ev){if(ev.target.value==="all crates"){const query=nonnull(window.searchState.inputElement()).value.trim();updateSearchHistory(buildUrl(query,null));}currentResults=null;search(true);}const makeUint8ArrayFromBase64=Uint8Array.fromBase64?Uint8Array.fromBase64:(string=>{const bytes_as_string=atob(string);const l=bytes_as_string.length;const bytes=new Uint8Array(l);for(let i=0;i{for(const key in callbacks){if(Object.hasOwn(callbacks,key)){window[key]=callbacks[key];}}databaseCallbacks=callbacks;if(window.searchIndex){window.rr_(window.searchIndex);}},loadTreeByHash:hashHex=>{const script=document.createElement("script");script.src=`${ROOT_PATH}search.index/${hashHex}.js`;script.onerror=e=>{if(databaseCallbacks){databaseCallbacks.err_rn_(hashHex,e);}};document.documentElement.appendChild(script);},loadDataByNameAndHash:(name,hashHex)=>{const script=document.createElement("script");script.src=`${ROOT_PATH}search.index/${name}/${hashHex}.js`;script.onerror=e=>{if(databaseCallbacks){databaseCallbacks.err_rd_(hashHex,e);}};document.documentElement.appendChild(script);},});}else if(typeof exports!=="undefined"){exports.initSearch=initSearch;} \ No newline at end of file diff --git a/pr/395/docs/static.files/settings-c38705f0.js b/pr/395/docs/static.files/settings-c38705f0.js new file mode 100644 index 000000000..7e4939e5c --- /dev/null +++ b/pr/395/docs/static.files/settings-c38705f0.js @@ -0,0 +1,17 @@ +"use strict";(function(){const isSettingsPage=window.location.pathname.endsWith("/settings.html");function changeSetting(settingName,value){if(settingName==="theme"){const useSystem=value==="system preference"?"true":"false";updateLocalStorage("use-system-theme",useSystem);}updateLocalStorage(settingName,""+value);switch(settingName){case"theme":case"preferred-dark-theme":case"preferred-light-theme":updateTheme();updateLightAndDark();break;case"line-numbers":if(value===true){const f=window.rustdoc_add_line_numbers_to_examples;if(f!==undefined){f();}}else{const f=window.rustdoc_remove_line_numbers_from_examples;if(f!==undefined){f();}}break;case"hide-sidebar":if(value===true){addClass(document.documentElement,"hide-sidebar");}else{removeClass(document.documentElement,"hide-sidebar");}break;case"hide-toc":if(value===true){addClass(document.documentElement,"hide-toc");}else{removeClass(document.documentElement,"hide-toc");}break;case"hide-modnav":if(value===true){addClass(document.documentElement,"hide-modnav");}else{removeClass(document.documentElement,"hide-modnav");}break;case"sans-serif-fonts":if(value===true){addClass(document.documentElement,"sans-serif");}else{removeClass(document.documentElement,"sans-serif");}break;case"word-wrap-source-code":if(value===true){addClass(document.documentElement,"word-wrap-source-code");}else{removeClass(document.documentElement,"word-wrap-source-code");}break;}}function showLightAndDark(){removeClass(document.getElementById("preferred-light-theme"),"hidden");removeClass(document.getElementById("preferred-dark-theme"),"hidden");}function hideLightAndDark(){addClass(document.getElementById("preferred-light-theme"),"hidden");addClass(document.getElementById("preferred-dark-theme"),"hidden");}function updateLightAndDark(){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||(useSystem===null&&getSettingValue("theme")===null)){showLightAndDark();}else{hideLightAndDark();}}function setEvents(settingsElement){updateLightAndDark();onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"),toggle=>{const settingId=toggle.id;const settingValue=getSettingValue(settingId);if(settingValue!==null){toggle.checked=settingValue==="true";}toggle.onchange=()=>{changeSetting(toggle.id,toggle.checked);};});onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"),elem=>{const settingId=elem.name;let settingValue=getSettingValue(settingId);if(settingId==="theme"){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||settingValue===null){settingValue=useSystem==="false"?"light":"system preference";}}if(settingValue!==null&&settingValue!=="null"){elem.checked=settingValue===elem.value;}elem.addEventListener("change",()=>{changeSetting(elem.name,elem.value);});},);}function buildSettingsPageSections(settings){let output="";for(const setting of settings){const js_data_name=setting["js_name"];const setting_name=setting["name"];if(setting["options"]!==undefined){output+=`\ +
+
${setting_name}
+
`;onEach(setting["options"],option=>{const checked=option===setting["default"]?" checked":"";const full=`${js_data_name}-${option.replace(/ /g,"-")}`;output+=`\ + `;});output+=`\ +
+
`;}else{const checked=setting["default"]===true?" checked":"";output+=`\ +
\ + \ +
`;}}return output;}function buildSettingsPage(){const theme_list=getVar("themes");const theme_names=(theme_list===null?"":theme_list).split(",").filter(t=>t);theme_names.push("light","dark","ayu");const settings=[{"name":"Theme","js_name":"theme","default":"system preference","options":theme_names.concat("system preference"),},{"name":"Preferred light theme","js_name":"preferred-light-theme","default":"light","options":theme_names,},{"name":"Preferred dark theme","js_name":"preferred-dark-theme","default":"dark","options":theme_names,},{"name":"Auto-hide item contents for large items","js_name":"auto-hide-large-items","default":true,},{"name":"Auto-hide item methods' documentation","js_name":"auto-hide-method-docs","default":false,},{"name":"Auto-hide trait implementation documentation","js_name":"auto-hide-trait-implementations","default":false,},{"name":"Directly go to item in search if there is only one result","js_name":"go-to-only-result","default":false,},{"name":"Show line numbers on code examples","js_name":"line-numbers","default":false,},{"name":"Hide persistent navigation bar","js_name":"hide-sidebar","default":false,},{"name":"Hide table of contents","js_name":"hide-toc","default":false,},{"name":"Hide module navigation","js_name":"hide-modnav","default":false,},{"name":"Disable keyboard shortcuts","js_name":"disable-shortcuts","default":false,},{"name":"Use sans serif fonts","js_name":"sans-serif-fonts","default":false,},{"name":"Word wrap source code","js_name":"word-wrap-source-code","default":false,},];const elementKind=isSettingsPage?"section":"div";const innerHTML=`
${buildSettingsPageSections(settings)}
`;const el=document.createElement(elementKind);el.id="settings";if(!isSettingsPage){el.className="popover";}el.innerHTML=innerHTML;if(isSettingsPage){const mainElem=document.getElementById(MAIN_ID);if(mainElem!==null){mainElem.appendChild(el);}}else{el.setAttribute("tabindex","-1");onEachLazy(document.querySelectorAll(".settings-menu"),menu=>{if(menu.offsetWidth!==0){menu.appendChild(el);return true;}});}return el;}const settingsMenu=buildSettingsPage();function displaySettings(){settingsMenu.style.display="";onEachLazy(document.querySelectorAll(".settings-menu"),menu=>{if(menu.offsetWidth!==0){if(!menu.contains(settingsMenu)&&settingsMenu.parentElement){settingsMenu.parentElement.removeChild(settingsMenu);menu.appendChild(settingsMenu);}return true;}});onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"),el=>{const val=getSettingValue(el.id);const checked=val==="true";if(checked!==el.checked&&val!==null){el.checked=checked;}});}function settingsBlurHandler(event){const isInPopover=onEachLazy(document.querySelectorAll(".settings-menu, .help-menu"),menu=>{return menu.contains(document.activeElement)||menu.contains(event.relatedTarget);},);if(!isInPopover){window.hidePopoverMenus();}}if(!isSettingsPage){const settingsMenu=nonnull(document.getElementById("settings"));onEachLazy(document.querySelectorAll(".settings-menu"),settingsButton=>{settingsButton.querySelector("a").onclick=event=>{if(!(event.target instanceof Element)||settingsMenu.contains(event.target)){return;}event.preventDefault();const shouldDisplaySettings=settingsMenu.style.display==="none";window.hideAllModals(false);if(shouldDisplaySettings){displaySettings();}};settingsButton.onblur=settingsBlurHandler;settingsButton.querySelector("a").onblur=settingsBlurHandler;});onEachLazy(settingsMenu.querySelectorAll("input"),el=>{el.onblur=settingsBlurHandler;});settingsMenu.onblur=settingsBlurHandler;}setTimeout(()=>{setEvents(settingsMenu);if(!isSettingsPage){displaySettings();}onEachLazy(document.querySelectorAll(".settings-menu"),settingsButton=>{removeClass(settingsButton,"rotate");});},0);})(); \ No newline at end of file diff --git a/pr/395/docs/static.files/src-script-813739b1.js b/pr/395/docs/static.files/src-script-813739b1.js new file mode 100644 index 000000000..bf546257c --- /dev/null +++ b/pr/395/docs/static.files/src-script-813739b1.js @@ -0,0 +1 @@ +"use strict";(function(){const rootPath=getVar("root-path");const NAME_OFFSET=0;const DIRS_OFFSET=1;const FILES_OFFSET=2;const RUSTDOC_MOBILE_BREAKPOINT=700;function closeSidebarIfMobile(){if(window.innerWidth{removeClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","false");};window.rustdocShowSourceSidebar=()=>{addClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","true");};window.rustdocToggleSrcSidebar=()=>{if(document.documentElement.classList.contains("src-sidebar-expanded")){window.rustdocCloseSourceSidebar();}else{window.rustdocShowSourceSidebar();}};function createSrcSidebar(srcIndexStr){const container=nonnull(document.querySelector("nav.sidebar"));const sidebar=document.createElement("div");sidebar.id="src-sidebar";const srcIndex=new Map(JSON.parse(srcIndexStr));let hasFoundFile=false;for(const[key,source]of srcIndex){source[NAME_OFFSET]=key;hasFoundFile=createDirEntry(source,sidebar,"",hasFoundFile);}container.appendChild(sidebar);const selected_elem=sidebar.getElementsByClassName("selected")[0];if(typeof selected_elem!=="undefined"){selected_elem.focus();}}function highlightSrcLines(){const match=window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);if(!match){return;}let from=parseInt(match[1],10);let to=from;if(typeof match[2]!=="undefined"){to=parseInt(match[2],10);}if(to{removeClass(e,"line-highlighted");});for(let i=from;i<=to;++i){elem=document.getElementById(""+i);if(!elem){break;}addClass(elem,"line-highlighted");}}const handleSrcHighlight=(function(){let prev_line_id=0;const set_fragment=name=>{const x=window.scrollX,y=window.scrollY;if(browserSupportsHistoryApi()){history.replaceState(null,"","#"+name);highlightSrcLines();}else{location.replace("#"+name);}window.scrollTo(x,y);};return ev=>{let cur_line_id=parseInt(ev.target.id,10);if(isNaN(cur_line_id)||ev.ctrlKey||ev.altKey||ev.metaKey){return;}ev.preventDefault();if(ev.shiftKey&&prev_line_id){if(prev_line_id>cur_line_id){const tmp=prev_line_id;prev_line_id=cur_line_id;cur_line_id=tmp;}set_fragment(prev_line_id+"-"+cur_line_id);}else{prev_line_id=cur_line_id;set_fragment(""+cur_line_id);}};}());window.addEventListener("hashchange",highlightSrcLines);onEachLazy(document.querySelectorAll("a[data-nosnippet]"),el=>{el.addEventListener("click",handleSrcHighlight);});highlightSrcLines();window.createSrcSidebar=createSrcSidebar;})(); \ No newline at end of file diff --git a/pr/395/docs/static.files/storage-e2aeef58.js b/pr/395/docs/static.files/storage-e2aeef58.js new file mode 100644 index 000000000..fb76ad5e9 --- /dev/null +++ b/pr/395/docs/static.files/storage-e2aeef58.js @@ -0,0 +1,27 @@ +"use strict";const builtinThemes=["light","dark","ayu"];const darkThemes=["dark","ayu"];window.currentTheme=(function(){const currentTheme=document.getElementById("themeStyle");return currentTheme instanceof HTMLLinkElement?currentTheme:null;})();const settingsDataset=(function(){const settingsElement=document.getElementById("default-settings");return settingsElement&&settingsElement.dataset?settingsElement.dataset:null;})();function nonnull(x,msg){if(x===null){throw(msg||"unexpected null value!");}else{return x;}}function nonundef(x,msg){if(x===undefined){throw(msg||"unexpected null value!");}else{return x;}}function getSettingValue(settingName){const current=getCurrentValue(settingName);if(current===null&&settingsDataset!==null){const def=settingsDataset[settingName.replace(/-/g,"_")];if(def!==undefined){return def;}}return current;}const localStoredTheme=getSettingValue("theme");function hasClass(elem,className){return!!elem&&!!elem.classList&&elem.classList.contains(className);}function addClass(elem,className){if(elem&&elem.classList){elem.classList.add(className);}}function removeClass(elem,className){if(elem&&elem.classList){elem.classList.remove(className);}}function onEach(arr,func){for(const elem of arr){if(func(elem)){return true;}}return false;}function onEachLazy(lazyArray,func){return onEach(Array.prototype.slice.call(lazyArray),func);}function updateLocalStorage(name,value){try{if(value===null){window.localStorage.removeItem("rustdoc-"+name);}else{window.localStorage.setItem("rustdoc-"+name,value);}}catch{}}function getCurrentValue(name){try{return window.localStorage.getItem("rustdoc-"+name);}catch{return null;}}function getVar(name){const el=document.querySelector("head > meta[name='rustdoc-vars']");return el?el.getAttribute("data-"+name):null;}function switchTheme(newThemeName,saveTheme){const themeNames=(getVar("themes")||"").split(",").filter(t=>t);themeNames.push(...builtinThemes);if(newThemeName===null||themeNames.indexOf(newThemeName)===-1){return;}if(saveTheme){updateLocalStorage("theme",newThemeName);}document.documentElement.setAttribute("data-theme",newThemeName);if(builtinThemes.indexOf(newThemeName)!==-1){if(window.currentTheme&&window.currentTheme.parentNode){window.currentTheme.parentNode.removeChild(window.currentTheme);window.currentTheme=null;}}else{const newHref=getVar("root-path")+encodeURIComponent(newThemeName)+getVar("resource-suffix")+".css";if(!window.currentTheme){if(document.readyState==="loading"){document.write(``);window.currentTheme=(function(){const currentTheme=document.getElementById("themeStyle");return currentTheme instanceof HTMLLinkElement?currentTheme:null;})();}else{window.currentTheme=document.createElement("link");window.currentTheme.rel="stylesheet";window.currentTheme.id="themeStyle";window.currentTheme.href=newHref;document.documentElement.appendChild(window.currentTheme);}}else if(newHref!==window.currentTheme.href){window.currentTheme.href=newHref;}}}const updateTheme=(function(){const mql=window.matchMedia("(prefers-color-scheme: dark)");function updateTheme(){if(getSettingValue("use-system-theme")!=="false"){const lightTheme=getSettingValue("preferred-light-theme")||"light";const darkTheme=getSettingValue("preferred-dark-theme")||"dark";updateLocalStorage("use-system-theme","true");switchTheme(mql.matches?darkTheme:lightTheme,true);}else{switchTheme(getSettingValue("theme"),false);}}mql.addEventListener("change",updateTheme);return updateTheme;})();if(getSettingValue("use-system-theme")!=="false"&&window.matchMedia){if(getSettingValue("use-system-theme")===null&&getSettingValue("preferred-dark-theme")===null&&localStoredTheme!==null&&darkThemes.indexOf(localStoredTheme)>=0){updateLocalStorage("preferred-dark-theme",localStoredTheme);}}updateTheme();if(getSettingValue("source-sidebar-show")==="true"){addClass(document.documentElement,"src-sidebar-expanded");}if(getSettingValue("hide-sidebar")==="true"){addClass(document.documentElement,"hide-sidebar");}if(getSettingValue("hide-toc")==="true"){addClass(document.documentElement,"hide-toc");}if(getSettingValue("hide-modnav")==="true"){addClass(document.documentElement,"hide-modnav");}if(getSettingValue("sans-serif-fonts")==="true"){addClass(document.documentElement,"sans-serif");}if(getSettingValue("word-wrap-source-code")==="true"){addClass(document.documentElement,"word-wrap-source-code");}function updateSidebarWidth(){const desktopSidebarWidth=getSettingValue("desktop-sidebar-width");if(desktopSidebarWidth&&desktopSidebarWidth!=="null"){document.documentElement.style.setProperty("--desktop-sidebar-width",desktopSidebarWidth+"px",);}const srcSidebarWidth=getSettingValue("src-sidebar-width");if(srcSidebarWidth&&srcSidebarWidth!=="null"){document.documentElement.style.setProperty("--src-sidebar-width",srcSidebarWidth+"px",);}}updateSidebarWidth();window.addEventListener("pageshow",ev=>{if(ev.persisted){setTimeout(updateTheme,0);setTimeout(updateSidebarWidth,0);}});class RustdocToolbarElement extends HTMLElement{constructor(){super();}connectedCallback(){if(this.firstElementChild){return;}const rootPath=getVar("root-path");const currentUrl=window.location.href.split("?")[0].split("#")[0];this.innerHTML=` +
+ Search +
+
+ Settings +
+
+ Help +
+ `;}}window.customElements.define("rustdoc-toolbar",RustdocToolbarElement);class RustdocTopBarElement extends HTMLElement{constructor(){super();}connectedCallback(){const rootPath=getVar("root-path");const tmplt=document.createElement("template");tmplt.innerHTML=` + + + + + `;const shadow=this.attachShadow({mode:"open"});shadow.appendChild(tmplt.content.cloneNode(true));this.innerHTML+=` + +
+ Settings +
+
+ Help +
+ `;}}window.customElements.define("rustdoc-topbar",RustdocTopBarElement); \ No newline at end of file diff --git a/pr/395/docs/static.files/stringdex-828709d0.js b/pr/395/docs/static.files/stringdex-828709d0.js new file mode 100644 index 000000000..b8268d530 --- /dev/null +++ b/pr/395/docs/static.files/stringdex-828709d0.js @@ -0,0 +1,2 @@ +const EMPTY_UINT8=new Uint8Array();class RoaringBitmap{constructor(u8array,startingOffset){const start=startingOffset?startingOffset:0;let i=start;this.keysAndCardinalities=EMPTY_UINT8;this.containers=[];this.consumed_len_bytes=0;if(u8array===null||u8array.length===i||u8array[i]===0){return this;}else if(u8array[i]>0xf0){const lspecial=u8array[i]&0x0f;this.keysAndCardinalities=new Uint8Array(lspecial*4);let pspecial=i+1;let key=u8array[pspecial+2]|(u8array[pspecial+3]<<8);let value=u8array[pspecial]|(u8array[pspecial+1]<<8);let entry=(key<<16)|value;let container;container=new RoaringBitmapArray(1,new Uint8Array(4));container.array[0]=value&0xFF;container.array[1]=(value>>8)&0xFF;this.containers.push(container);this.keysAndCardinalities[0]=key;this.keysAndCardinalities[1]=key>>8;pspecial+=4;for(let ispecial=1;ispecial>16;container=this.addToArrayAt(key);const cardinalityOld=container.cardinality;container.array[cardinalityOld*2]=value&0xFF;container.array[(cardinalityOld*2)+1]=(value>>8)&0xFF;container.cardinality=cardinalityOld+1;pspecial+=2;}this.consumed_len_bytes=pspecial-i;return this;}else if(u8array[i]<0x3a){const lspecial=u8array[i];this.keysAndCardinalities=new Uint8Array(lspecial*4);let pspecial=i+1;for(let ispecial=0;ispecial>8)&0xFF;container.cardinality=cardinalityOld+1;pspecial+=4;}this.consumed_len_bytes=pspecial-i;return this;}const has_runs=u8array[i]===0x3b;if(u8array[i]!==0x3a&&u8array[i]!==0x3b){throw new Error("not a roaring bitmap: "+u8array[i]);}const size=has_runs?((u8array[i+2]|(u8array[i+3]<<8))+1):((u8array[i+4]|(u8array[i+5]<<8)|(u8array[i+6]<<16)|(u8array[i+7]<<24)));i+=has_runs?4:8;let is_run;if(has_runs){const is_run_len=(size+7)>>3;is_run=new Uint8Array(u8array.buffer,i+u8array.byteOffset,is_run_len);i+=is_run_len;}else{is_run=EMPTY_UINT8;}this.keysAndCardinalities=u8array.subarray(i,i+(size*4));i+=size*4;let offsets=null;if(!has_runs||size>=4){offsets=[];for(let j=0;j>3]&(1<<(j&0x7))){const runcount=(u8array[i]|(u8array[i+1]<<8));i+=2;this.containers.push(new RoaringBitmapRun(runcount,new Uint8Array(u8array.buffer,i+u8array.byteOffset,runcount*4),));i+=runcount*4;}else if(cardinality>=4096){this.containers.push(new RoaringBitmapBits(new Uint8Array(u8array.buffer,i+u8array.byteOffset,8192,)));i+=8192;}else{const end=cardinality*2;this.containers.push(new RoaringBitmapArray(cardinality,new Uint8Array(u8array.buffer,i+u8array.byteOffset,end),));i+=end;}}this.consumed_len_bytes=i-start;}static makeSingleton(number){const result=new RoaringBitmap(null,0);result.keysAndCardinalities=Uint8Array.of((number>>16),(number>>24),0,0,);result.containers.push(new RoaringBitmapArray(1,Uint8Array.of(number,number>>8),));return result;}static everything(){if(EVERYTHING_BITMAP.isEmpty()){let i=0;const l=1<<16;const everything_range=new RoaringBitmapRun(1,Uint8Array.of(0,0,0xff,0xff));EVERYTHING_BITMAP.keysAndCardinalities=new Uint8Array(l*4);while(i>8;EVERYTHING_BITMAP.keysAndCardinalities[(i*4)+2]=0xff;EVERYTHING_BITMAP.keysAndCardinalities[(i*4)+3]=0xff;i+=1;}}return EVERYTHING_BITMAP;}static empty(){return EMPTY_BITMAP;}isEmpty(){return this.containers.length===0;}addToArrayAt(key){let mid=this.getContainerId(key);let container;if(mid===-1){container=new RoaringBitmapArray(0,new Uint8Array(2));mid=this.containers.length;this.containers.push(container);if(mid*4>this.keysAndCardinalities.length){const keysAndContainers=new Uint8Array(mid*8);keysAndContainers.set(this.keysAndCardinalities);this.keysAndCardinalities=keysAndContainers;}this.keysAndCardinalities[(mid*4)+0]=key;this.keysAndCardinalities[(mid*4)+1]=key>>8;}else{container=this.containers[mid];const cardinalityOld=this.keysAndCardinalities[(mid*4)+2]|(this.keysAndCardinalities[(mid*4)+3]<<8);const cardinality=cardinalityOld+1;this.keysAndCardinalities[(mid*4)+2]=cardinality;this.keysAndCardinalities[(mid*4)+3]=cardinality>>8;}const cardinalityOld=this.keysAndCardinalities[(mid*4)+2]|(this.keysAndCardinalities[(mid*4)+3]<<8);if(!(container instanceof RoaringBitmapArray)||container.array.byteLength<((cardinalityOld+1)*2)){const newBuf=new Uint8Array((cardinalityOld+1)*4);let idx=0;for(const cvalue of container.values()){newBuf[idx]=cvalue&0xFF;newBuf[idx+1]=(cvalue>>8)&0xFF;idx+=2;}if(container instanceof RoaringBitmapArray){container.cardinality=cardinalityOld;container.array=newBuf;return container;}const newcontainer=new RoaringBitmapArray(cardinalityOld,newBuf);this.containers[mid]=newcontainer;return newcontainer;}else{return container;}}union(that){if(this.isEmpty()){return that;}if(that.isEmpty()){return this;}if(this===RoaringBitmap.everything()||that===RoaringBitmap.everything()){return RoaringBitmap.everything();}let i=0;const il=this.containers.length;let j=0;const jl=that.containers.length;const result=new RoaringBitmap(null,0);result.keysAndCardinalities=new Uint8Array((il+jl)*4);while(i=jl||(i=il||(jthatContainer.array.length?thisContainer.array.length:thatContainer.array.length,);let k=0;const kl=resultArray.length;while(k>8)&0xFF;k+=2;}result.containers.push(new RoaringBitmapArray(resultValues.length,resultArray,));card=resultValues.length;}result.keysAndCardinalities[k+0]=this.keysAndCardinalities[ik+0];result.keysAndCardinalities[k+1]=this.keysAndCardinalities[ik+1];card-=1;result.keysAndCardinalities[k+2]=card;result.keysAndCardinalities[k+3]=card>>8;i+=1;j+=1;}}return result;}intersection(that){if(this.isEmpty()||that.isEmpty()){return EMPTY_BITMAP;}if(this===RoaringBitmap.everything()){return that;}if(that===RoaringBitmap.everything()){return this;}let i=0;const il=this.containers.length;let j=0;const jl=that.containers.length;const result=new RoaringBitmap(null,0);result.keysAndCardinalities=new Uint8Array((il>jl?il:jl)*4);while(i=jl||(i=il||(jthatContainer.array.length?thisContainer.array.length:thatContainer.array.length,);let k=0;const kl=resultArray.length;while(k>8)&0xFF;k+=2;}result.containers.push(new RoaringBitmapArray(resultValues.length,resultArray,));}}if(card!==0){result.keysAndCardinalities[k+0]=this.keysAndCardinalities[ik+0];result.keysAndCardinalities[k+1]=this.keysAndCardinalities[ik+1];card-=1;result.keysAndCardinalities[k+2]=card;result.keysAndCardinalities[k+3]=card>>8;}i+=1;j+=1;}}return result;}contains(keyvalue){const key=keyvalue>>16;const value=keyvalue&0xFFFF;const mid=this.getContainerId(key);return mid===-1?false:this.containers[mid].contains(value);}remove(keyvalue){const key=keyvalue>>16;const value=keyvalue&0xFFFF;const mid=this.getContainerId(key);if(mid===-1){return this;}const container=this.containers[mid];if(!container.contains(value)){return this;}const newCardinality=(this.keysAndCardinalities[(mid*4)+2]|(this.keysAndCardinalities[(mid*4)+3]<<8));const l=this.containers.length;const m=l-(newCardinality===0?1:0);const result=new RoaringBitmap(null,0);result.keysAndCardinalities=new Uint8Array(m*4);let j=0;for(let i=0;i>8;const card=newCardinality-1;result.keysAndCardinalities[(j*4)+2]=card;result.keysAndCardinalities[(j*4)+3]=card>>8;const newContainer=new RoaringBitmapArray(newCardinality,new Uint8Array(newCardinality*2),);let newContainerSlot=0;for(const containerValue of container.values()){if(containerValue!==value){newContainer.array[newContainerSlot]=value&0xFF;newContainerSlot+=1;newContainer.array[newContainerSlot]=value>>8;newContainerSlot+=1;}}result.containers.push(newContainer);j+=1;}}else{result.keysAndCardinalities[(j*4)+0]=this.keysAndCardinalities[(i*4)+0];result.keysAndCardinalities[(j*4)+1]=this.keysAndCardinalities[(i*4)+1];result.keysAndCardinalities[(j*4)+2]=this.keysAndCardinalities[(i*4)+2];result.keysAndCardinalities[(j*4)+3]=this.keysAndCardinalities[(i*4)+3];result.containers.push(this.containers[i]);j+=1;}}return result;}getContainerId(key){let left=0;let right=this.containers.length-1;while(left<=right){const mid=Math.floor((left+right)/2);const x=this.keysAndCardinalities[(mid*4)]|(this.keysAndCardinalities[(mid*4)+1]<<8);if(xkey){right=mid-1;}else{return mid;}}return-1;}*entries(){const l=this.containers.length;for(let i=0;i>1;const i=mid*4;const start=this.array[i]|(this.array[i+1]<<8);const lenm1=this.array[i+2]|(this.array[i+3]<<8);if((start+lenm1)value){right=mid-1;}else{return true;}}return false;}*values(){let i=0;while(i>1;const i=mid*2;const x=this.array[i]|(this.array[i+1]<<8);if(xvalue){right=mid-1;}else{return true;}}return false;}*values(){let i=0;const l=this.cardinality*2;while(i>3]&(1<<(value&7)));}*values(){let i=0;const l=this.array.length<<3;while(i=this.values.length*9){const keys=this.keys;const values=this.values;const l=values.length;this.capacityClass+=1;const capacity=1<otherDistance){const otherKey=keys.slice(j,j+6);values[slot]=value;value=otherValue;keys[j+0]=key[start+0];keys[j+1]=key[start+1];keys[j+2]=key[start+2];keys[j+3]=key[start+3];keys[j+4]=key[start+4];keys[j+5]=key[start+5];key=otherKey;start=0;distance=otherDistance;}distance+=1;slot=(slot+1)&mask;}}}get(key){if(key.length!==6){throw"invalid key";}return this.getWithOffsetKey(key,0);}getWithOffsetKey(key,start){const mask=~(0xffffffff<otherDistance){break;}}slot=(slot+1)&mask;}return undefined;}}function bitCount(n){n=(~~n)-((n>>1)&0x55555555);n=(n&0x33333333)+((n>>2)&0x33333333);return((n+(n>>4)&0xF0F0F0F)*0x1010101)>>24;}class Uint8ArraySearchPattern{constructor(needle){this.needle=needle;this.skipTable=[];const m=needle.length;for(let i=0;i<256;i+=1){this.skipTable.push(m);}for(let i=0;i=m){for(let i=m-1;i>=0;i-=1){if(haystack[skip+i]!==needle[i]){skip+=skipTable[haystack[skip+m-1]];continue search;}}return true;}return false;}}function loadDatabase(hooks){const callbacks={rr_:function(data){const dataObj=JSON.parse(data);for(const colName of Object.keys(dataObj)){if(Object.hasOwn(dataObj[colName],"N")){const counts=[];const countsstring=dataObj[colName]["N"];let i=0;const l=countsstring.length;while(i>4)&0x0f));const id3=id2+(((nodeid[4]&0x0f)<<8)|nodeid[5]);leaves=RoaringBitmap.makeSingleton(id1).union(RoaringBitmap.makeSingleton(id2)).union(RoaringBitmap.makeSingleton(id3));}else{leaves=RoaringBitmap.makeSingleton((nodeid[2]<<24)|(nodeid[3]<<16)|(nodeid[4]<<8)|nodeid[5],);}if(isWhole){const data=(nodeid[0]&0x20)!==0?Uint8Array.of(((nodeid[0]&0x0f)<<4)|(nodeid[1]>>4)):EMPTY_UINT8;newPromise=Promise.resolve(new PrefixSearchTree(EMPTY_SEARCH_TREE_BRANCHES,EMPTY_SEARCH_TREE_BRANCHES,data,leaves,EMPTY_BITMAP,));}else{const data=(nodeid[0]&0xf0)===0x80?0:(((nodeid[0]&0x0f)<<4)|(nodeid[1]>>4));newPromise=Promise.resolve(new SuffixSearchTree(EMPTY_SEARCH_TREE_BRANCHES,data,leaves,));}}else{const hashHex=makeHexFromUint8Array(nodeid);newPromise=new Promise((resolve,reject)=>{const cb=registry.searchTreeLoadPromiseCallbacks.get(nodeid);if(cb){registry.searchTreeLoadPromiseCallbacks.set(nodeid,(err,data)=>{cb(err,data);if(data){resolve(data);}else{reject(err);}});}else{registry.searchTreeLoadPromiseCallbacks.set(nodeid,(err,data)=>{if(data){resolve(data);}else{reject(err);}});hooks.loadTreeByHash(hashHex);}});}registry.searchTreePromises.set(nodeid,newPromise);return newPromise;},dataLoadByNameAndHash:function(name,hash){const existingBucket=registry.dataColumnsBuckets.get(hash);if(existingBucket){return existingBucket;}const hashHex=makeHexFromUint8Array(hash);const newBucket=new Promise((resolve,reject)=>{const cb=registry.dataColumnLoadPromiseCallbacks.get(hash);if(cb){registry.dataColumnLoadPromiseCallbacks.set(hash,(err,data)=>{cb(err,data);if(data){resolve(data);}else{reject(err);}});}else{registry.dataColumnLoadPromiseCallbacks.set(hash,(err,data)=>{if(data){resolve(data);}else{reject(err);}});hooks.loadDataByNameAndHash(name,hashHex);}});registry.dataColumnsBuckets.set(hash,newBucket);return newBucket;},};class SearchTreeBranches{constructor(length,nodeids){this.nodeids=nodeids;this.subtrees=[];for(let i=0;i=this.keys[i]){throw new Error("HERE");}i+=1;}}*entries(){let i=0;const l=this.keys.length;while(i>1;if(this.keys[mid]k){right=mid-1;}else{return mid;}}return-1;}getKey(i){return this.keys[i];}getKeys(){return this.keys;}}const EMPTY_SEARCH_TREE_BRANCHES=new SearchTreeBranchesArray(EMPTY_UINT8,EMPTY_UINT8,);const SHORT_ALPHABITMAP_CHARS=[];for(let i=0x61;i<=0x7A;++i){if(i===0x76||i===0x71){continue;}SHORT_ALPHABITMAP_CHARS.push(i);}const LONG_ALPHABITMAP_CHARS=[0x31,0x32,0x33,0x34,0x35,0x36];for(let i=0x61;i<=0x7A;++i){LONG_ALPHABITMAP_CHARS.push(i);}function makeSearchTreeBranchesAlphaBitmapClass(alphabitmap_chars,width){const bitwidth=width*8;const cls=class SearchTreeBranchesAlphaBitmap extends SearchTreeBranches{constructor(bitmap,nodeids){super(nodeids.length/6,nodeids);if(nodeids.length/6!==bitCount(bitmap)){throw new Error(`mismatch ${bitmap} ${nodeids}`);}this.bitmap=bitmap;this.nodeids=nodeids;}*entries(){let i=0;let j=0;while(i=this.subtrees.length?-1:result;}getKey(branch_index){return this.getKeys()[branch_index];}getKeys(){const length=bitCount(this.bitmap);const result=new Uint8Array(length);let result_index=0;for(let alpha_index=0;alpha_index=6?new Lev2TParametricDescription(w):new Lev1TParametricDescription(w);const stack=[[Promise.resolve(this.trie(dataColumn,searchPattern)),0]];const n=levParams.n;while(stack.length!==0){const[triePromise,levState]=stack.pop();const trie=await triePromise;for(const byte of trie.keysExcludeSuffixOnly()){const levPos=levParams.getPosition(levState);const vector=levParams.getVector(name,byte,levPos,Math.min(w,levPos+(2*n)+1),);const newLevState=levParams.transition(levState,levPos,vector,);if(newLevState>=0){const child=trie.child(byte);if(child){stack.push([child,newLevState]);if(levParams.isAccept(newLevState)){yield child;}}}}}}getCurrentLeaves(){return this.leaves_whole.union(this.leaves_suffix);}}class PrefixTrie{constructor(tree,offset,dataColumn,searchPattern){this.tree=tree;this.offset=offset;this.dataColumn=dataColumn;this.searchPattern=searchPattern;}matches(){if(this.offset===this.tree.data.length){return this.tree.leaves_whole;}else{return EMPTY_BITMAP;}}async*substringMatches(){let layer=[Promise.resolve(this.tree)];while(layer.length){const current_layer=layer;layer=[];for await(const tree of current_layer){let rejected=null;let leaves=tree.getCurrentLeaves();for(const leaf of leaves.entries()){const haystack=await this.dataColumn.at(leaf);if(haystack===undefined||!this.searchPattern.matches(haystack)){if(!rejected){rejected=[];}rejected.push(leaf);}}if(rejected){if(leaves.cardinality()!==rejected.length){for(const rej of rejected){leaves=leaves.remove(rej);}yield leaves;}}else{yield leaves;}}const subnodes=new HashTable();for await(const node of current_layer){const branches=node.branches;const l=branches.subtrees.length;for(let i=0;i0&&backlog[backlogSlot].length>1].length){const parentSlot=(backlogSlot-1)>>1;const parent=backlog[parentSlot];backlog[parentSlot]=backlog[backlogSlot];backlog[backlogSlot]=parent;backlogSlot=parentSlot;}}while(backlog.length!==0){const backlogEntry=backlog[0];if(minLength!==null&&backlogEntry.length>minLength){break;}if(!backlogEntry.bitmap.isEmpty()){yield backlogEntry.bitmap;}backlog[0]=backlog[backlog.length-1];backlog.length-=1;let backlogSlot=0;const backlogLength=backlog.length;while(backlogSlot{return node.trie(this.dataColumn,this.searchPattern);})]);i+=1;}return nodes;}else{const codePoint=data[this.offset];const trie=new PrefixTrie(this.tree,this.offset+1,this.dataColumn,this.searchPattern,);return[[codePoint,Promise.resolve(trie)]];}}keysExcludeSuffixOnly(){const data=this.tree.data;if(this.offset===data.length){return this.tree.might_have_prefix_branches.getKeys();}else{return Uint8Array.of(data[this.offset]);}}childrenExcludeSuffixOnly(){const data=this.tree.data;if(this.offset===data.length){const nodes=[];let i=0;for(const[k,v]of this.tree.might_have_prefix_branches.entries()){let node;if(v){node=v;}else{const newnode=this.tree.might_have_prefix_branches.getNodeID(i);if(!newnode){throw new Error(`malformed tree; no node for key ${k}`);}node=registry.searchTreeLoadByNodeID(newnode);this.tree.might_have_prefix_branches.subtrees[i]=node;this.tree.branches.subtrees[this.tree.branches.getIndex(k)]=node;}nodes.push([k,node.then(node=>{return node.trie(this.dataColumn,this.searchPattern);})]);i+=1;}return nodes;}else{const codePoint=data[this.offset];const trie=new PrefixTrie(this.tree,this.offset+1,this.dataColumn,this.searchPattern,);return[[codePoint,Promise.resolve(trie)]];}}child(byte){if(this.offset===this.tree.data.length){const i=this.tree.branches.getIndex(byte);if(i!==-1){let branch=this.tree.branches.subtrees[i];if(branch===null){const newnode=this.tree.branches.getNodeID(i);if(!newnode){throw new Error(`malformed tree; no node for key ${byte}`);}branch=registry.searchTreeLoadByNodeID(newnode);this.tree.branches.subtrees[i]=branch;const mhpI=this.tree.might_have_prefix_branches.getIndex(byte);if(mhpI!==-1){this.tree.might_have_prefix_branches.subtrees[mhpI]=branch;}}return branch.then(branch=>branch.trie(this.dataColumn,this.searchPattern));}}else if(this.tree.data[this.offset]===byte){return Promise.resolve(new PrefixTrie(this.tree,this.offset+1,this.dataColumn,this.searchPattern,));}return null;}}class SuffixSearchTree{constructor(branches,dataLen,leaves_suffix,){this.branches=branches;this.dataLen=dataLen;this.leaves_suffix=leaves_suffix;}trie(dataColumn,searchPattern){return new SuffixTrie(this,0,dataColumn,searchPattern);}async search(name,dataColumn){if(typeof name==="string"){const utf8encoder=new TextEncoder();name=utf8encoder.encode(name);}const searchPattern=new Uint8ArraySearchPattern(name);let trie=this.trie(dataColumn,searchPattern);for(const datum of name){const newTrie=trie.child(datum);if(newTrie){trie=await newTrie;}else{return null;}}return trie;}async*searchLev(_name,_dataColumn){}getCurrentLeaves(){return this.leaves_suffix;}}class SuffixTrie{constructor(tree,offset,dataColumn,searchPattern){this.tree=tree;this.offset=offset;this.dataColumn=dataColumn;this.searchPattern=searchPattern;}matches(){return EMPTY_BITMAP;}async*substringMatches(){let layer=[Promise.resolve(this.tree)];while(layer.length){const current_layer=layer;layer=[];for await(const tree of current_layer){let rejected=null;let leaves=tree.getCurrentLeaves();for(const leaf of leaves.entries()){const haystack=await this.dataColumn.at(leaf);if(haystack===undefined||!this.searchPattern.matches(haystack)){if(!rejected){rejected=[];}rejected.push(leaf);}}if(rejected){if(leaves.cardinality()!==rejected.length){for(const rej of rejected){leaves=leaves.remove(rej);}yield leaves;}}else{yield leaves;}}const subnodes=new HashTable();for await(const node of current_layer){const branches=node.branches;const l=branches.subtrees.length;for(let i=0;ibranch.trie(this.dataColumn,this.searchPattern));}}else{return Promise.resolve(new SuffixTrie(this.tree,this.offset+1,this.dataColumn,this.searchPattern,));}return null;}}class DataColumn{constructor(counts,hashes,emptyset,name,searchTree){this.searchTree=searchTree;this.hashes=hashes;this.emptyset=emptyset;this.name=name;this.buckets=[];this.bucket_keys=[];const l=counts.length;let k=0;let totalLength=0;for(let i=0;i=this.bucket_keys.length){return undefined;}else{const start=this.bucket_keys[idx];const bucket=this.buckets[idx];const data=this.buckets[idx].data;if(data===null){return this.atAsyncFetch(id,start,bucket);}else{return data[id-start];}}}}async atAsyncFetch(id,start,bucket){const{hash,end}=bucket;const dataSansEmptysetOrig=await registry.dataLoadByNameAndHash(this.name,hash,);let data=bucket.data;if(data!==null){return data[id-start];}const dataSansEmptyset=[...dataSansEmptysetOrig];let dataWithEmptyset=null;let pos=start;let insertCount=0;while(pos=48&&c<=63){dataSansEmptyset.push(backrefs[c-48]);i+=1;}else{let n=0;while(c<96){n=(n<<4)|(c&0xF);i+=1;c=data[i];}n=(n<<4)|(c&0xF);i+=1;const item=data.subarray(i,i+n);dataSansEmptyset.push(item);i+=n;backrefs.unshift(item);if(backrefs.length>16){backrefs.pop();}}}cb(null,dataSansEmptyset);}}function makeSearchTreeFromBase64(inputBase64){const input=makeUint8ArrayFromBase64(inputBase64);let i=0;const l=input.length;const stash=new HashTable();const hash=Uint8Array.of(0,0,0,0,0,0,0,0);const truncatedHash=new Uint8Array(hash.buffer,2,6);const hash_history=[];const data_history=[];let canonical=EMPTY_UINT8;let tree=new PrefixSearchTree(EMPTY_SEARCH_TREE_BRANCHES,EMPTY_SEARCH_TREE_BRANCHES,EMPTY_UINT8,EMPTY_BITMAP,EMPTY_BITMAP,);function makeBranchesFromBinaryData(input,i,compression_tag,){const is_pure_suffixes_only_node=(compression_tag&0x01)!==0x00;const is_stack_compressed=(compression_tag&0x02)!==0;const is_long_compressed=(compression_tag&0x04)!==0;const all_children_are_compressed=(compression_tag&0xF0)===0xF0&&!is_long_compressed;const any_children_are_compressed=(compression_tag&0xF0)!==0x00||is_long_compressed;const start_point=i;let cplen;let cslen;let alphabitmap=null;if(is_pure_suffixes_only_node){cplen=0;cslen=input[i];i+=1;if(cslen>=0xc0){alphabitmap=SearchTreeBranchesLongAlphaBitmap;cslen=cslen&0x3F;}else if(cslen>=0x80){alphabitmap=SearchTreeBranchesShortAlphaBitmap;cslen=cslen&0x7F;}}else{cplen=input[i];i+=1;cslen=input[i];i+=1;if(cplen===0xff&&cslen===0xff){cplen=0x100;cslen=0;}else if(cplen>=0xc0&&cslen>=0xc0){alphabitmap=SearchTreeBranchesLongAlphaBitmap;cplen=cplen&0x3F;cslen=cslen&0x3F;}else if(cplen>=0x80&&cslen>=0x80){alphabitmap=SearchTreeBranchesShortAlphaBitmap;cplen=cplen&0x7F;cslen=cslen&0x7F;}}let j=0;let cpnodes;if(any_children_are_compressed){cpnodes=cplen===0?EMPTY_UINT8:new Uint8Array(cplen*6);while(j=cplen||(csicsbranches[csi])){branchset[j]=csbranches[csi];const joff=j*6;const csioff=csi*6;hashes[joff+0]=csnodes[csioff+0];hashes[joff+1]=csnodes[csioff+1];hashes[joff+2]=csnodes[csioff+2];hashes[joff+3]=csnodes[csioff+3];hashes[joff+4]=csnodes[csioff+4];hashes[joff+5]=csnodes[csioff+5];csi+=1;}else{branchset[j]=cpbranches[cpi];const joff=j*6;const cpioff=cpi*6;hashes[joff+0]=cpnodes[cpioff+0];hashes[joff+1]=cpnodes[cpioff+1];hashes[joff+2]=cpnodes[cpioff+2];hashes[joff+3]=cpnodes[cpioff+3];hashes[joff+4]=cpnodes[cpioff+4];hashes[joff+5]=cpnodes[cpioff+5];cpi+=1;}j+=1;}branches=new SearchTreeBranchesArray(branchset,hashes);}i+=cslen;}return{consumed_len_bytes:i-start_point,cpbranches,csbranches,cpnodes,csnodes,branches,might_have_prefix_branches,};}while(i1){const is_long_compressed=(compression_tag&0x04)!==0;const is_data_compressed=(compression_tag&0x08)!==0;i+=1;if(is_long_compressed){compression_tag|=input[i]<<8;i+=1;compression_tag|=input[i]<<16;i+=1;}let dlen=input[i]&0x7F;no_leaves_flag=input[i]&0x80;i+=1;if(is_data_compressed){data=data_history[data_history.length-dlen-1];dlen=data.length;}else if(is_pure_suffixes_only_node){data=EMPTY_UINT8;}else{data=dlen===0?EMPTY_UINT8:new Uint8Array(input.buffer,i+input.byteOffset,dlen);i+=dlen;}const coffset=i;const{cpbranches,csbranches,cpnodes,csnodes,consumed_len_bytes:branches_consumed_len_bytes,branches,might_have_prefix_branches,}=makeBranchesFromBinaryData(input,i,compression_tag);i+=branches_consumed_len_bytes;let whole;let suffix;if(is_pure_suffixes_only_node){if(no_leaves_flag){whole=EMPTY_BITMAP;suffix=EMPTY_BITMAP;}else{suffix=input[i]===0?EMPTY_BITMAP1:new RoaringBitmap(input,i);i+=suffix.consumed_len_bytes;}tree=new SuffixSearchTree(branches,dlen,suffix,);const clen=(3+csnodes.length+csbranches.length+suffix.consumed_len_bytes);if(canonical.length{registry.searchTreeRootCallback=(error,data)=>{if(data){resolve(data);}else{reject(error);}};hooks.loadRoot(callbacks);});}if(typeof window!=="undefined"){window.Stringdex={loadDatabase,};window.RoaringBitmap=RoaringBitmap;if(window.StringdexOnload){window.StringdexOnload.forEach(cb=>cb(window.Stringdex));}}else{module.exports.Stringdex={loadDatabase,};module.exports.RoaringBitmap=RoaringBitmap;}const makeUint8ArrayFromBase64=Uint8Array.fromBase64?Uint8Array.fromBase64:(string=>{const bytes_as_string=atob(string);const l=bytes_as_string.length;const bytes=new Uint8Array(l);for(let i=0;i{const alpha={"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"a":10,"b":11,"A":10,"B":11,"c":12,"d":13,"C":12,"D":13,"e":14,"f":15,"E":14,"F":15,};const l=string.length>>1;const bytes=new Uint8Array(l);for(let i=0;iarray.toHex()):(array=>{const alpha=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f",];const l=array.length;const v=[];for(let i=0;i>4]);v.push(alpha[array[i]&0xf]);}return v.join("");});function siphashOfBytes(input,k0lo,k0hi,k1lo,k1hi,output){let v0lo=k0lo ^ 0x70736575;let v0hi=k0hi ^ 0x736f6d65;let v1lo=k1lo ^ 0x6e646f6d;let v1hi=k1hi ^ 0x646f7261;let v2lo=k0lo ^ 0x6e657261;let v2hi=k0hi ^ 0x6c796765;let v3lo=k1lo ^ 0x79746573;let v3hi=k1hi ^ 0x74656462;const inputLength=input.length;let inputI=0;const left=inputLength&0x7;let milo=0;let mihi=0;while(inputI>>8;output[5]=(v0lo ^ v1lo ^ v2lo ^ v3lo)>>>16;output[4]=(v0lo ^ v1lo ^ v2lo ^ v3lo)>>>24;output[3]=(v0hi ^ v1hi ^ v2hi ^ v3hi)&0xff;output[2]=(v0hi ^ v1hi ^ v2hi ^ v3hi)>>>8;output[1]=(v0hi ^ v1hi ^ v2hi ^ v3hi)>>>16;output[0]=(v0hi ^ v1hi ^ v2hi ^ v3hi)>>>24;function u8ToU64le(offset,length){const n0=offset>>0)+(v1lo>>>0)>0xffffffff)?1:0))|0;v0lo=(v0lo+v1lo)|0;let v1lo_=v1lo;let v1hi_=v1hi;v1lo=(v1lo_<<13)|(v1hi_>>>19);v1hi=(v1hi_<<13)|(v1lo_>>>19);v1lo ^=v0lo;v1hi ^=v0hi;const v0lo_=v0lo;const v0hi_=v0hi;v0lo=v0hi_;v0hi=v0lo_;v2hi=(v2hi+v3hi+(((v2lo>>>0)+(v3lo>>>0)>0xffffffff)?1:0))|0;v2lo=(v2lo+v3lo)|0;let v3lo_=v3lo;let v3hi_=v3hi;v3lo=(v3lo_<<16)|(v3hi_>>>16);v3hi=(v3hi_<<16)|(v3lo_>>>16);v3lo ^=v2lo;v3hi ^=v2hi;v0hi=(v0hi+v3hi+(((v0lo>>>0)+(v3lo>>>0)>0xffffffff)?1:0))|0;v0lo=(v0lo+v3lo)|0;v3lo_=v3lo;v3hi_=v3hi;v3lo=(v3lo_<<21)|(v3hi_>>>11);v3hi=(v3hi_<<21)|(v3lo_>>>11);v3lo ^=v0lo;v3hi ^=v0hi;v2hi=(v2hi+v1hi+(((v2lo>>>0)+(v1lo>>>0)>0xffffffff)?1:0))|0;v2lo=(v2lo+v1lo)|0;v1lo_=v1lo;v1hi_=v1hi;v1lo=(v1lo_<<17)|(v1hi_>>>15);v1hi=(v1hi_<<17)|(v1lo_>>>15);v1lo ^=v2lo;v1hi ^=v2hi;const v2lo_=v2lo;const v2hi_=v2hi;v2lo=v2hi_;v2hi=v2lo_;}}class ParametricDescription{constructor(w,n,minErrors){this.w=w;this.n=n;this.minErrors=minErrors;}isAccept(absState){const state=Math.floor(absState/(this.w+1));const offset=absState%(this.w+1);return this.w-offset+this.minErrors[state]<=this.n;}getPosition(absState){return absState%(this.w+1);}getVector(name,charCode,pos,end){let vector=0;for(let i=pos;i>5;const bitStart=bitLoc&31;if(bitStart+bitsPerValue<=32){return((data[dataLoc]>>bitStart)&this.MASKS[bitsPerValue-1]);}else{const part=32-bitStart;return ~~(((data[dataLoc]>>bitStart)&this.MASKS[part-1])+((data[1+dataLoc]&this.MASKS[bitsPerValue-part-1])<Dir"],["impl<'arbitrary> Arbitrary<'arbitrary> for Side"],["impl<'arbitrary> Arbitrary<'arbitrary> for StreamId"],["impl<'arbitrary> Arbitrary<'arbitrary> for VarInt"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[709]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/clap_builder/derive/trait.Args.js b/pr/395/docs/trait.impl/clap_builder/derive/trait.Args.js new file mode 100644 index 000000000..0ebbfe755 --- /dev/null +++ b/pr/395/docs/trait.impl/clap_builder/derive/trait.Args.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Args for Opt"]]],["perf",[["impl Args for Opt"],["impl Args for Opt"],["impl Args for CommonOpt"]]],["quinn_perf",[["impl Args for Cli"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[116,368,132]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js b/pr/395/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js new file mode 100644 index 000000000..6b11f051f --- /dev/null +++ b/pr/395/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl CommandFactory for Opt"]]],["perf",[["impl CommandFactory for Opt"],["impl CommandFactory for Opt"],["impl CommandFactory for CommonOpt"]]],["quinn_perf",[["impl CommandFactory for Cli"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[126,398,142]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js b/pr/395/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js new file mode 100644 index 000000000..48a955b6f --- /dev/null +++ b/pr/395/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl FromArgMatches for Opt"]]],["perf",[["impl FromArgMatches for Opt"],["impl FromArgMatches for Opt"],["impl FromArgMatches for CommonOpt"]]],["quinn_perf",[["impl FromArgMatches for Commands"],["impl FromArgMatches for Cli"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[126,398,276]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/clap_builder/derive/trait.Parser.js b/pr/395/docs/trait.impl/clap_builder/derive/trait.Parser.js new file mode 100644 index 000000000..823b963bf --- /dev/null +++ b/pr/395/docs/trait.impl/clap_builder/derive/trait.Parser.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Parser for Opt"]]],["perf",[["impl Parser for Opt"],["impl Parser for Opt"],["impl Parser for CommonOpt"]]],["quinn_perf",[["impl Parser for Cli"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[118,374,134]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/clap_builder/derive/trait.Subcommand.js b/pr/395/docs/trait.impl/clap_builder/derive/trait.Subcommand.js new file mode 100644 index 000000000..18f0f2f47 --- /dev/null +++ b/pr/395/docs/trait.impl/clap_builder/derive/trait.Subcommand.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["quinn_perf",[["impl Subcommand for Commands"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[146]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js b/pr/395/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js new file mode 100644 index 000000000..c39cd5dd7 --- /dev/null +++ b/pr/395/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["perf",[["impl ValueEnum for CongestionAlgorithm"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[160]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/clone/trait.Clone.js b/pr/395/docs/trait.impl/core/clone/trait.Clone.js new file mode 100644 index 000000000..6966d52bc --- /dev/null +++ b/pr/395/docs/trait.impl/core/clone/trait.Clone.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Clone for CipherSuite"],["impl Clone for Opt"]]],["iroh_quinn",[["impl Clone for ReadError"],["impl Clone for ReadExactError"],["impl Clone for ReadToEndError"],["impl Clone for ResetError"],["impl Clone for SendDatagramError"],["impl Clone for StoppedError"],["impl Clone for WriteError"],["impl Clone for Connection"],["impl Clone for Endpoint"],["impl Clone for EndpointStats"],["impl Clone for WeakConnectionHandle"]]],["iroh_quinn_proto",[["impl Clone for ClosePathError"],["impl Clone for ConfigError"],["impl Clone for ConnectError"],["impl Clone for ConnectionError"],["impl Clone for Dir"],["impl Clone for EcnCodepoint"],["impl Clone for FinishError"],["impl Clone for FrameType"],["impl Clone for LongType"],["impl Clone for MaybeFrame"],["impl Clone for PacketDecodeError"],["impl Clone for PathError"],["impl Clone for PathEvent"],["impl Clone for PathStatus"],["impl Clone for ProtectedHeader"],["impl Clone for ReadError"],["impl Clone for ReadableError"],["impl Clone for SendDatagramError"],["impl Clone for SetPathStatusError"],["impl Clone for Side"],["impl Clone for WriteError"],["impl Clone for Event"],["impl Clone for Error"],["impl Clone for UnexpectedEnd"],["impl Clone for Bbr"],["impl Clone for BbrConfig"],["impl Clone for Cubic"],["impl Clone for CubicConfig"],["impl Clone for NewReno"],["impl Clone for NewRenoConfig"],["impl Clone for NoInitialCipherSuite"],["impl Clone for AckFrequencyConfig"],["impl Clone for ApplicationClose"],["impl Clone for ClientConfig"],["impl Clone for ClosedPath"],["impl Clone for ClosedStream"],["impl Clone for ConnectionClose"],["impl Clone for ConnectionHandle"],["impl Clone for ConnectionId"],["impl Clone for ConnectionStats"],["impl Clone for Datagram"],["impl Clone for DatagramInfo"],["impl Clone for EndpointConfig"],["impl Clone for FourTuple"],["impl Clone for FrameStats"],["impl Clone for IdleTimeout"],["impl Clone for InvalidCid"],["impl Clone for MtuDiscoveryConfig"],["impl Clone for MultipathNotNegotiated"],["impl Clone for PathId"],["impl Clone for PathStats"],["impl Clone for ProtectedInitialHeader"],["impl Clone for RandomConnectionIdGenerator"],["impl Clone for RttEstimator"],["impl Clone for ServerConfig"],["impl Clone for ShouldTransmit"],["impl Clone for StreamId"],["impl Clone for StreamInfo"],["impl Clone for TransportConfig"],["impl Clone for Error"],["impl Clone for Code"],["impl Clone for UdpStats"],["impl Clone for ValidationTokenConfig"],["impl Clone for VarInt"],["impl Clone for VarIntBoundsExceeded"],["impl Clone for Written"],["impl Clone for TransportParameters"]]],["iroh_quinn_udp",[["impl Clone for EcnCodepoint"],["impl Clone for RecvMeta"],["impl<'a> Clone for Transmit<'a>"]]],["perf",[["impl Clone for CongestionAlgorithm"],["impl Clone for OpenStreamStats"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[499,2982,19209,851,568]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/cmp/trait.Eq.js b/pr/395/docs/trait.impl/core/cmp/trait.Eq.js new file mode 100644 index 000000000..739b22559 --- /dev/null +++ b/pr/395/docs/trait.impl/core/cmp/trait.Eq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Eq for CipherSuite"]]],["iroh_quinn",[["impl Eq for ReadError"],["impl Eq for ReadExactError"],["impl Eq for ReadToEndError"],["impl Eq for ResetError"],["impl Eq for SendDatagramError"],["impl Eq for StoppedError"],["impl Eq for WriteError"]]],["iroh_quinn_proto",[["impl Eq for ClosePathError"],["impl Eq for ConfigError"],["impl Eq for ConnectError"],["impl Eq for ConnectionError"],["impl Eq for Dir"],["impl Eq for EcnCodepoint"],["impl Eq for FinishError"],["impl Eq for FrameType"],["impl Eq for LongType"],["impl Eq for MaybeFrame"],["impl Eq for PacketDecodeError"],["impl Eq for PathError"],["impl Eq for PathEvent"],["impl Eq for PathStatus"],["impl Eq for ReadError"],["impl Eq for ReadableError"],["impl Eq for SendDatagramError"],["impl Eq for SetPathStatusError"],["impl Eq for Side"],["impl Eq for StreamEvent"],["impl Eq for WriteError"],["impl Eq for Error"],["impl Eq for UnexpectedEnd"],["impl Eq for ExportKeyingMaterialError"],["impl Eq for ApplicationClose"],["impl Eq for Chunk"],["impl Eq for ClosedPath"],["impl Eq for ClosedStream"],["impl Eq for ConnectionClose"],["impl Eq for ConnectionHandle"],["impl Eq for ConnectionId"],["impl Eq for DatagramInfo"],["impl Eq for FourTuple"],["impl Eq for IdleTimeout"],["impl Eq for InvalidFrameId"],["impl Eq for PathId"],["impl Eq for PathStats"],["impl Eq for ShouldTransmit"],["impl Eq for StreamId"],["impl Eq for StreamInfo"],["impl Eq for Error"],["impl Eq for Code"],["impl Eq for UdpStats"],["impl Eq for VarInt"],["impl Eq for VarIntBoundsExceeded"],["impl Eq for Written"],["impl Eq for TransportParameters"]]],["iroh_quinn_udp",[["impl Eq for EcnCodepoint"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[251,1794,12667,282]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/cmp/trait.Ord.js b/pr/395/docs/trait.impl/core/cmp/trait.Ord.js new file mode 100644 index 000000000..8af068c55 --- /dev/null +++ b/pr/395/docs/trait.impl/core/cmp/trait.Ord.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl Ord for Dir"],["impl Ord for PacketDecodeError"],["impl Ord for ReadError"],["impl Ord for ReadableError"],["impl Ord for SendDatagramError"],["impl Ord for Side"],["impl Ord for WriteError"],["impl Ord for ConnectionHandle"],["impl Ord for ConnectionId"],["impl Ord for IdleTimeout"],["impl Ord for PathId"],["impl Ord for StreamId"],["impl Ord for VarInt"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[3470]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/cmp/trait.PartialEq.js b/pr/395/docs/trait.impl/core/cmp/trait.PartialEq.js new file mode 100644 index 000000000..b4eeae96b --- /dev/null +++ b/pr/395/docs/trait.impl/core/cmp/trait.PartialEq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl PartialEq for CipherSuite"]]],["iroh_quinn",[["impl PartialEq for ReadError"],["impl PartialEq for ReadExactError"],["impl PartialEq for ReadToEndError"],["impl PartialEq for ResetError"],["impl PartialEq for SendDatagramError"],["impl PartialEq for StoppedError"],["impl PartialEq for WriteError"]]],["iroh_quinn_proto",[["impl PartialEq for ClosePathError"],["impl PartialEq for ConfigError"],["impl PartialEq for ConnectError"],["impl PartialEq for ConnectionError"],["impl PartialEq for Dir"],["impl PartialEq for EcnCodepoint"],["impl PartialEq for FinishError"],["impl PartialEq for FrameType"],["impl PartialEq for LongType"],["impl PartialEq for MaybeFrame"],["impl PartialEq for PacketDecodeError"],["impl PartialEq for PathError"],["impl PartialEq for PathEvent"],["impl PartialEq for PathStatus"],["impl PartialEq for ReadError"],["impl PartialEq for ReadableError"],["impl PartialEq for SendDatagramError"],["impl PartialEq for SetPathStatusError"],["impl PartialEq for Side"],["impl PartialEq for StreamEvent"],["impl PartialEq for WriteError"],["impl PartialEq for Error"],["impl PartialEq for UnexpectedEnd"],["impl PartialEq for ExportKeyingMaterialError"],["impl PartialEq for ApplicationClose"],["impl PartialEq for Chunk"],["impl PartialEq for ClosedPath"],["impl PartialEq for ClosedStream"],["impl PartialEq for ConnectionClose"],["impl PartialEq for ConnectionHandle"],["impl PartialEq for ConnectionId"],["impl PartialEq for DatagramInfo"],["impl PartialEq for FourTuple"],["impl PartialEq for IdleTimeout"],["impl PartialEq for InvalidFrameId"],["impl PartialEq for PathId"],["impl PartialEq for PathStats"],["impl PartialEq for ShouldTransmit"],["impl PartialEq for StreamId"],["impl PartialEq for StreamInfo"],["impl PartialEq for Error"],["impl PartialEq for Code"],["impl PartialEq for UdpStats"],["impl PartialEq for VarInt"],["impl PartialEq for VarIntBoundsExceeded"],["impl PartialEq for Written"],["impl PartialEq for TransportParameters"]]],["iroh_quinn_udp",[["impl PartialEq for EcnCodepoint"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[272,1941,13654,303]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/cmp/trait.PartialOrd.js b/pr/395/docs/trait.impl/core/cmp/trait.PartialOrd.js new file mode 100644 index 000000000..fa81432c6 --- /dev/null +++ b/pr/395/docs/trait.impl/core/cmp/trait.PartialOrd.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl PartialOrd for Dir"],["impl PartialOrd for PacketDecodeError"],["impl PartialOrd for ReadError"],["impl PartialOrd for ReadableError"],["impl PartialOrd for SendDatagramError"],["impl PartialOrd for Side"],["impl PartialOrd for WriteError"],["impl PartialOrd for ConnectionHandle"],["impl PartialOrd for ConnectionId"],["impl PartialOrd for IdleTimeout"],["impl PartialOrd for PathId"],["impl PartialOrd for StreamId"],["impl PartialOrd for VarInt"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[3743]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/convert/trait.From.js b/pr/395/docs/trait.impl/core/convert/trait.From.js new file mode 100644 index 000000000..7873e1228 --- /dev/null +++ b/pr/395/docs/trait.impl/core/convert/trait.From.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl From<ConnectionError> for ReadError"],["impl From<ConnectionError> for ResetError"],["impl From<ConnectionError> for SendDatagramError"],["impl From<ConnectionError> for StoppedError"],["impl From<ConnectionError> for WriteError"],["impl From<ReadError> for ReadExactError"],["impl From<ReadError> for ReadToEndError"],["impl From<ReadError> for Error"],["impl From<ResetError> for ReadError"],["impl From<ResetError> for Error"],["impl From<StoppedError> for WriteError"],["impl From<StoppedError> for Error"],["impl From<WriteError> for Error"],["impl From<ClosedStream> for WriteError"]]],["iroh_quinn_proto",[["impl From<ConnectionError> for Error"],["impl From<Dir> for StreamType"],["impl From<PathEvent> for Event"],["impl From<Side> for Side"],["impl From<Error> for TransportError"],["impl From<u16> for VarInt"],["impl From<u32> for VarInt"],["impl From<u8> for VarInt"],["impl From<TryFromIntError> for ConfigError"],["impl From<UnexpectedEnd> for PacketDecodeError"],["impl From<UnexpectedEnd> for Error"],["impl From<UnsupportedVersion> for ConnectError"],["impl From<ClosedStream> for Error"],["impl From<ConnectionHandle> for usize"],["impl From<StreamId> for u64"],["impl From<StreamId> for VarInt"],["impl From<Error> for ConnectionError"],["impl From<Error> for ConnectionClose"],["impl From<Code> for u64"],["impl From<Code> for VarInt"],["impl From<VarInt> for u64"],["impl From<VarInt> for IdleTimeout"],["impl From<VarInt> for StreamId"],["impl From<VarInt> for Code"],["impl From<VarIntBoundsExceeded> for ConfigError"],["impl From<Unspecified> for CryptoError"],["impl<T: Into<u32>> From<T> for PathId"]]],["iroh_quinn_udp",[["impl<'s, S> From<&'s S> for UdpSockRef<'s>
where\n S: AsFd,
"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[5595,10936,620]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/convert/trait.TryFrom.js b/pr/395/docs/trait.impl/core/convert/trait.TryFrom.js new file mode 100644 index 000000000..010a7eb0d --- /dev/null +++ b/pr/395/docs/trait.impl/core/convert/trait.TryFrom.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl TryFrom<u128> for VarInt"],["impl TryFrom<u64> for FrameType"],["impl TryFrom<u64> for VarInt"],["impl TryFrom<usize> for VarInt"],["impl TryFrom<Arc<ClientConfig>> for QuicClientConfig"],["impl TryFrom<Arc<ServerConfig>> for QuicServerConfig"],["impl TryFrom<Duration> for IdleTimeout"],["impl TryFrom<ClientConfig> for QuicClientConfig"],["impl TryFrom<ServerConfig> for QuicServerConfig"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[3696]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/default/trait.Default.js b/pr/395/docs/trait.impl/core/default/trait.Default.js new file mode 100644 index 000000000..51d382b93 --- /dev/null +++ b/pr/395/docs/trait.impl/core/default/trait.Default.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Default for Stats"],["impl Default for StreamStats"]]],["bulk",[["impl Default for ClientStats"]]],["iroh_quinn",[["impl Default for EndpointStats"]]],["iroh_quinn_proto",[["impl Default for PathStatus"],["impl Default for BbrConfig"],["impl Default for ControllerMetrics"],["impl Default for CubicConfig"],["impl Default for NewRenoConfig"],["impl Default for AckFrequencyConfig"],["impl Default for BloomTokenLog"],["impl Default for ClosedPath"],["impl Default for ClosedStream"],["impl Default for ConnectionStats"],["impl Default for EndpointConfig"],["impl Default for FrameStats"],["impl Default for HashedConnectionIdGenerator"],["impl Default for IdleTimeout"],["impl Default for MtuDiscoveryConfig"],["impl Default for PathId"],["impl Default for PathStats"],["impl Default for RandomConnectionIdGenerator"],["impl Default for ShouldTransmit"],["impl Default for TokenMemoryCache"],["impl Default for TransportConfig"],["impl Default for UdpStats"],["impl Default for ValidationTokenConfig"],["impl Default for VarInt"],["impl Default for Written"]]],["iroh_quinn_udp",[["impl Default for RecvMeta"]]],["perf",[["impl Default for OpenStreamStats"],["impl Default for Stats"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[557,278,302,7570,299,565]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/error/trait.Error.js b/pr/395/docs/trait.impl/core/error/trait.Error.js new file mode 100644 index 000000000..3e728c469 --- /dev/null +++ b/pr/395/docs/trait.impl/core/error/trait.Error.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl Error for ReadError"],["impl Error for ReadExactError"],["impl Error for ReadToEndError"],["impl Error for ResetError"],["impl Error for SendDatagramError"],["impl Error for StoppedError"],["impl Error for WriteError"],["impl Error for RetryError"]]],["iroh_quinn_proto",[["impl Error for ClosePathError"],["impl Error for ConfigError"],["impl Error for ConnectError"],["impl Error for ConnectionError"],["impl Error for FinishError"],["impl Error for PacketDecodeError"],["impl Error for PathError"],["impl Error for ReadError"],["impl Error for ReadableError"],["impl Error for SendDatagramError"],["impl Error for SetPathStatusError"],["impl Error for WriteError"],["impl Error for Error"],["impl Error for Error"],["impl Error for UnexpectedEnd"],["impl Error for NoInitialCipherSuite"],["impl Error for ClosedPath"],["impl Error for ClosedStream"],["impl Error for InvalidFrameId"],["impl Error for MultipathNotNegotiated"],["impl Error for RetryError"],["impl Error for Error"],["impl Error for VarIntBoundsExceeded"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[2150,6642]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/fmt/trait.Debug.js b/pr/395/docs/trait.impl/core/fmt/trait.Debug.js new file mode 100644 index 000000000..551bbe323 --- /dev/null +++ b/pr/395/docs/trait.impl/core/fmt/trait.Debug.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Debug for CipherSuite"],["impl Debug for TransferResult"],["impl Debug for Opt"]]],["certificate",[["impl Debug for SkipServerVerification"]]],["iroh_quinn",[["impl Debug for ReadError"],["impl Debug for ReadExactError"],["impl Debug for ReadToEndError"],["impl Debug for ResetError"],["impl Debug for SendDatagramError"],["impl Debug for StoppedError"],["impl Debug for WriteError"],["impl Debug for Connecting"],["impl Debug for Connection"],["impl Debug for Endpoint"],["impl Debug for EndpointStats"],["impl Debug for Incoming"],["impl Debug for IncomingFuture"],["impl Debug for Path"],["impl Debug for RecvStream"],["impl Debug for RetryError"],["impl Debug for SendStream"],["impl Debug for SmolRuntime"],["impl Debug for TokioRuntime"],["impl Debug for UnorderedRecvStream"],["impl Debug for WeakConnectionHandle"]]],["iroh_quinn_proto",[["impl Debug for ClosePathError"],["impl Debug for ConfigError"],["impl Debug for ConnectError"],["impl Debug for ConnectionError"],["impl Debug for Dir"],["impl Debug for EcnCodepoint"],["impl Debug for Event"],["impl Debug for FinishError"],["impl Debug for FrameType"],["impl Debug for LongType"],["impl Debug for MaybeFrame"],["impl Debug for PacketDecodeError"],["impl Debug for PathError"],["impl Debug for PathEvent"],["impl Debug for PathStatus"],["impl Debug for ProtectedHeader"],["impl Debug for ReadError"],["impl Debug for ReadableError"],["impl Debug for SendDatagramError"],["impl Debug for SetPathStatusError"],["impl Debug for Side"],["impl Debug for StreamEvent"],["impl Debug for WriteError"],["impl Debug for Error"],["impl Debug for Event"],["impl Debug for Error"],["impl Debug for UnexpectedEnd"],["impl Debug for Bbr"],["impl Debug for BbrConfig"],["impl Debug for ControllerMetrics"],["impl Debug for Cubic"],["impl Debug for CubicConfig"],["impl Debug for NewReno"],["impl Debug for NewRenoConfig"],["impl Debug for NoInitialCipherSuite"],["impl Debug for CryptoError"],["impl Debug for ExportKeyingMaterialError"],["impl Debug for UnsupportedVersion"],["impl Debug for AcceptError"],["impl Debug for AckFrequencyConfig"],["impl Debug for ApplicationClose"],["impl Debug for Chunk"],["impl Debug for ClientConfig"],["impl Debug for ClosedPath"],["impl Debug for ClosedStream"],["impl Debug for Connection"],["impl Debug for ConnectionClose"],["impl Debug for ConnectionEvent"],["impl Debug for ConnectionHandle"],["impl Debug for ConnectionId"],["impl Debug for ConnectionStats"],["impl Debug for Datagram"],["impl Debug for DatagramInfo"],["impl Debug for Endpoint"],["impl Debug for EndpointConfig"],["impl Debug for EndpointEvent"],["impl Debug for FourTuple"],["impl Debug for FrameStats"],["impl Debug for IdleTimeout"],["impl Debug for Incoming"],["impl Debug for InvalidCid"],["impl Debug for InvalidFrameId"],["impl Debug for MtuDiscoveryConfig"],["impl Debug for MultipathNotNegotiated"],["impl Debug for PartialDecode"],["impl Debug for PathId"],["impl Debug for PathStats"],["impl Debug for ProtectedInitialHeader"],["impl Debug for QlogFileFactory"],["impl Debug for RandomConnectionIdGenerator"],["impl Debug for RetryError"],["impl Debug for RttEstimator"],["impl Debug for ServerConfig"],["impl Debug for ShouldTransmit"],["impl Debug for StreamId"],["impl Debug for StreamInfo"],["impl Debug for TokenMemoryCache"],["impl Debug for Transmit"],["impl Debug for TransportConfig"],["impl Debug for Error"],["impl Debug for Code"],["impl Debug for UdpStats"],["impl Debug for ValidationTokenConfig"],["impl Debug for VarInt"],["impl Debug for VarIntBoundsExceeded"],["impl Debug for Written"],["impl Debug for TransportParameters"]]],["iroh_quinn_udp",[["impl Debug for EcnCodepoint"],["impl Debug for RecvMeta"],["impl Debug for UdpSocketState"],["impl<'a> Debug for Transmit<'a>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[768,318,5582,24602,1121]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/fmt/trait.Display.js b/pr/395/docs/trait.impl/core/fmt/trait.Display.js new file mode 100644 index 000000000..c04b17907 --- /dev/null +++ b/pr/395/docs/trait.impl/core/fmt/trait.Display.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl Display for ReadError"],["impl Display for ReadExactError"],["impl Display for ReadToEndError"],["impl Display for ResetError"],["impl Display for SendDatagramError"],["impl Display for StoppedError"],["impl Display for WriteError"],["impl Display for RetryError"]]],["iroh_quinn_proto",[["impl Display for ClosePathError"],["impl Display for ConfigError"],["impl Display for ConnectError"],["impl Display for ConnectionError"],["impl Display for Dir"],["impl Display for FinishError"],["impl Display for FrameType"],["impl Display for MaybeFrame"],["impl Display for PacketDecodeError"],["impl Display for PathError"],["impl Display for ReadError"],["impl Display for ReadableError"],["impl Display for SendDatagramError"],["impl Display for SetPathStatusError"],["impl Display for WriteError"],["impl Display for Error"],["impl Display for Error"],["impl Display for UnexpectedEnd"],["impl Display for NoInitialCipherSuite"],["impl Display for ApplicationClose"],["impl Display for ClosedPath"],["impl Display for ClosedStream"],["impl Display for ConnectionClose"],["impl Display for ConnectionId"],["impl Display for Datagram"],["impl Display for DatagramInfo"],["impl Display for FourTuple"],["impl Display for InvalidFrameId"],["impl Display for MultipathNotNegotiated"],["impl Display for PathId"],["impl Display for RetryError"],["impl Display for StreamId"],["impl Display for StreamInfo"],["impl Display for Error"],["impl Display for Code"],["impl Display for VarInt"],["impl Display for VarIntBoundsExceeded"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[2166,10582]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/future/future/trait.Future.js b/pr/395/docs/trait.impl/core/future/future/trait.Future.js new file mode 100644 index 000000000..8c8628b9c --- /dev/null +++ b/pr/395/docs/trait.impl/core/future/future/trait.Future.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl Future for Accept<'_>"],["impl Future for AcceptBi<'_>"],["impl Future for AcceptUni<'_>"],["impl Future for ClosePath"],["impl Future for Connecting"],["impl Future for IncomingFuture"],["impl Future for OnClosed"],["impl Future for OpenBi<'_>"],["impl Future for OpenPath"],["impl Future for OpenUni<'_>"],["impl Future for ReadDatagram<'_>"],["impl Future for SendDatagram<'_>"],["impl Future for ZeroRttAccepted"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[3786]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/future/into_future/trait.IntoFuture.js b/pr/395/docs/trait.impl/core/future/into_future/trait.IntoFuture.js new file mode 100644 index 000000000..b07d4bb68 --- /dev/null +++ b/pr/395/docs/trait.impl/core/future/into_future/trait.IntoFuture.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl IntoFuture for Incoming"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[318]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/hash/trait.Hash.js b/pr/395/docs/trait.impl/core/hash/trait.Hash.js new file mode 100644 index 000000000..0018a136e --- /dev/null +++ b/pr/395/docs/trait.impl/core/hash/trait.Hash.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl Hash for Dir"],["impl Hash for PacketDecodeError"],["impl Hash for ReadError"],["impl Hash for ReadableError"],["impl Hash for SendDatagramError"],["impl Hash for Side"],["impl Hash for WriteError"],["impl Hash for ConnectionHandle"],["impl Hash for ConnectionId"],["impl Hash for FourTuple"],["impl Hash for IdleTimeout"],["impl Hash for PathId"],["impl Hash for StreamId"],["impl Hash for VarInt"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[3805]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/marker/trait.Copy.js b/pr/395/docs/trait.impl/core/marker/trait.Copy.js new file mode 100644 index 000000000..3073465ce --- /dev/null +++ b/pr/395/docs/trait.impl/core/marker/trait.Copy.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Copy for CipherSuite"],["impl Copy for Opt"]]],["iroh_quinn",[["impl Copy for EndpointStats"]]],["iroh_quinn_proto",[["impl Copy for Dir"],["impl Copy for EcnCodepoint"],["impl Copy for FrameType"],["impl Copy for LongType"],["impl Copy for MaybeFrame"],["impl Copy for PathError"],["impl Copy for PathStatus"],["impl Copy for Side"],["impl Copy for Error"],["impl Copy for UnexpectedEnd"],["impl Copy for ConnectionHandle"],["impl Copy for ConnectionId"],["impl Copy for DatagramInfo"],["impl Copy for FourTuple"],["impl Copy for FrameStats"],["impl Copy for IdleTimeout"],["impl Copy for InvalidCid"],["impl Copy for MultipathNotNegotiated"],["impl Copy for PathId"],["impl Copy for PathStats"],["impl Copy for RandomConnectionIdGenerator"],["impl Copy for RttEstimator"],["impl Copy for ShouldTransmit"],["impl Copy for StreamId"],["impl Copy for StreamInfo"],["impl Copy for Code"],["impl Copy for UdpStats"],["impl Copy for VarInt"],["impl Copy for VarIntBoundsExceeded"],["impl Copy for Written"],["impl Copy for TransportParameters"]]],["iroh_quinn_udp",[["impl Copy for EcnCodepoint"],["impl Copy for RecvMeta"]]],["perf",[["impl Copy for CongestionAlgorithm"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[497,291,8760,561,285]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/marker/trait.Freeze.js b/pr/395/docs/trait.impl/core/marker/trait.Freeze.js new file mode 100644 index 000000000..787fedeeb --- /dev/null +++ b/pr/395/docs/trait.impl/core/marker/trait.Freeze.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Freeze for CipherSuite",1,["bench::CipherSuite"]],["impl Freeze for Stats",1,["bench::stats::Stats"]],["impl Freeze for StreamStats",1,["bench::stats::StreamStats"]],["impl Freeze for TransferResult",1,["bench::stats::TransferResult"]],["impl Freeze for Opt",1,["bench::Opt"]]]],["bulk",[["impl Freeze for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl Freeze for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl !Freeze for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl !Freeze for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl !Freeze for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl !Freeze for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl !Freeze for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl !Freeze for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl !Freeze for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl !Freeze for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl !Freeze for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl Freeze for AddressDiscovery",1,["iroh_quinn::path::AddressDiscovery"]],["impl Freeze for ClosePath",1,["iroh_quinn::path::ClosePath"]],["impl Freeze for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl Freeze for Connection",1,["iroh_quinn::connection::Connection"]],["impl Freeze for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl Freeze for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl Freeze for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl Freeze for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl Freeze for Path",1,["iroh_quinn::path::Path"]],["impl Freeze for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl Freeze for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl Freeze for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl Freeze for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl Freeze for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl Freeze for UnorderedRecvStream",1,["iroh_quinn::recv_stream::UnorderedRecvStream"]],["impl Freeze for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl Freeze for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl<'a> !Freeze for Accept<'a>",1,["iroh_quinn::endpoint::Accept"]],["impl<'a> !Freeze for AcceptBi<'a>",1,["iroh_quinn::connection::AcceptBi"]],["impl<'a> !Freeze for AcceptUni<'a>",1,["iroh_quinn::connection::AcceptUni"]],["impl<'a> !Freeze for OpenBi<'a>",1,["iroh_quinn::connection::OpenBi"]],["impl<'a> !Freeze for OpenUni<'a>",1,["iroh_quinn::connection::OpenUni"]],["impl<'a> !Freeze for ReadDatagram<'a>",1,["iroh_quinn::connection::ReadDatagram"]],["impl<'a> !Freeze for SendDatagram<'a>",1,["iroh_quinn::connection::SendDatagram"]]]],["iroh_quinn_proto",[["impl !Freeze for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl !Freeze for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl !Freeze for Event",1,["iroh_quinn_proto::connection::Event"]],["impl !Freeze for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl !Freeze for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl !Freeze for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl !Freeze for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl !Freeze for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl !Freeze for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl !Freeze for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl !Freeze for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl !Freeze for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl !Freeze for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl Freeze for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl Freeze for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl Freeze for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl Freeze for Dir",1,["iroh_quinn_proto::Dir"]],["impl Freeze for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl Freeze for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl Freeze for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl Freeze for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl Freeze for MaybeFrame",1,["iroh_quinn_proto::frame::MaybeFrame"]],["impl Freeze for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl Freeze for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl Freeze for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl Freeze for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl Freeze for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl Freeze for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl Freeze for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl Freeze for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl Freeze for Side",1,["iroh_quinn_proto::Side"]],["impl Freeze for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl Freeze for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl Freeze for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl Freeze for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl Freeze for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl Freeze for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl Freeze for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl Freeze for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl Freeze for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl Freeze for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl Freeze for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl Freeze for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl Freeze for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl Freeze for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl Freeze for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl Freeze for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl Freeze for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl Freeze for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl Freeze for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl Freeze for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl Freeze for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl Freeze for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl Freeze for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl Freeze for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl Freeze for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl Freeze for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl Freeze for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl Freeze for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl Freeze for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl Freeze for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl Freeze for DatagramInfo",1,["iroh_quinn_proto::frame::DatagramInfo"]],["impl Freeze for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl Freeze for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl Freeze for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl Freeze for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl Freeze for FourTuple",1,["iroh_quinn_proto::FourTuple"]],["impl Freeze for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl Freeze for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl Freeze for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl Freeze for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl Freeze for InvalidFrameId",1,["iroh_quinn_proto::frame::InvalidFrameId"]],["impl Freeze for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl Freeze for MultipathNotNegotiated",1,["iroh_quinn_proto::connection::MultipathNotNegotiated"]],["impl Freeze for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl Freeze for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl Freeze for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl Freeze for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl Freeze for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl Freeze for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl Freeze for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl Freeze for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl Freeze for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl Freeze for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl Freeze for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl Freeze for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl Freeze for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl Freeze for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl Freeze for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl Freeze for StreamInfo",1,["iroh_quinn_proto::frame::StreamInfo"]],["impl Freeze for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl Freeze for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl Freeze for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl Freeze for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl Freeze for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl Freeze for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl Freeze for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl Freeze for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl Freeze for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl Freeze for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl Freeze for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> Freeze for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> Freeze for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> Freeze for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<'a> Freeze for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> Freeze for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<T> Freeze for KeyPair<T>
where\n T: Freeze,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl !Freeze for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl Freeze for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl Freeze for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl<'a> Freeze for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> Freeze for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl !Freeze for StreamStats",1,["perf::stats::StreamStats"]],["impl Freeze for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl Freeze for Opt",1,["perf::client::Opt"]],["impl Freeze for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl Freeze for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl Freeze for Opt",1,["perf::server::Opt"]],["impl Freeze for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl Freeze for Stats",1,["perf::stats::Stats"]],["impl Freeze for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl Freeze for Commands",1,["quinn_perf::Commands"]],["impl Freeze for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1466,297,369,10531,37070,1629,2824,575]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/marker/trait.Send.js b/pr/395/docs/trait.impl/core/marker/trait.Send.js new file mode 100644 index 000000000..e5e68cd3d --- /dev/null +++ b/pr/395/docs/trait.impl/core/marker/trait.Send.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Send for CipherSuite",1,["bench::CipherSuite"]],["impl Send for Stats",1,["bench::stats::Stats"]],["impl Send for StreamStats",1,["bench::stats::StreamStats"]],["impl Send for TransferResult",1,["bench::stats::TransferResult"]],["impl Send for Opt",1,["bench::Opt"]]]],["bulk",[["impl Send for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl Send for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl Send for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl Send for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl Send for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl Send for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl Send for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl Send for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl Send for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl Send for AddressDiscovery",1,["iroh_quinn::path::AddressDiscovery"]],["impl Send for ClosePath",1,["iroh_quinn::path::ClosePath"]],["impl Send for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl Send for Connection",1,["iroh_quinn::connection::Connection"]],["impl Send for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl Send for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl Send for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl Send for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl Send for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl Send for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl Send for Path",1,["iroh_quinn::path::Path"]],["impl Send for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl Send for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl Send for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl Send for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl Send for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl Send for UnorderedRecvStream",1,["iroh_quinn::recv_stream::UnorderedRecvStream"]],["impl Send for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl Send for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl<'a> Send for Accept<'a>",1,["iroh_quinn::endpoint::Accept"]],["impl<'a> Send for AcceptBi<'a>",1,["iroh_quinn::connection::AcceptBi"]],["impl<'a> Send for AcceptUni<'a>",1,["iroh_quinn::connection::AcceptUni"]],["impl<'a> Send for OpenBi<'a>",1,["iroh_quinn::connection::OpenBi"]],["impl<'a> Send for OpenUni<'a>",1,["iroh_quinn::connection::OpenUni"]],["impl<'a> Send for ReadDatagram<'a>",1,["iroh_quinn::connection::ReadDatagram"]],["impl<'a> Send for SendDatagram<'a>",1,["iroh_quinn::connection::SendDatagram"]]]],["iroh_quinn_proto",[["impl Send for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl Send for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl Send for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl Send for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl Send for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl Send for Dir",1,["iroh_quinn_proto::Dir"]],["impl Send for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl Send for Event",1,["iroh_quinn_proto::connection::Event"]],["impl Send for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl Send for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl Send for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl Send for MaybeFrame",1,["iroh_quinn_proto::frame::MaybeFrame"]],["impl Send for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl Send for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl Send for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl Send for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl Send for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl Send for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl Send for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl Send for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl Send for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl Send for Side",1,["iroh_quinn_proto::Side"]],["impl Send for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl Send for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl Send for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl Send for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl Send for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl Send for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl Send for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl Send for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl Send for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl Send for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl Send for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl Send for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl Send for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl Send for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl Send for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl Send for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl Send for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl Send for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl Send for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl Send for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl Send for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl Send for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl Send for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl Send for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl Send for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl Send for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl Send for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl Send for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl Send for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl Send for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl Send for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl Send for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl Send for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl Send for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl Send for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl Send for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl Send for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl Send for DatagramInfo",1,["iroh_quinn_proto::frame::DatagramInfo"]],["impl Send for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl Send for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl Send for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl Send for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl Send for FourTuple",1,["iroh_quinn_proto::FourTuple"]],["impl Send for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl Send for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl Send for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl Send for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl Send for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl Send for InvalidFrameId",1,["iroh_quinn_proto::frame::InvalidFrameId"]],["impl Send for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl Send for MultipathNotNegotiated",1,["iroh_quinn_proto::connection::MultipathNotNegotiated"]],["impl Send for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl Send for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl Send for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl Send for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl Send for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl Send for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl Send for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl Send for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl Send for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl Send for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl Send for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl Send for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl Send for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl Send for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl Send for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl Send for StreamInfo",1,["iroh_quinn_proto::frame::StreamInfo"]],["impl Send for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl Send for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl Send for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl Send for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl Send for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl Send for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl Send for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl Send for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl Send for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl Send for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl Send for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl Send for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> Send for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> Send for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> Send for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<'a> Send for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> Send for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<T> Send for KeyPair<T>
where\n T: Send,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl Send for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl Send for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl Send for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl<'a> Send for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> Send for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl Send for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl Send for Opt",1,["perf::client::Opt"]],["impl Send for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl Send for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl Send for Opt",1,["perf::server::Opt"]],["impl Send for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl Send for Stats",1,["perf::stats::Stats"]],["impl Send for StreamStats",1,["perf::stats::StreamStats"]],["impl Send for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl Send for Commands",1,["quinn_perf::Commands"]],["impl Send for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1436,291,363,10317,36409,1598,2769,563]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/marker/trait.StructuralPartialEq.js b/pr/395/docs/trait.impl/core/marker/trait.StructuralPartialEq.js new file mode 100644 index 000000000..45565dfe8 --- /dev/null +++ b/pr/395/docs/trait.impl/core/marker/trait.StructuralPartialEq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl StructuralPartialEq for CipherSuite"]]],["iroh_quinn",[["impl StructuralPartialEq for ReadError"],["impl StructuralPartialEq for ReadExactError"],["impl StructuralPartialEq for ReadToEndError"],["impl StructuralPartialEq for ResetError"],["impl StructuralPartialEq for SendDatagramError"],["impl StructuralPartialEq for StoppedError"],["impl StructuralPartialEq for WriteError"]]],["iroh_quinn_proto",[["impl StructuralPartialEq for ClosePathError"],["impl StructuralPartialEq for ConfigError"],["impl StructuralPartialEq for ConnectError"],["impl StructuralPartialEq for ConnectionError"],["impl StructuralPartialEq for Dir"],["impl StructuralPartialEq for EcnCodepoint"],["impl StructuralPartialEq for FinishError"],["impl StructuralPartialEq for FrameType"],["impl StructuralPartialEq for LongType"],["impl StructuralPartialEq for MaybeFrame"],["impl StructuralPartialEq for PacketDecodeError"],["impl StructuralPartialEq for PathError"],["impl StructuralPartialEq for PathEvent"],["impl StructuralPartialEq for PathStatus"],["impl StructuralPartialEq for ReadError"],["impl StructuralPartialEq for ReadableError"],["impl StructuralPartialEq for SendDatagramError"],["impl StructuralPartialEq for SetPathStatusError"],["impl StructuralPartialEq for Side"],["impl StructuralPartialEq for StreamEvent"],["impl StructuralPartialEq for WriteError"],["impl StructuralPartialEq for Error"],["impl StructuralPartialEq for UnexpectedEnd"],["impl StructuralPartialEq for ExportKeyingMaterialError"],["impl StructuralPartialEq for ApplicationClose"],["impl StructuralPartialEq for Chunk"],["impl StructuralPartialEq for ClosedPath"],["impl StructuralPartialEq for ClosedStream"],["impl StructuralPartialEq for ConnectionClose"],["impl StructuralPartialEq for ConnectionHandle"],["impl StructuralPartialEq for ConnectionId"],["impl StructuralPartialEq for DatagramInfo"],["impl StructuralPartialEq for FourTuple"],["impl StructuralPartialEq for IdleTimeout"],["impl StructuralPartialEq for InvalidFrameId"],["impl StructuralPartialEq for PathId"],["impl StructuralPartialEq for PathStats"],["impl StructuralPartialEq for ShouldTransmit"],["impl StructuralPartialEq for StreamId"],["impl StructuralPartialEq for StreamInfo"],["impl StructuralPartialEq for Code"],["impl StructuralPartialEq for UdpStats"],["impl StructuralPartialEq for VarInt"],["impl StructuralPartialEq for VarIntBoundsExceeded"],["impl StructuralPartialEq for Written"],["impl StructuralPartialEq for TransportParameters"]]],["iroh_quinn_udp",[["impl StructuralPartialEq for EcnCodepoint"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[308,2193,15021,339]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/marker/trait.Sync.js b/pr/395/docs/trait.impl/core/marker/trait.Sync.js new file mode 100644 index 000000000..774bebcb5 --- /dev/null +++ b/pr/395/docs/trait.impl/core/marker/trait.Sync.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Sync for CipherSuite",1,["bench::CipherSuite"]],["impl Sync for Stats",1,["bench::stats::Stats"]],["impl Sync for StreamStats",1,["bench::stats::StreamStats"]],["impl Sync for TransferResult",1,["bench::stats::TransferResult"]],["impl Sync for Opt",1,["bench::Opt"]]]],["bulk",[["impl Sync for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl Sync for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl Sync for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl Sync for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl Sync for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl Sync for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl Sync for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl Sync for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl Sync for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl Sync for AddressDiscovery",1,["iroh_quinn::path::AddressDiscovery"]],["impl Sync for ClosePath",1,["iroh_quinn::path::ClosePath"]],["impl Sync for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl Sync for Connection",1,["iroh_quinn::connection::Connection"]],["impl Sync for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl Sync for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl Sync for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl Sync for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl Sync for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl Sync for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl Sync for Path",1,["iroh_quinn::path::Path"]],["impl Sync for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl Sync for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl Sync for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl Sync for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl Sync for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl Sync for UnorderedRecvStream",1,["iroh_quinn::recv_stream::UnorderedRecvStream"]],["impl Sync for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl Sync for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl<'a> Sync for Accept<'a>",1,["iroh_quinn::endpoint::Accept"]],["impl<'a> Sync for AcceptBi<'a>",1,["iroh_quinn::connection::AcceptBi"]],["impl<'a> Sync for AcceptUni<'a>",1,["iroh_quinn::connection::AcceptUni"]],["impl<'a> Sync for OpenBi<'a>",1,["iroh_quinn::connection::OpenBi"]],["impl<'a> Sync for OpenUni<'a>",1,["iroh_quinn::connection::OpenUni"]],["impl<'a> Sync for ReadDatagram<'a>",1,["iroh_quinn::connection::ReadDatagram"]],["impl<'a> Sync for SendDatagram<'a>",1,["iroh_quinn::connection::SendDatagram"]]]],["iroh_quinn_proto",[["impl Sync for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl Sync for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl Sync for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl Sync for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl Sync for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl Sync for Dir",1,["iroh_quinn_proto::Dir"]],["impl Sync for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl Sync for Event",1,["iroh_quinn_proto::connection::Event"]],["impl Sync for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl Sync for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl Sync for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl Sync for MaybeFrame",1,["iroh_quinn_proto::frame::MaybeFrame"]],["impl Sync for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl Sync for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl Sync for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl Sync for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl Sync for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl Sync for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl Sync for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl Sync for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl Sync for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl Sync for Side",1,["iroh_quinn_proto::Side"]],["impl Sync for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl Sync for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl Sync for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl Sync for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl Sync for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl Sync for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl Sync for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl Sync for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl Sync for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl Sync for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl Sync for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl Sync for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl Sync for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl Sync for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl Sync for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl Sync for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl Sync for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl Sync for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl Sync for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl Sync for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl Sync for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl Sync for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl Sync for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl Sync for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl Sync for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl Sync for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl Sync for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl Sync for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl Sync for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl Sync for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl Sync for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl Sync for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl Sync for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl Sync for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl Sync for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl Sync for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl Sync for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl Sync for DatagramInfo",1,["iroh_quinn_proto::frame::DatagramInfo"]],["impl Sync for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl Sync for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl Sync for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl Sync for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl Sync for FourTuple",1,["iroh_quinn_proto::FourTuple"]],["impl Sync for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl Sync for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl Sync for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl Sync for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl Sync for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl Sync for InvalidFrameId",1,["iroh_quinn_proto::frame::InvalidFrameId"]],["impl Sync for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl Sync for MultipathNotNegotiated",1,["iroh_quinn_proto::connection::MultipathNotNegotiated"]],["impl Sync for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl Sync for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl Sync for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl Sync for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl Sync for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl Sync for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl Sync for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl Sync for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl Sync for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl Sync for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl Sync for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl Sync for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl Sync for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl Sync for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl Sync for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl Sync for StreamInfo",1,["iroh_quinn_proto::frame::StreamInfo"]],["impl Sync for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl Sync for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl Sync for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl Sync for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl Sync for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl Sync for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl Sync for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl Sync for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl Sync for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl Sync for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl Sync for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl Sync for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> Sync for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> Sync for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> Sync for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<'a> Sync for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> Sync for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<T> Sync for KeyPair<T>
where\n T: Sync,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl Sync for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl Sync for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl Sync for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl<'a> Sync for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> Sync for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl Sync for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl Sync for Opt",1,["perf::client::Opt"]],["impl Sync for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl Sync for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl Sync for Opt",1,["perf::server::Opt"]],["impl Sync for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl Sync for Stats",1,["perf::stats::Stats"]],["impl Sync for StreamStats",1,["perf::stats::StreamStats"]],["impl Sync for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl Sync for Commands",1,["quinn_perf::Commands"]],["impl Sync for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1436,291,363,10317,36409,1598,2769,563]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/marker/trait.Unpin.js b/pr/395/docs/trait.impl/core/marker/trait.Unpin.js new file mode 100644 index 000000000..4133aabed --- /dev/null +++ b/pr/395/docs/trait.impl/core/marker/trait.Unpin.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Unpin for CipherSuite",1,["bench::CipherSuite"]],["impl Unpin for Stats",1,["bench::stats::Stats"]],["impl Unpin for StreamStats",1,["bench::stats::StreamStats"]],["impl Unpin for TransferResult",1,["bench::stats::TransferResult"]],["impl Unpin for Opt",1,["bench::Opt"]]]],["bulk",[["impl Unpin for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl Unpin for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl Unpin for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl Unpin for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl Unpin for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl Unpin for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl Unpin for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl Unpin for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl Unpin for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl Unpin for AddressDiscovery",1,["iroh_quinn::path::AddressDiscovery"]],["impl Unpin for ClosePath",1,["iroh_quinn::path::ClosePath"]],["impl Unpin for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl Unpin for Connection",1,["iroh_quinn::connection::Connection"]],["impl Unpin for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl Unpin for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl Unpin for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl Unpin for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl Unpin for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl Unpin for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl Unpin for Path",1,["iroh_quinn::path::Path"]],["impl Unpin for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl Unpin for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl Unpin for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl Unpin for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl Unpin for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl Unpin for UnorderedRecvStream",1,["iroh_quinn::recv_stream::UnorderedRecvStream"]],["impl Unpin for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl Unpin for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl<'__pin, 'a> Unpin for Accept<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"],["impl<'__pin, 'a> Unpin for AcceptBi<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"],["impl<'__pin, 'a> Unpin for AcceptUni<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"],["impl<'__pin, 'a> Unpin for OpenBi<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"],["impl<'__pin, 'a> Unpin for OpenUni<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"],["impl<'__pin, 'a> Unpin for ReadDatagram<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"],["impl<'__pin, 'a> Unpin for SendDatagram<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"]]],["iroh_quinn_proto",[["impl Unpin for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl Unpin for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl Unpin for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl Unpin for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl Unpin for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl Unpin for Dir",1,["iroh_quinn_proto::Dir"]],["impl Unpin for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl Unpin for Event",1,["iroh_quinn_proto::connection::Event"]],["impl Unpin for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl Unpin for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl Unpin for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl Unpin for MaybeFrame",1,["iroh_quinn_proto::frame::MaybeFrame"]],["impl Unpin for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl Unpin for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl Unpin for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl Unpin for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl Unpin for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl Unpin for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl Unpin for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl Unpin for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl Unpin for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl Unpin for Side",1,["iroh_quinn_proto::Side"]],["impl Unpin for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl Unpin for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl Unpin for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl Unpin for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl Unpin for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl Unpin for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl Unpin for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl Unpin for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl Unpin for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl Unpin for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl Unpin for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl Unpin for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl Unpin for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl Unpin for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl Unpin for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl Unpin for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl Unpin for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl Unpin for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl Unpin for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl Unpin for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl Unpin for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl Unpin for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl Unpin for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl Unpin for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl Unpin for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl Unpin for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl Unpin for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl Unpin for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl Unpin for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl Unpin for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl Unpin for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl Unpin for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl Unpin for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl Unpin for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl Unpin for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl Unpin for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl Unpin for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl Unpin for DatagramInfo",1,["iroh_quinn_proto::frame::DatagramInfo"]],["impl Unpin for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl Unpin for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl Unpin for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl Unpin for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl Unpin for FourTuple",1,["iroh_quinn_proto::FourTuple"]],["impl Unpin for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl Unpin for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl Unpin for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl Unpin for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl Unpin for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl Unpin for InvalidFrameId",1,["iroh_quinn_proto::frame::InvalidFrameId"]],["impl Unpin for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl Unpin for MultipathNotNegotiated",1,["iroh_quinn_proto::connection::MultipathNotNegotiated"]],["impl Unpin for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl Unpin for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl Unpin for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl Unpin for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl Unpin for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl Unpin for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl Unpin for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl Unpin for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl Unpin for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl Unpin for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl Unpin for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl Unpin for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl Unpin for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl Unpin for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl Unpin for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl Unpin for StreamInfo",1,["iroh_quinn_proto::frame::StreamInfo"]],["impl Unpin for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl Unpin for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl Unpin for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl Unpin for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl Unpin for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl Unpin for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl Unpin for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl Unpin for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl Unpin for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl Unpin for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl Unpin for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl Unpin for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> Unpin for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> Unpin for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> Unpin for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<'a> Unpin for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> Unpin for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<T> Unpin for KeyPair<T>
where\n T: Unpin,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl Unpin for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl Unpin for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl Unpin for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl<'a> Unpin for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> Unpin for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl Unpin for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl Unpin for Opt",1,["perf::client::Opt"]],["impl Unpin for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl Unpin for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl Unpin for Opt",1,["perf::server::Opt"]],["impl Unpin for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl Unpin for Stats",1,["perf::stats::Stats"]],["impl Unpin for StreamStats",1,["perf::stats::StreamStats"]],["impl Unpin for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl Unpin for Commands",1,["quinn_perf::Commands"]],["impl Unpin for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1451,294,366,11772,36733,1613,2796,569]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/marker/trait.UnsafeUnpin.js b/pr/395/docs/trait.impl/core/marker/trait.UnsafeUnpin.js new file mode 100644 index 000000000..070ffdd04 --- /dev/null +++ b/pr/395/docs/trait.impl/core/marker/trait.UnsafeUnpin.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl UnsafeUnpin for CipherSuite",1,["bench::CipherSuite"]],["impl UnsafeUnpin for Stats",1,["bench::stats::Stats"]],["impl UnsafeUnpin for StreamStats",1,["bench::stats::StreamStats"]],["impl UnsafeUnpin for TransferResult",1,["bench::stats::TransferResult"]],["impl UnsafeUnpin for Opt",1,["bench::Opt"]]]],["bulk",[["impl UnsafeUnpin for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl UnsafeUnpin for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl UnsafeUnpin for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl UnsafeUnpin for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl UnsafeUnpin for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl UnsafeUnpin for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl UnsafeUnpin for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl UnsafeUnpin for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl UnsafeUnpin for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl UnsafeUnpin for AddressDiscovery",1,["iroh_quinn::path::AddressDiscovery"]],["impl UnsafeUnpin for ClosePath",1,["iroh_quinn::path::ClosePath"]],["impl UnsafeUnpin for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl UnsafeUnpin for Connection",1,["iroh_quinn::connection::Connection"]],["impl UnsafeUnpin for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl UnsafeUnpin for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl UnsafeUnpin for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl UnsafeUnpin for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl UnsafeUnpin for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl UnsafeUnpin for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl UnsafeUnpin for Path",1,["iroh_quinn::path::Path"]],["impl UnsafeUnpin for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl UnsafeUnpin for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl UnsafeUnpin for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl UnsafeUnpin for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl UnsafeUnpin for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl UnsafeUnpin for UnorderedRecvStream",1,["iroh_quinn::recv_stream::UnorderedRecvStream"]],["impl UnsafeUnpin for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl UnsafeUnpin for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl<'a> !UnsafeUnpin for Accept<'a>",1,["iroh_quinn::endpoint::Accept"]],["impl<'a> !UnsafeUnpin for AcceptBi<'a>",1,["iroh_quinn::connection::AcceptBi"]],["impl<'a> !UnsafeUnpin for AcceptUni<'a>",1,["iroh_quinn::connection::AcceptUni"]],["impl<'a> !UnsafeUnpin for OpenBi<'a>",1,["iroh_quinn::connection::OpenBi"]],["impl<'a> !UnsafeUnpin for OpenUni<'a>",1,["iroh_quinn::connection::OpenUni"]],["impl<'a> !UnsafeUnpin for ReadDatagram<'a>",1,["iroh_quinn::connection::ReadDatagram"]],["impl<'a> !UnsafeUnpin for SendDatagram<'a>",1,["iroh_quinn::connection::SendDatagram"]]]],["iroh_quinn_proto",[["impl UnsafeUnpin for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl UnsafeUnpin for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl UnsafeUnpin for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl UnsafeUnpin for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl UnsafeUnpin for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl UnsafeUnpin for Dir",1,["iroh_quinn_proto::Dir"]],["impl UnsafeUnpin for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl UnsafeUnpin for Event",1,["iroh_quinn_proto::connection::Event"]],["impl UnsafeUnpin for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl UnsafeUnpin for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl UnsafeUnpin for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl UnsafeUnpin for MaybeFrame",1,["iroh_quinn_proto::frame::MaybeFrame"]],["impl UnsafeUnpin for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl UnsafeUnpin for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl UnsafeUnpin for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl UnsafeUnpin for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl UnsafeUnpin for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl UnsafeUnpin for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl UnsafeUnpin for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl UnsafeUnpin for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl UnsafeUnpin for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl UnsafeUnpin for Side",1,["iroh_quinn_proto::Side"]],["impl UnsafeUnpin for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl UnsafeUnpin for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl UnsafeUnpin for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl UnsafeUnpin for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl UnsafeUnpin for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl UnsafeUnpin for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl UnsafeUnpin for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl UnsafeUnpin for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl UnsafeUnpin for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl UnsafeUnpin for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl UnsafeUnpin for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl UnsafeUnpin for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl UnsafeUnpin for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl UnsafeUnpin for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl UnsafeUnpin for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl UnsafeUnpin for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl UnsafeUnpin for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl UnsafeUnpin for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl UnsafeUnpin for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl UnsafeUnpin for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl UnsafeUnpin for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl UnsafeUnpin for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl UnsafeUnpin for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl UnsafeUnpin for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl UnsafeUnpin for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl UnsafeUnpin for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl UnsafeUnpin for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl UnsafeUnpin for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl UnsafeUnpin for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl UnsafeUnpin for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl UnsafeUnpin for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl UnsafeUnpin for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl UnsafeUnpin for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl UnsafeUnpin for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl UnsafeUnpin for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl UnsafeUnpin for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl UnsafeUnpin for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl UnsafeUnpin for DatagramInfo",1,["iroh_quinn_proto::frame::DatagramInfo"]],["impl UnsafeUnpin for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl UnsafeUnpin for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl UnsafeUnpin for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl UnsafeUnpin for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl UnsafeUnpin for FourTuple",1,["iroh_quinn_proto::FourTuple"]],["impl UnsafeUnpin for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl UnsafeUnpin for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl UnsafeUnpin for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl UnsafeUnpin for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl UnsafeUnpin for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl UnsafeUnpin for InvalidFrameId",1,["iroh_quinn_proto::frame::InvalidFrameId"]],["impl UnsafeUnpin for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl UnsafeUnpin for MultipathNotNegotiated",1,["iroh_quinn_proto::connection::MultipathNotNegotiated"]],["impl UnsafeUnpin for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl UnsafeUnpin for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl UnsafeUnpin for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl UnsafeUnpin for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl UnsafeUnpin for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl UnsafeUnpin for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl UnsafeUnpin for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl UnsafeUnpin for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl UnsafeUnpin for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl UnsafeUnpin for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl UnsafeUnpin for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl UnsafeUnpin for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl UnsafeUnpin for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl UnsafeUnpin for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl UnsafeUnpin for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl UnsafeUnpin for StreamInfo",1,["iroh_quinn_proto::frame::StreamInfo"]],["impl UnsafeUnpin for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl UnsafeUnpin for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl UnsafeUnpin for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl UnsafeUnpin for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl UnsafeUnpin for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl UnsafeUnpin for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl UnsafeUnpin for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl UnsafeUnpin for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl UnsafeUnpin for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl UnsafeUnpin for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl UnsafeUnpin for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl UnsafeUnpin for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> UnsafeUnpin for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> UnsafeUnpin for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> UnsafeUnpin for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<'a> UnsafeUnpin for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> UnsafeUnpin for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<T> UnsafeUnpin for KeyPair<T>
where\n T: UnsafeUnpin,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl UnsafeUnpin for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl UnsafeUnpin for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl UnsafeUnpin for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl<'a> UnsafeUnpin for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> UnsafeUnpin for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl UnsafeUnpin for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl UnsafeUnpin for Opt",1,["perf::client::Opt"]],["impl UnsafeUnpin for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl UnsafeUnpin for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl UnsafeUnpin for Opt",1,["perf::server::Opt"]],["impl UnsafeUnpin for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl UnsafeUnpin for Stats",1,["perf::stats::Stats"]],["impl UnsafeUnpin for StreamStats",1,["perf::stats::StreamStats"]],["impl UnsafeUnpin for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl UnsafeUnpin for Commands",1,["quinn_perf::Commands"]],["impl UnsafeUnpin for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[826,312,384,6298,23233,988,1671,605]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/ops/bit/trait.Not.js b/pr/395/docs/trait.impl/core/ops/bit/trait.Not.js new file mode 100644 index 000000000..5b2b391db --- /dev/null +++ b/pr/395/docs/trait.impl/core/ops/bit/trait.Not.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl Not for Side"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[275]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/ops/deref/trait.Deref.js b/pr/395/docs/trait.impl/core/ops/deref/trait.Deref.js new file mode 100644 index 000000000..7b1e3385e --- /dev/null +++ b/pr/395/docs/trait.impl/core/ops/deref/trait.Deref.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl Deref for ConnectionId"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[315]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/ops/deref/trait.DerefMut.js b/pr/395/docs/trait.impl/core/ops/deref/trait.DerefMut.js new file mode 100644 index 000000000..0fbf8f6b5 --- /dev/null +++ b/pr/395/docs/trait.impl/core/ops/deref/trait.DerefMut.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl DerefMut for ConnectionId"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[324]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/ops/drop/trait.Drop.js b/pr/395/docs/trait.impl/core/ops/drop/trait.Drop.js new file mode 100644 index 000000000..4f190db7b --- /dev/null +++ b/pr/395/docs/trait.impl/core/ops/drop/trait.Drop.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl Drop for Incoming"],["impl Drop for OnClosed"],["impl Drop for RecvStream"],["impl Drop for SendStream"]]],["iroh_quinn_proto",[["impl Drop for Chunks<'_>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1084,303]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/ops/index/trait.Index.js b/pr/395/docs/trait.impl/core/ops/index/trait.Index.js new file mode 100644 index 000000000..dabc7ba10 --- /dev/null +++ b/pr/395/docs/trait.impl/core/ops/index/trait.Index.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl Index<ConnectionHandle> for Slab<ConnectionMeta>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[361]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/ops/index/trait.IndexMut.js b/pr/395/docs/trait.impl/core/ops/index/trait.IndexMut.js new file mode 100644 index 000000000..ebfe83045 --- /dev/null +++ b/pr/395/docs/trait.impl/core/ops/index/trait.IndexMut.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl IndexMut<ConnectionHandle> for Slab<ConnectionMeta>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[370]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js b/pr/395/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js new file mode 100644 index 000000000..705386914 --- /dev/null +++ b/pr/395/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl RefUnwindSafe for CipherSuite",1,["bench::CipherSuite"]],["impl RefUnwindSafe for Stats",1,["bench::stats::Stats"]],["impl RefUnwindSafe for StreamStats",1,["bench::stats::StreamStats"]],["impl RefUnwindSafe for TransferResult",1,["bench::stats::TransferResult"]],["impl RefUnwindSafe for Opt",1,["bench::Opt"]]]],["bulk",[["impl RefUnwindSafe for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl !RefUnwindSafe for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl !RefUnwindSafe for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl !RefUnwindSafe for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl !RefUnwindSafe for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl !RefUnwindSafe for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl !RefUnwindSafe for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl !RefUnwindSafe for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl !RefUnwindSafe for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl !RefUnwindSafe for AddressDiscovery",1,["iroh_quinn::path::AddressDiscovery"]],["impl !RefUnwindSafe for ClosePath",1,["iroh_quinn::path::ClosePath"]],["impl !RefUnwindSafe for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl !RefUnwindSafe for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl !RefUnwindSafe for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl !RefUnwindSafe for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl !RefUnwindSafe for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl !RefUnwindSafe for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl !RefUnwindSafe for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl !RefUnwindSafe for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl RefUnwindSafe for Connection",1,["iroh_quinn::connection::Connection"]],["impl RefUnwindSafe for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl RefUnwindSafe for Path",1,["iroh_quinn::path::Path"]],["impl RefUnwindSafe for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl RefUnwindSafe for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl RefUnwindSafe for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl RefUnwindSafe for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl RefUnwindSafe for UnorderedRecvStream",1,["iroh_quinn::recv_stream::UnorderedRecvStream"]],["impl RefUnwindSafe for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl<'a> !RefUnwindSafe for Accept<'a>",1,["iroh_quinn::endpoint::Accept"]],["impl<'a> !RefUnwindSafe for AcceptBi<'a>",1,["iroh_quinn::connection::AcceptBi"]],["impl<'a> !RefUnwindSafe for AcceptUni<'a>",1,["iroh_quinn::connection::AcceptUni"]],["impl<'a> !RefUnwindSafe for OpenBi<'a>",1,["iroh_quinn::connection::OpenBi"]],["impl<'a> !RefUnwindSafe for OpenUni<'a>",1,["iroh_quinn::connection::OpenUni"]],["impl<'a> !RefUnwindSafe for ReadDatagram<'a>",1,["iroh_quinn::connection::ReadDatagram"]],["impl<'a> !RefUnwindSafe for SendDatagram<'a>",1,["iroh_quinn::connection::SendDatagram"]]]],["iroh_quinn_proto",[["impl !RefUnwindSafe for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl !RefUnwindSafe for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl !RefUnwindSafe for Event",1,["iroh_quinn_proto::connection::Event"]],["impl !RefUnwindSafe for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl !RefUnwindSafe for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl !RefUnwindSafe for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl !RefUnwindSafe for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl !RefUnwindSafe for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl !RefUnwindSafe for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl !RefUnwindSafe for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl !RefUnwindSafe for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl !RefUnwindSafe for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl !RefUnwindSafe for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl !RefUnwindSafe for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl !RefUnwindSafe for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl !RefUnwindSafe for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl !RefUnwindSafe for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl !RefUnwindSafe for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl !RefUnwindSafe for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl RefUnwindSafe for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl RefUnwindSafe for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl RefUnwindSafe for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl RefUnwindSafe for Dir",1,["iroh_quinn_proto::Dir"]],["impl RefUnwindSafe for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl RefUnwindSafe for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl RefUnwindSafe for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl RefUnwindSafe for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl RefUnwindSafe for MaybeFrame",1,["iroh_quinn_proto::frame::MaybeFrame"]],["impl RefUnwindSafe for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl RefUnwindSafe for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl RefUnwindSafe for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl RefUnwindSafe for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl RefUnwindSafe for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl RefUnwindSafe for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl RefUnwindSafe for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl RefUnwindSafe for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl RefUnwindSafe for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl RefUnwindSafe for Side",1,["iroh_quinn_proto::Side"]],["impl RefUnwindSafe for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl RefUnwindSafe for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl RefUnwindSafe for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl RefUnwindSafe for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl RefUnwindSafe for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl RefUnwindSafe for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl RefUnwindSafe for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl RefUnwindSafe for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl RefUnwindSafe for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl RefUnwindSafe for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl RefUnwindSafe for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl RefUnwindSafe for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl RefUnwindSafe for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl RefUnwindSafe for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl RefUnwindSafe for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl RefUnwindSafe for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl RefUnwindSafe for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl RefUnwindSafe for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl RefUnwindSafe for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl RefUnwindSafe for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl RefUnwindSafe for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl RefUnwindSafe for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl RefUnwindSafe for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl RefUnwindSafe for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl RefUnwindSafe for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl RefUnwindSafe for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl RefUnwindSafe for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl RefUnwindSafe for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl RefUnwindSafe for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl RefUnwindSafe for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl RefUnwindSafe for DatagramInfo",1,["iroh_quinn_proto::frame::DatagramInfo"]],["impl RefUnwindSafe for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl RefUnwindSafe for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl RefUnwindSafe for FourTuple",1,["iroh_quinn_proto::FourTuple"]],["impl RefUnwindSafe for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl RefUnwindSafe for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl RefUnwindSafe for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl RefUnwindSafe for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl RefUnwindSafe for InvalidFrameId",1,["iroh_quinn_proto::frame::InvalidFrameId"]],["impl RefUnwindSafe for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl RefUnwindSafe for MultipathNotNegotiated",1,["iroh_quinn_proto::connection::MultipathNotNegotiated"]],["impl RefUnwindSafe for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl RefUnwindSafe for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl RefUnwindSafe for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl RefUnwindSafe for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl RefUnwindSafe for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl RefUnwindSafe for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl RefUnwindSafe for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl RefUnwindSafe for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl RefUnwindSafe for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl RefUnwindSafe for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl RefUnwindSafe for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl RefUnwindSafe for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl RefUnwindSafe for StreamInfo",1,["iroh_quinn_proto::frame::StreamInfo"]],["impl RefUnwindSafe for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl RefUnwindSafe for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl RefUnwindSafe for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl RefUnwindSafe for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl RefUnwindSafe for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl RefUnwindSafe for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl RefUnwindSafe for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl RefUnwindSafe for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl RefUnwindSafe for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> !RefUnwindSafe for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> !RefUnwindSafe for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> !RefUnwindSafe for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<'a> RefUnwindSafe for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> RefUnwindSafe for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<T> RefUnwindSafe for KeyPair<T>
where\n T: RefUnwindSafe,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl RefUnwindSafe for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl RefUnwindSafe for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl RefUnwindSafe for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl<'a> RefUnwindSafe for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> RefUnwindSafe for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl !RefUnwindSafe for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl !RefUnwindSafe for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl RefUnwindSafe for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl RefUnwindSafe for Opt",1,["perf::client::Opt"]],["impl RefUnwindSafe for Opt",1,["perf::server::Opt"]],["impl RefUnwindSafe for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl RefUnwindSafe for Stats",1,["perf::stats::Stats"]],["impl RefUnwindSafe for StreamStats",1,["perf::stats::StreamStats"]],["impl RefUnwindSafe for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl RefUnwindSafe for Commands",1,["quinn_perf::Commands"]],["impl RefUnwindSafe for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1686,341,414,11991,41831,1848,3221,663]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js b/pr/395/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js new file mode 100644 index 000000000..bbc497ab7 --- /dev/null +++ b/pr/395/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl UnwindSafe for CipherSuite",1,["bench::CipherSuite"]],["impl UnwindSafe for Stats",1,["bench::stats::Stats"]],["impl UnwindSafe for StreamStats",1,["bench::stats::StreamStats"]],["impl UnwindSafe for TransferResult",1,["bench::stats::TransferResult"]],["impl UnwindSafe for Opt",1,["bench::Opt"]]]],["bulk",[["impl UnwindSafe for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl !UnwindSafe for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl !UnwindSafe for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl !UnwindSafe for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl !UnwindSafe for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl !UnwindSafe for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl !UnwindSafe for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl !UnwindSafe for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl !UnwindSafe for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl !UnwindSafe for AddressDiscovery",1,["iroh_quinn::path::AddressDiscovery"]],["impl !UnwindSafe for ClosePath",1,["iroh_quinn::path::ClosePath"]],["impl !UnwindSafe for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl !UnwindSafe for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl !UnwindSafe for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl !UnwindSafe for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl !UnwindSafe for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl !UnwindSafe for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl !UnwindSafe for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl !UnwindSafe for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl UnwindSafe for Connection",1,["iroh_quinn::connection::Connection"]],["impl UnwindSafe for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl UnwindSafe for Path",1,["iroh_quinn::path::Path"]],["impl UnwindSafe for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl UnwindSafe for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl UnwindSafe for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl UnwindSafe for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl UnwindSafe for UnorderedRecvStream",1,["iroh_quinn::recv_stream::UnorderedRecvStream"]],["impl UnwindSafe for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl<'a> !UnwindSafe for Accept<'a>",1,["iroh_quinn::endpoint::Accept"]],["impl<'a> !UnwindSafe for AcceptBi<'a>",1,["iroh_quinn::connection::AcceptBi"]],["impl<'a> !UnwindSafe for AcceptUni<'a>",1,["iroh_quinn::connection::AcceptUni"]],["impl<'a> !UnwindSafe for OpenBi<'a>",1,["iroh_quinn::connection::OpenBi"]],["impl<'a> !UnwindSafe for OpenUni<'a>",1,["iroh_quinn::connection::OpenUni"]],["impl<'a> !UnwindSafe for ReadDatagram<'a>",1,["iroh_quinn::connection::ReadDatagram"]],["impl<'a> !UnwindSafe for SendDatagram<'a>",1,["iroh_quinn::connection::SendDatagram"]]]],["iroh_quinn_proto",[["impl !UnwindSafe for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl !UnwindSafe for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl !UnwindSafe for Event",1,["iroh_quinn_proto::connection::Event"]],["impl !UnwindSafe for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl !UnwindSafe for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl !UnwindSafe for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl !UnwindSafe for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl !UnwindSafe for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl !UnwindSafe for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl !UnwindSafe for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl !UnwindSafe for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl !UnwindSafe for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl !UnwindSafe for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl !UnwindSafe for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl !UnwindSafe for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl !UnwindSafe for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl !UnwindSafe for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl !UnwindSafe for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl !UnwindSafe for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl UnwindSafe for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl UnwindSafe for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl UnwindSafe for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl UnwindSafe for Dir",1,["iroh_quinn_proto::Dir"]],["impl UnwindSafe for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl UnwindSafe for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl UnwindSafe for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl UnwindSafe for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl UnwindSafe for MaybeFrame",1,["iroh_quinn_proto::frame::MaybeFrame"]],["impl UnwindSafe for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl UnwindSafe for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl UnwindSafe for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl UnwindSafe for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl UnwindSafe for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl UnwindSafe for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl UnwindSafe for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl UnwindSafe for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl UnwindSafe for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl UnwindSafe for Side",1,["iroh_quinn_proto::Side"]],["impl UnwindSafe for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl UnwindSafe for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl UnwindSafe for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl UnwindSafe for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl UnwindSafe for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl UnwindSafe for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl UnwindSafe for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl UnwindSafe for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl UnwindSafe for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl UnwindSafe for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl UnwindSafe for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl UnwindSafe for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl UnwindSafe for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl UnwindSafe for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl UnwindSafe for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl UnwindSafe for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl UnwindSafe for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl UnwindSafe for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl UnwindSafe for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl UnwindSafe for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl UnwindSafe for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl UnwindSafe for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl UnwindSafe for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl UnwindSafe for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl UnwindSafe for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl UnwindSafe for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl UnwindSafe for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl UnwindSafe for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl UnwindSafe for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl UnwindSafe for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl UnwindSafe for DatagramInfo",1,["iroh_quinn_proto::frame::DatagramInfo"]],["impl UnwindSafe for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl UnwindSafe for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl UnwindSafe for FourTuple",1,["iroh_quinn_proto::FourTuple"]],["impl UnwindSafe for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl UnwindSafe for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl UnwindSafe for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl UnwindSafe for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl UnwindSafe for InvalidFrameId",1,["iroh_quinn_proto::frame::InvalidFrameId"]],["impl UnwindSafe for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl UnwindSafe for MultipathNotNegotiated",1,["iroh_quinn_proto::connection::MultipathNotNegotiated"]],["impl UnwindSafe for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl UnwindSafe for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl UnwindSafe for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl UnwindSafe for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl UnwindSafe for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl UnwindSafe for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl UnwindSafe for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl UnwindSafe for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl UnwindSafe for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl UnwindSafe for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl UnwindSafe for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl UnwindSafe for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl UnwindSafe for StreamInfo",1,["iroh_quinn_proto::frame::StreamInfo"]],["impl UnwindSafe for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl UnwindSafe for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl UnwindSafe for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl UnwindSafe for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl UnwindSafe for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl UnwindSafe for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl UnwindSafe for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl UnwindSafe for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl UnwindSafe for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> !UnwindSafe for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> !UnwindSafe for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> !UnwindSafe for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<'a> !UnwindSafe for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> !UnwindSafe for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<T> UnwindSafe for KeyPair<T>
where\n T: UnwindSafe,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl UnwindSafe for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl UnwindSafe for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl UnwindSafe for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl<'a> UnwindSafe for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> UnwindSafe for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl !UnwindSafe for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl !UnwindSafe for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl UnwindSafe for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl UnwindSafe for Opt",1,["perf::client::Opt"]],["impl UnwindSafe for Opt",1,["perf::server::Opt"]],["impl UnwindSafe for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl UnwindSafe for Stats",1,["perf::stats::Stats"]],["impl UnwindSafe for StreamStats",1,["perf::stats::StreamStats"]],["impl UnwindSafe for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl UnwindSafe for Commands",1,["quinn_perf::Commands"]],["impl UnwindSafe for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1641,332,405,11694,40861,1803,3140,645]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/core/str/traits/trait.FromStr.js b/pr/395/docs/trait.impl/core/str/traits/trait.FromStr.js new file mode 100644 index 000000000..8d57e8916 --- /dev/null +++ b/pr/395/docs/trait.impl/core/str/traits/trait.FromStr.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl FromStr for CipherSuite"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[281]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/futures_core/stream/trait.Stream.js b/pr/395/docs/trait.impl/futures_core/stream/trait.Stream.js new file mode 100644 index 000000000..907146828 --- /dev/null +++ b/pr/395/docs/trait.impl/futures_core/stream/trait.Stream.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl Stream for AddressDiscovery"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[172]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js b/pr/395/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js new file mode 100644 index 000000000..f5d531735 --- /dev/null +++ b/pr/395/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl AsyncRead for RecvStream"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[157]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js b/pr/395/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js new file mode 100644 index 000000000..53ae8c728 --- /dev/null +++ b/pr/395/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl AsyncWrite for SendStream"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[158]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/identity_hash/trait.IdentityHashable.js b/pr/395/docs/trait.impl/identity_hash/trait.IdentityHashable.js new file mode 100644 index 000000000..dd10c652c --- /dev/null +++ b/pr/395/docs/trait.impl/identity_hash/trait.IdentityHashable.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl IdentityHashable for PathId"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[170]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn/runtime/trait.AsyncTimer.js b/pr/395/docs/trait.impl/iroh_quinn/runtime/trait.AsyncTimer.js new file mode 100644 index 000000000..4cabdc2d0 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn/runtime/trait.AsyncTimer.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn/runtime/trait.Runtime.js b/pr/395/docs/trait.impl/iroh_quinn/runtime/trait.Runtime.js new file mode 100644 index 000000000..4cabdc2d0 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn/runtime/trait.Runtime.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.js b/pr/395/docs/trait.impl/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/coding/trait.Decodable.js b/pr/395/docs/trait.impl/iroh_quinn_proto/coding/trait.Decodable.js new file mode 100644 index 000000000..ed3b1991d --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/coding/trait.Decodable.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[23]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/coding/trait.Encodable.js b/pr/395/docs/trait.impl/iroh_quinn_proto/coding/trait.Encodable.js new file mode 100644 index 000000000..ed3b1991d --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/coding/trait.Encodable.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[23]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/config/qlog/trait.QlogFactory.js b/pr/395/docs/trait.impl/iroh_quinn_proto/config/qlog/trait.QlogFactory.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/config/qlog/trait.QlogFactory.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/config/trait.TimeSource.js b/pr/395/docs/trait.impl/iroh_quinn_proto/config/trait.TimeSource.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/config/trait.TimeSource.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/congestion/trait.Controller.js b/pr/395/docs/trait.impl/iroh_quinn_proto/congestion/trait.Controller.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/congestion/trait.Controller.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/congestion/trait.ControllerFactory.js b/pr/395/docs/trait.impl/iroh_quinn_proto/congestion/trait.ControllerFactory.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/congestion/trait.ControllerFactory.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.AeadKey.js b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.AeadKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.AeadKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.ClientConfig.js b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.ClientConfig.js new file mode 100644 index 000000000..692621ae4 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.ClientConfig.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]],["perf",[["impl ClientConfig for NoProtectionClientConfig"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24,347]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.js b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.HeaderKey.js b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.HeaderKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.HeaderKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.HmacKey.js b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.HmacKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.HmacKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.PacketKey.js b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.PacketKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.PacketKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.ServerConfig.js b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.ServerConfig.js new file mode 100644 index 000000000..45d00a6be --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.ServerConfig.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]],["perf",[["impl ServerConfig for NoProtectionServerConfig"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24,347]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.Session.js b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.Session.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/crypto/trait.Session.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/packet/trait.ConnectionIdParser.js b/pr/395/docs/trait.impl/iroh_quinn_proto/packet/trait.ConnectionIdParser.js new file mode 100644 index 000000000..ed3b1991d --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/packet/trait.ConnectionIdParser.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[23]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/token/trait.TokenLog.js b/pr/395/docs/trait.impl/iroh_quinn_proto/token/trait.TokenLog.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/token/trait.TokenLog.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/iroh_quinn_proto/token/trait.TokenStore.js b/pr/395/docs/trait.impl/iroh_quinn_proto/token/trait.TokenStore.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/395/docs/trait.impl/iroh_quinn_proto/token/trait.TokenStore.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js b/pr/395/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js new file mode 100644 index 000000000..45f6b7887 --- /dev/null +++ b/pr/395/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["certificate",[["impl ServerCertVerifier for SkipServerVerification"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[205]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js b/pr/395/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js new file mode 100644 index 000000000..f5d531735 --- /dev/null +++ b/pr/395/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl AsyncRead for RecvStream"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[157]} \ No newline at end of file diff --git a/pr/395/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js b/pr/395/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js new file mode 100644 index 000000000..53ae8c728 --- /dev/null +++ b/pr/395/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl AsyncWrite for SendStream"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[158]} \ No newline at end of file diff --git a/pr/395/docs/type.impl/core/result/enum.Result.js b/pr/395/docs/type.impl/core/result/enum.Result.js new file mode 100644 index 000000000..ad2c8c137 --- /dev/null +++ b/pr/395/docs/type.impl/core/result/enum.Result.js @@ -0,0 +1,9 @@ +(function() { + var type_impls = Object.fromEntries([["iroh_quinn_proto",[["
§

impl<'a, T, E> Arbitrary<'a> for Result<T, E>
where\n T: Arbitrary<'a>,\n E: Arbitrary<'a>,

§

fn arbitrary(u: &mut Unstructured<'a>) -> Result<Result<T, E>, Error>

Generate an arbitrary value of Self from the given unstructured data. Read more
§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type\nneeds to construct itself. Read more
§

fn try_size_hint(\n depth: usize,\n) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type\nneeds to construct itself. Read more
§

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self, Error>

Generate an arbitrary value of Self from the entirety of the given\nunstructured data. Read more
","Arbitrary<'a>","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> Clone for Result<T, E>
where\n T: Clone,\n E: Clone,

Source§

fn clone(&self) -> Result<T, E>

Returns a duplicate of the value. Read more
Source§

fn clone_from(&mut self, source: &Result<T, E>)

Performs copy-assignment from source. Read more
","Clone","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> Debug for Result<T, E>
where\n T: Debug,\n E: Debug,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
","Debug","iroh_quinn_proto::coding::Result"],["
Source§

impl<'de, T, E> Deserialize<'de> for Result<T, E>
where\n T: Deserialize<'de>,\n E: Deserialize<'de>,

Source§

fn deserialize<D>(\n deserializer: D,\n) -> Result<Result<T, E>, <D as Deserializer<'de>>::Error>
where\n D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
","Deserialize<'de>","iroh_quinn_proto::coding::Result"],["
Source§

impl<'de, T, TAs, E, EAs> DeserializeAs<'de, Result<T, E>> for Result<TAs, EAs>
where\n TAs: DeserializeAs<'de, T>,\n EAs: DeserializeAs<'de, E>,

Source§

fn deserialize_as<D>(\n deserializer: D,\n) -> Result<Result<T, E>, <D as Deserializer<'de>>::Error>
where\n D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer.
","DeserializeAs<'de, Result>","iroh_quinn_proto::coding::Result"],["
Source§

impl<L, R> From<Either<L, R>> for Result<R, L>

Convert from Either to Result with Right => Ok and Left => Err.

\n
Source§

fn from(val: Either<L, R>) -> Result<R, L>

Converts to this type from the input type.
","From>","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<A, E, V> FromIterator<Result<A, E>> for Result<V, E>
where\n V: FromIterator<A>,

Source§

fn from_iter<I>(iter: I) -> Result<V, E>
where\n I: IntoIterator<Item = Result<A, E>>,

Takes 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.

\n

Here is an example which increments every integer in a vector,\nchecking for overflow:

\n\n
let 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]));
\n

Here is another example that tries to subtract one from another list\nof integers, this time checking for underflow:

\n\n
let 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!\"));
\n

Here is a variation on the previous example, showing that no\nfurther elements are taken from iter after the first Err.

\n\n
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);
\n

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.

\n
","FromIterator>","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E, F> FromResidual<Result<Infallible, E>> for Result<T, F>
where\n F: From<E>,

Source§

fn from_residual(residual: Result<Infallible, E>) -> Result<T, F>

🔬This is a nightly-only experimental API. (try_trait_v2)
Constructs the type from a compatible Residual type. Read more
","FromResidual>","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E, F> FromResidual<Yeet<E>> for Result<T, F>
where\n F: From<E>,

Source§

fn from_residual(_: Yeet<E>) -> Result<T, F>

🔬This is a nightly-only experimental API. (try_trait_v2)
Constructs the type from a compatible Residual type. Read more
","FromResidual>","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> Hash for Result<T, E>
where\n T: Hash,\n E: Hash,

Source§

fn hash<__H>(&self, state: &mut __H)
where\n __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where\n H: Hasher,\n Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
","Hash","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> IntoIterator for Result<T, E>

Source§

fn into_iter(self) -> IntoIter<T>

Returns a consuming iterator over the possibly contained value.

\n

The iterator yields one value if the result is Result::Ok, otherwise none.

\n
§Examples
\n
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, []);
Source§

type Item = T

The type of the elements being iterated over.
Source§

type IntoIter = IntoIter<T>

Which kind of iterator are we turning this into?
","IntoIterator","iroh_quinn_proto::coding::Result"],["
1.0.0 (const: unstable) · Source§

impl<T, E> Ord for Result<T, E>
where\n T: Ord,\n E: Ord,

Source§

fn cmp(&self, other: &Result<T, E>) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where\n Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where\n Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where\n Self: Sized,

Restrict a value to a certain interval. Read more
","Ord","iroh_quinn_proto::coding::Result"],["
1.0.0 (const: unstable) · Source§

impl<T, E> PartialEq for Result<T, E>
where\n T: PartialEq,\n E: PartialEq,

Source§

fn eq(&self, other: &Result<T, E>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient,\nand should not be overridden without very good reason.
","PartialEq","iroh_quinn_proto::coding::Result"],["
1.0.0 (const: unstable) · Source§

impl<T, E> PartialOrd for Result<T, E>
where\n T: PartialOrd,\n E: PartialOrd,

Source§

fn partial_cmp(&self, other: &Result<T, E>) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the\n<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the >\noperator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by\nthe >= operator. Read more
","PartialOrd","iroh_quinn_proto::coding::Result"],["
1.16.0 · Source§

impl<T, U, E> Product<Result<U, E>> for Result<T, E>
where\n T: Product<U>,

Source§

fn product<I>(iter: I) -> Result<T, E>
where\n I: Iterator<Item = Result<U, E>>,

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 product of all elements is returned.

\n
§Examples
\n

This multiplies each number in a vector of strings,\nif a string could not be parsed the operation returns Err:

\n\n
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());
","Product>","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Residual<T> for Result<Infallible, E>

Source§

type TryType = Result<T, E>

🔬This is a nightly-only experimental API. (try_trait_v2_residual)
The “return” type of this meta-function.
","Residual","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Result<&T, E>

1.59.0 (const: 1.83.0) · Source

pub const fn copied(self) -> Result<T, E>
where\n T: Copy,

Maps a Result<&T, E> to a Result<T, E> by copying the contents of the\nOk part.

\n
§Examples
\n
let val = 12;\nlet x: Result<&i32, i32> = Ok(&val);\nassert_eq!(x, Ok(&12));\nlet copied = x.copied();\nassert_eq!(copied, Ok(12));
1.59.0 · Source

pub fn cloned(self) -> Result<T, E>
where\n T: Clone,

Maps a Result<&T, E> to a Result<T, E> by cloning the contents of the\nOk part.

\n
§Examples
\n
let val = 12;\nlet x: Result<&i32, i32> = Ok(&val);\nassert_eq!(x, Ok(&12));\nlet cloned = x.cloned();\nassert_eq!(cloned, Ok(12));
",0,"iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Result<&mut T, E>

1.59.0 (const: 1.83.0) · Source

pub const fn copied(self) -> Result<T, E>
where\n T: Copy,

Maps a Result<&mut T, E> to a Result<T, E> by copying the contents of the\nOk part.

\n
§Examples
\n
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));
1.59.0 · Source

pub fn cloned(self) -> Result<T, E>
where\n T: Clone,

Maps a Result<&mut T, E> to a Result<T, E> by cloning the contents of the\nOk part.

\n
§Examples
\n
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));
",0,"iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Result<Option<T>, E>

1.33.0 (const: 1.83.0) · Source

pub const fn transpose(self) -> Option<Result<T, E>>

Transposes a Result of an Option into an Option of a Result.

\n

Ok(None) will be mapped to None.\nOk(Some(_)) and Err(_) will be mapped to Some(Ok(_)) and Some(Err(_)).

\n
§Examples
\n
#[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);
",0,"iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Result<Result<T, E>, E>

1.89.0 (const: 1.89.0) · Source

pub const fn flatten(self) -> Result<T, E>

Converts from Result<Result<T, E>, E> to Result<T, E>

\n
§Examples
\n
let 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());
\n

Flattening only removes one level of nesting at a time:

\n\n
let 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());
",0,"iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Result<T, E>

1.0.0 (const: 1.48.0) · Source

pub const fn is_ok(&self) -> bool

Returns true if the result is Ok.

\n
§Examples
\n
let x: Result<i32, &str> = Ok(-3);\nassert_eq!(x.is_ok(), true);\n\nlet x: Result<i32, &str> = Err(\"Some error message\");\nassert_eq!(x.is_ok(), false);
1.70.0 (const: unstable) · Source

pub fn is_ok_and<F>(self, f: F) -> bool
where\n F: FnOnce(T) -> bool,

Returns true if the result is Ok and the value inside of it matches a predicate.

\n
§Examples
\n
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);\n\nlet x: Result<String, &str> = Ok(\"ownership\".to_string());\nassert_eq!(x.as_ref().is_ok_and(|x| x.len() > 1), true);\nprintln!(\"still alive {:?}\", x);
1.0.0 (const: 1.48.0) · Source

pub const fn is_err(&self) -> bool

Returns true if the result is Err.

\n
§Examples
\n
let x: Result<i32, &str> = Ok(-3);\nassert_eq!(x.is_err(), false);\n\nlet x: Result<i32, &str> = Err(\"Some error message\");\nassert_eq!(x.is_err(), true);
1.70.0 (const: unstable) · Source

pub fn is_err_and<F>(self, f: F) -> bool
where\n F: FnOnce(E) -> bool,

Returns true if the result is Err and the value inside of it matches a predicate.

\n
§Examples
\n
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);\n\nlet x: Result<u32, String> = Err(\"ownership\".to_string());\nassert_eq!(x.as_ref().is_err_and(|x| x.len() > 1), true);\nprintln!(\"still alive {:?}\", x);
1.0.0 (const: unstable) · Source

pub fn ok(self) -> Option<T>

Converts from Result<T, E> to Option<T>.

\n

Converts self into an Option<T>, consuming self,\nand discarding the error, if any.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.ok(), Some(2));\n\nlet x: Result<u32, &str> = Err(\"Nothing here\");\nassert_eq!(x.ok(), None);
1.0.0 (const: unstable) · Source

pub fn err(self) -> Option<E>

Converts from Result<T, E> to Option<E>.

\n

Converts self into an Option<E>, consuming self,\nand discarding the success value, if any.

\n
§Examples
\n
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\"));
1.0.0 (const: 1.48.0) · Source

pub const fn as_ref(&self) -> Result<&T, &E>

Converts from &Result<T, E> to Result<&T, &E>.

\n

Produces a new Result, containing a reference\ninto the original, leaving the original in place.

\n
§Examples
\n
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\"));
1.0.0 (const: 1.83.0) · Source

pub const fn as_mut(&mut self) -> Result<&mut T, &mut E>

Converts from &mut Result<T, E> to Result<&mut T, &mut E>.

\n
§Examples
\n
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);
1.0.0 (const: unstable) · Source

pub fn map<U, F>(self, op: F) -> Result<U, E>
where\n F: FnOnce(T) -> U,

Maps a Result<T, E> to Result<U, E> by applying a function to a\ncontained Ok value, leaving an Err value untouched.

\n

This function can be used to compose the results of two functions.

\n
§Examples
\n

Print the numbers on each line of a string multiplied by two.

\n\n
let 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}
1.41.0 (const: unstable) · Source

pub fn map_or<U, F>(self, default: U, f: F) -> U
where\n F: FnOnce(T) -> U,

Returns the provided default (if Err), or\napplies a function to the contained value (if Ok).

\n

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.

\n
§Examples
\n
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);
1.41.0 (const: unstable) · Source

pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U
where\n D: FnOnce(E) -> U,\n F: FnOnce(T) -> U,

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.

\n

This function can be used to unpack a successful result\nwhile handling an error.

\n
§Examples
\n
let 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);
Source

pub const fn map_or_default<U, F>(self, f: F) -> U
where\n F: FnOnce(T) -> U,\n U: Default,

🔬This is a nightly-only experimental API. (result_option_map_or_default)

Maps a Result<T, E> to a U by applying function f to the contained\nvalue if the result is Ok, otherwise if Err, returns the\ndefault value for the type U.

\n
§Examples
\n
#![feature(result_option_map_or_default)]\n\nlet x: Result<_, &str> = Ok(\"foo\");\nlet y: Result<&str, _> = Err(\"bar\");\n\nassert_eq!(x.map_or_default(|x| x.len()), 3);\nassert_eq!(y.map_or_default(|y| y.len()), 0);
1.0.0 (const: unstable) · Source

pub fn map_err<F, O>(self, op: O) -> Result<T, F>
where\n O: FnOnce(E) -> F,

Maps a Result<T, E> to Result<T, F> by applying a function to a\ncontained Err value, leaving an Ok value untouched.

\n

This function can be used to pass through a successful result while handling\nan error.

\n
§Examples
\n
fn 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()));
1.76.0 (const: unstable) · Source

pub fn inspect<F>(self, f: F) -> Result<T, E>
where\n F: FnOnce(&T),

Calls a function with a reference to the contained value if Ok.

\n

Returns the original result.

\n
§Examples
\n
let x: u8 = \"4\"\n    .parse::<u8>()\n    .inspect(|x| println!(\"original: {x}\"))\n    .map(|x| x.pow(3))\n    .expect(\"failed to parse number\");
1.76.0 (const: unstable) · Source

pub fn inspect_err<F>(self, f: F) -> Result<T, E>
where\n F: FnOnce(&E),

Calls a function with a reference to the contained value if Err.

\n

Returns the original result.

\n
§Examples
\n
use std::{fs, io};\n\nfn read() -> io::Result<String> {\n    fs::read_to_string(\"address.txt\")\n        .inspect_err(|e| eprintln!(\"failed to read file: {e}\"))\n}
1.47.0 (const: unstable) · Source

pub fn as_deref(&self) -> Result<&<T as Deref>::Target, &E>
where\n T: Deref,

Converts from Result<T, E> (or &Result<T, E>) to Result<&<T as Deref>::Target, &E>.

\n

Coerces the Ok variant of the original Result via Deref\nand returns the new Result.

\n
§Examples
\n
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);
1.47.0 (const: unstable) · Source

pub fn as_deref_mut(&mut self) -> Result<&mut <T as Deref>::Target, &mut E>
where\n T: DerefMut,

Converts from Result<T, E> (or &mut Result<T, E>) to Result<&mut <T as DerefMut>::Target, &mut E>.

\n

Coerces the Ok variant of the original Result via DerefMut\nand returns the new Result.

\n
§Examples
\n
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);
1.0.0 (const: unstable) · Source

pub fn iter(&self) -> Iter<'_, T>

Returns an iterator over the possibly contained value.

\n

The iterator yields one value if the result is Result::Ok, otherwise none.

\n
§Examples
\n
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);
1.0.0 (const: unstable) · Source

pub fn iter_mut(&mut self) -> IterMut<'_, T>

Returns a mutable iterator over the possibly contained value.

\n

The iterator yields one value if the result is Result::Ok, otherwise none.

\n
§Examples
\n
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);
1.4.0 · Source

pub fn expect(self, msg: &str) -> T
where\n E: Debug,

Returns the contained Ok value, consuming the self value.

\n

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.

\n
§Panics
\n

Panics if the value is an Err, with a panic message including the\npassed message, and the content of the Err.

\n
§Examples
\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.expect(\"Testing expect\"); // panics with `Testing expect: emergency failure`
§Recommended Message Style
\n

We recommend that expect messages are used to describe the reason you\nexpect the Result should be Ok.

\n\n
let path = std::env::var(\"IMPORTANT_PATH\")\n    .expect(\"env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`\");
\n

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”.

\n

For 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.

\n
1.0.0 · Source

pub fn unwrap(self) -> T
where\n E: Debug,

Returns the contained Ok value, consuming the self value.

\n

Because this function may panic, its use is generally discouraged.\nPanics are meant for unrecoverable errors, and\nmay abort the entire program.

\n

Instead, prefer to use the ? (try) operator, or pattern matching\nto handle the Err case explicitly, or call unwrap_or,\nunwrap_or_else, or unwrap_or_default.

\n
§Panics
\n

Panics if the value is an Err, with a panic message provided by the\nErr’s value.

\n
§Examples
\n

Basic usage:

\n\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.unwrap(), 2);
\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.unwrap(); // panics with `emergency failure`
1.16.0 (const: unstable) · Source

pub fn unwrap_or_default(self) -> T
where\n T: Default,

Returns the contained Ok value or a default

\n

Consumes the self argument then, if Ok, returns the contained\nvalue, otherwise if Err, returns the default value for that\ntype.

\n
§Examples
\n

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.

\n\n
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);
1.17.0 · Source

pub fn expect_err(self, msg: &str) -> E
where\n T: Debug,

Returns the contained Err value, consuming the self value.

\n
§Panics
\n

Panics if the value is an Ok, with a panic message including the\npassed message, and the content of the Ok.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(10);\nx.expect_err(\"Testing expect_err\"); // panics with `Testing expect_err: 10`
1.0.0 · Source

pub fn unwrap_err(self) -> E
where\n T: Debug,

Returns the contained Err value, consuming the self value.

\n
§Panics
\n

Panics if the value is an Ok, with a custom panic message provided\nby the Ok’s value.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nx.unwrap_err(); // panics with `2`
\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(x.unwrap_err(), \"emergency failure\");
Source

pub const fn into_ok(self) -> T
where\n E: Into<!>,

🔬This is a nightly-only experimental API. (unwrap_infallible)

Returns the contained Ok value, but never panics.

\n

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.

\n
§Examples
\n
\nfn only_good_news() -> Result<String, !> {\n    Ok(\"this is fine\".into())\n}\n\nlet s: String = only_good_news().into_ok();\nprintln!(\"{s}\");
Source

pub const fn into_err(self) -> E
where\n T: Into<!>,

🔬This is a nightly-only experimental API. (unwrap_infallible)

Returns the contained Err value, but never panics.

\n

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.

\n
§Examples
\n
\nfn only_bad_news() -> Result<!, String> {\n    Err(\"Oops, it failed\".into())\n}\n\nlet error: String = only_bad_news().into_err();\nprintln!(\"{error}\");
1.0.0 (const: unstable) · Source

pub fn and<U>(self, res: Result<U, E>) -> Result<U, E>

Returns res if the result is Ok, otherwise returns the Err value of self.

\n

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.

\n
§Examples
\n
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\"));
1.0.0 (const: unstable) · Source

pub fn and_then<U, F>(self, op: F) -> Result<U, E>
where\n F: FnOnce(T) -> Result<U, E>,

Calls op if the result is Ok, otherwise returns the Err value of self.

\n

This function can be used for control flow based on Result values.

\n
§Examples
\n
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\"));
\n

Often used to chain fallible operations that may return Err.

\n\n
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);
1.0.0 (const: unstable) · Source

pub fn or<F>(self, res: Result<T, F>) -> Result<T, F>

Returns res if the result is Err, otherwise returns the Ok value of self.

\n

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.

\n
§Examples
\n
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));
1.0.0 (const: unstable) · Source

pub fn or_else<F, O>(self, op: O) -> Result<T, F>
where\n O: FnOnce(E) -> Result<T, F>,

Calls op if the result is Err, otherwise returns the Ok value of self.

\n

This function can be used for control flow based on result values.

\n
§Examples
\n
fn 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));
1.0.0 (const: unstable) · Source

pub fn unwrap_or(self, default: T) -> T

Returns the contained Ok value or a provided default.

\n

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.

\n
§Examples
\n
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);
1.0.0 (const: unstable) · Source

pub fn unwrap_or_else<F>(self, op: F) -> T
where\n F: FnOnce(E) -> T,

Returns the contained Ok value or computes it from a closure.

\n
§Examples
\n
fn count(x: &str) -> usize { x.len() }\n\nassert_eq!(Ok(2).unwrap_or_else(count), 2);\nassert_eq!(Err(\"foo\").unwrap_or_else(count), 3);
1.58.0 · Source

pub unsafe fn unwrap_unchecked(self) -> T

Returns the contained Ok value, consuming the self value,\nwithout checking that the value is not an Err.

\n
§Safety
\n

Calling this method on an Err is undefined behavior.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(unsafe { x.unwrap_unchecked() }, 2);
\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nunsafe { x.unwrap_unchecked() }; // Undefined behavior!
1.58.0 · Source

pub unsafe fn unwrap_err_unchecked(self) -> E

Returns the contained Err value, consuming the self value,\nwithout checking that the value is not an Ok.

\n
§Safety
\n

Calling this method on an Ok is undefined behavior.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nunsafe { x.unwrap_err_unchecked() }; // Undefined behavior!
\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(unsafe { x.unwrap_err_unchecked() }, \"emergency failure\");
",0,"iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Serialize for Result<T, E>
where\n T: Serialize,\n E: Serialize,

Source§

fn serialize<S>(\n &self,\n serializer: S,\n) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where\n S: Serializer,

Serialize this value into the given Serde serializer. Read more
","Serialize","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, TAs, E, EAs> SerializeAs<Result<T, E>> for Result<TAs, EAs>
where\n TAs: SerializeAs<T>,\n EAs: SerializeAs<E>,

Source§

fn serialize_as<S>(\n source: &Result<T, E>,\n serializer: S,\n) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where\n S: Serializer,

Serialize this value into the given Serde serializer.
","SerializeAs>","iroh_quinn_proto::coding::Result"],["
1.16.0 · Source§

impl<T, U, E> Sum<Result<U, E>> for Result<T, E>
where\n T: Sum<U>,

Source§

fn sum<I>(iter: I) -> Result<T, E>
where\n I: Iterator<Item = Result<U, E>>,

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.

\n
§Examples
\n

This sums up every integer in a vector, rejecting the sum if a negative\nelement is encountered:

\n\n
let 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\"));
","Sum>","iroh_quinn_proto::coding::Result"],["
1.61.0 · Source§

impl<T, E> Termination for Result<T, E>
where\n T: Termination,\n E: Debug,

Source§

fn report(self) -> ExitCode

Is called to get the representation of the value as status code.\nThis status code is returned to the operating system.
","Termination","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Try for Result<T, E>

Source§

type Output = T

🔬This is a nightly-only experimental API. (try_trait_v2)
The type of the value produced by ? when not short-circuiting.
Source§

type Residual = Result<Infallible, E>

🔬This is a nightly-only experimental API. (try_trait_v2)
The type of the value passed to FromResidual::from_residual\nas part of ? when short-circuiting. Read more
Source§

fn from_output(output: <Result<T, E> as Try>::Output) -> Result<T, E>

🔬This is a nightly-only experimental API. (try_trait_v2)
Constructs the type from its Output type. Read more
Source§

fn branch(\n self,\n) -> ControlFlow<<Result<T, E> as Try>::Residual, <Result<T, E> as Try>::Output>

🔬This is a nightly-only experimental API. (try_trait_v2)
Used in ? 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 more
","Try","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> CloneFromCell for Result<T, E>
where\n T: CloneFromCell,\n E: CloneFromCell,

","CloneFromCell","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> Copy for Result<T, E>
where\n T: Copy,\n E: Copy,

","Copy","iroh_quinn_proto::coding::Result"],["
1.0.0 (const: unstable) · Source§

impl<T, E> Eq for Result<T, E>
where\n T: Eq,\n E: Eq,

","Eq","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> StructuralPartialEq for Result<T, E>

","StructuralPartialEq","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> UseCloned for Result<T, E>
where\n T: UseCloned,\n E: UseCloned,

","UseCloned","iroh_quinn_proto::coding::Result"]]]]); + if (window.register_type_impls) { + window.register_type_impls(type_impls); + } else { + window.pending_type_impls = type_impls; + } +})() +//{"start":55,"fragment_lengths":[187391]} \ No newline at end of file