From bd00a1b1afc3f74ed7eb493544ebc40fdd7b407f Mon Sep 17 00:00:00 2001 From: Arqu Date: Thu, 8 Jan 2026 13:22:42 +0000 Subject: [PATCH] deploy: a9bd48d39ae61b2b0ddd84ecc2f4ccf81193969e --- pr/299/docs/.lock | 0 pr/299/docs/bench/all.html | 1 + pr/299/docs/bench/enum.CipherSuite.html | 35 + .../fn.configure_tracing_subscriber.html | 1 + pr/299/docs/bench/fn.connect_client.html | 6 + pr/299/docs/bench/fn.drain_stream.html | 4 + pr/299/docs/bench/fn.rt.html | 1 + pr/299/docs/bench/fn.send_data_on_stream.html | 4 + pr/299/docs/bench/fn.server_endpoint.html | 7 + pr/299/docs/bench/fn.transport_config.html | 1 + pr/299/docs/bench/index.html | 1 + pr/299/docs/bench/sidebar-items.js | 1 + .../docs/bench/stats/fn.throughput_bps.html | 1 + pr/299/docs/bench/stats/index.html | 1 + pr/299/docs/bench/stats/sidebar-items.js | 1 + pr/299/docs/bench/stats/struct.Stats.html | 29 + .../docs/bench/stats/struct.StreamStats.html | 27 + .../bench/stats/struct.TransferResult.html | 28 + pr/299/docs/bench/struct.Opt.html | 68 + pr/299/docs/bulk/all.html | 1 + pr/299/docs/bulk/fn.client.html | 5 + pr/299/docs/bulk/fn.handle_client_stream.html | 5 + pr/299/docs/bulk/fn.main.html | 1 + pr/299/docs/bulk/fn.server.html | 1 + pr/299/docs/bulk/index.html | 1 + pr/299/docs/bulk/sidebar-items.js | 1 + pr/299/docs/bulk/struct.ClientStats.html | 27 + pr/299/docs/certificate/all.html | 1 + .../docs/certificate/fn.configure_client.html | 1 + .../fn.generate_self_signed_cert.html | 1 + pr/299/docs/certificate/fn.main.html | 1 + .../certificate/fn.read_certs_from_file.html | 1 + pr/299/docs/certificate/index.html | 1 + pr/299/docs/certificate/sidebar-items.js | 1 + .../struct.SkipServerVerification.html | 44 + pr/299/docs/crates.js | 2 + pr/299/docs/data_transfer/all.html | 1 + pr/299/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/299/docs/data_transfer/index.html | 1 + pr/299/docs/data_transfer/sidebar-items.js | 1 + pr/299/docs/help.html | 1 + pr/299/docs/iroh_quinn/all.html | 1 + pr/299/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/299/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/299/docs/iroh_quinn/enum.ConfigError.html | 36 + pr/299/docs/iroh_quinn/enum.ConnectError.html | 51 + .../docs/iroh_quinn/enum.ConnectionError.html | 54 + pr/299/docs/iroh_quinn/enum.Dir.html | 54 + pr/299/docs/iroh_quinn/enum.EcnCodepoint.html | 41 + pr/299/docs/iroh_quinn/enum.FrameType.html | 79 + pr/299/docs/iroh_quinn/enum.ReadError.html | 45 + .../docs/iroh_quinn/enum.ReadExactError.html | 38 + .../docs/iroh_quinn/enum.ReadToEndError.html | 38 + pr/299/docs/iroh_quinn/enum.ResetError.html | 40 + .../iroh_quinn/enum.SendDatagramError.html | 44 + pr/299/docs/iroh_quinn/enum.Side.html | 55 + pr/299/docs/iroh_quinn/enum.StoppedError.html | 40 + pr/299/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/299/docs/iroh_quinn/index.html | 34 + .../docs/iroh_quinn/path/struct.OpenPath.html | 11 + pr/299/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/299/docs/iroh_quinn/sidebar-items.js | 1 + pr/299/docs/iroh_quinn/struct.Accept.html | 50 + pr/299/docs/iroh_quinn/struct.AcceptBi.html | 54 + pr/299/docs/iroh_quinn/struct.AcceptUni.html | 54 + .../iroh_quinn/struct.AckFrequencyConfig.html | 65 + .../iroh_quinn/struct.ApplicationClose.html | 38 + .../docs/iroh_quinn/struct.BloomTokenLog.html | 48 + pr/299/docs/iroh_quinn/struct.Chunk.html | 35 + .../docs/iroh_quinn/struct.ClientConfig.html | 50 + .../docs/iroh_quinn/struct.ClosedStream.html | 33 + pr/299/docs/iroh_quinn/struct.Connecting.html | 101 + pr/299/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/299/docs/iroh_quinn/struct.Endpoint.html | 103 + .../iroh_quinn/struct.EndpointConfig.html | 67 + .../docs/iroh_quinn/struct.EndpointStats.html | 36 + pr/299/docs/iroh_quinn/struct.FrameStats.html | 65 + .../docs/iroh_quinn/struct.IdleTimeout.html | 52 + pr/299/docs/iroh_quinn/struct.Incoming.html | 46 + .../iroh_quinn/struct.IncomingFuture.html | 53 + .../iroh_quinn/struct.MtuDiscoveryConfig.html | 88 + .../docs/iroh_quinn/struct.NoneTokenLog.html | 31 + .../iroh_quinn/struct.NoneTokenStore.html | 25 + pr/299/docs/iroh_quinn/struct.OnClosed.html | 50 + pr/299/docs/iroh_quinn/struct.OpenBi.html | 54 + pr/299/docs/iroh_quinn/struct.OpenPath.html | 57 + pr/299/docs/iroh_quinn/struct.OpenUni.html | 54 + pr/299/docs/iroh_quinn/struct.Path.html | 48 + pr/299/docs/iroh_quinn/struct.PathId.html | 53 + pr/299/docs/iroh_quinn/struct.PathStats.html | 54 + pr/299/docs/iroh_quinn/struct.QlogConfig.html | 34 + .../iroh_quinn/struct.QlogFileFactory.html | 39 + .../docs/iroh_quinn/struct.ReadDatagram.html | 54 + pr/299/docs/iroh_quinn/struct.RecvStream.html | 210 + pr/299/docs/iroh_quinn/struct.RetryError.html | 27 + .../docs/iroh_quinn/struct.SendDatagram.html | 54 + pr/299/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/299/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/299/docs/iroh_quinn/struct.Transmit.html | 37 + .../iroh_quinn/struct.TransportConfig.html | 252 + .../iroh_quinn/struct.TransportErrorCode.html | 57 + pr/299/docs/iroh_quinn/struct.UdpStats.html | 36 + .../struct.UnorderedRecvStream.html | 54 + .../struct.ValidationTokenConfig.html | 74 + pr/299/docs/iroh_quinn/struct.VarInt.html | 65 + .../struct.VarIntBoundsExceeded.html | 33 + .../struct.WeakConnectionHandle.html | 31 + pr/299/docs/iroh_quinn/struct.Written.html | 38 + .../iroh_quinn/struct.ZeroRttAccepted.html | 51 + pr/299/docs/iroh_quinn/trait.AsyncTimer.html | 11 + .../docs/iroh_quinn/trait.AsyncUdpSocket.html | 38 + .../trait.ConnectionIdGenerator.html | 21 + pr/299/docs/iroh_quinn/trait.QlogFactory.html | 23 + pr/299/docs/iroh_quinn/trait.Runtime.html | 19 + pr/299/docs/iroh_quinn/trait.TimeSource.html | 7 + pr/299/docs/iroh_quinn/trait.TokenLog.html | 45 + pr/299/docs/iroh_quinn/trait.TokenStore.html | 13 + pr/299/docs/iroh_quinn/trait.UdpSender.html | 30 + pr/299/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 + ...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/299/docs/iroh_quinn_proto/enum.Dir.html | 53 + .../iroh_quinn_proto/enum.EcnCodepoint.html | 41 + pr/299/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 + .../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/299/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/struct.ApplicationClose.html | 11 + .../frame/struct.ConnectionClose.html | 11 + .../frame/struct.Datagram.html | 11 + pr/299/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/299/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 | 47 + .../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 | 30 + .../iroh_quinn_proto/struct.Datagrams.html | 44 + .../iroh_quinn_proto/struct.Endpoint.html | 83 + .../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 | 37 + .../iroh_quinn_proto/struct.InvalidCid.html | 27 + .../struct.MtuDiscoveryConfig.html | 85 + .../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 | 54 + .../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 + .../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 | 36 + .../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/299/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/299/docs/iroh_quinn_udp/index.html | 22 + pr/299/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/299/docs/perf/all.html | 1 + pr/299/docs/perf/client/fn.run.html | 1 + pr/299/docs/perf/client/index.html | 1 + pr/299/docs/perf/client/sidebar-items.js | 1 + pr/299/docs/perf/client/struct.Opt.html | 43 + .../docs/perf/enum.CongestionAlgorithm.html | 30 + pr/299/docs/perf/fn.parse_byte_size.html | 1 + pr/299/docs/perf/index.html | 1 + pr/299/docs/perf/noprotection/index.html | 1 + .../docs/perf/noprotection/sidebar-items.js | 1 + .../struct.NoProtectionClientConfig.html | 29 + .../struct.NoProtectionServerConfig.html | 37 + pr/299/docs/perf/server/fn.run.html | 1 + pr/299/docs/perf/server/index.html | 1 + pr/299/docs/perf/server/sidebar-items.js | 1 + pr/299/docs/perf/server/struct.Opt.html | 42 + pr/299/docs/perf/sidebar-items.js | 1 + .../docs/perf/static.PERF_CIPHER_SUITES.html | 1 + pr/299/docs/perf/stats/index.html | 1 + pr/299/docs/perf/stats/sidebar-items.js | 1 + .../perf/stats/struct.OpenStreamStats.html | 35 + pr/299/docs/perf/stats/struct.Stats.html | 24 + .../docs/perf/stats/struct.StreamStats.html | 24 + pr/299/docs/perf/struct.CommonOpt.html | 84 + pr/299/docs/quinn_perf/all.html | 1 + pr/299/docs/quinn_perf/enum.Commands.html | 40 + pr/299/docs/quinn_perf/fn.main.html | 1 + pr/299/docs/quinn_perf/index.html | 1 + pr/299/docs/quinn_perf/sidebar-items.js | 1 + pr/299/docs/quinn_perf/struct.Cli.html | 45 + pr/299/docs/search.index/00c07f35c6ce.js | 1 + pr/299/docs/search.index/01806ef5b5d6.js | 1 + pr/299/docs/search.index/01d48ddd05cf.js | 1 + pr/299/docs/search.index/01f5bffda89c.js | 1 + pr/299/docs/search.index/02f80e846705.js | 1 + pr/299/docs/search.index/030aa7192f47.js | 1 + pr/299/docs/search.index/030abe455d20.js | 1 + pr/299/docs/search.index/035c9d3c036b.js | 1 + pr/299/docs/search.index/04219d6c5f1f.js | 1 + pr/299/docs/search.index/045e5265a54f.js | 1 + pr/299/docs/search.index/04786c4690da.js | 1 + pr/299/docs/search.index/054086f1f577.js | 1 + pr/299/docs/search.index/05f4c9420eb7.js | 1 + pr/299/docs/search.index/0615a2bf5050.js | 1 + pr/299/docs/search.index/07b12fe46ce8.js | 1 + pr/299/docs/search.index/07bca17b1c6a.js | 1 + pr/299/docs/search.index/081380447331.js | 1 + pr/299/docs/search.index/0933b5050388.js | 1 + pr/299/docs/search.index/096a32f3fb06.js | 1 + pr/299/docs/search.index/09c6ace039aa.js | 1 + pr/299/docs/search.index/0aebd591215a.js | 1 + pr/299/docs/search.index/0b9da5e00812.js | 1 + pr/299/docs/search.index/0d24876322d0.js | 1 + pr/299/docs/search.index/0d377ac14863.js | 1 + pr/299/docs/search.index/0d5508bf0207.js | 1 + pr/299/docs/search.index/0dd31cbab4cc.js | 1 + pr/299/docs/search.index/0e61017bc68b.js | 1 + pr/299/docs/search.index/0fa74a34d33c.js | 1 + pr/299/docs/search.index/103f1ba9c2d5.js | 1 + pr/299/docs/search.index/10e19c9358f8.js | 1 + pr/299/docs/search.index/1198e3f6eeb3.js | 1 + pr/299/docs/search.index/125b8dc7d658.js | 1 + pr/299/docs/search.index/1344897c571c.js | 1 + pr/299/docs/search.index/1499361f517d.js | 1 + pr/299/docs/search.index/1566e3c31611.js | 1 + pr/299/docs/search.index/16681f81c6a9.js | 1 + pr/299/docs/search.index/16f90e0495ca.js | 1 + pr/299/docs/search.index/17747f0de527.js | 1 + pr/299/docs/search.index/182d04459856.js | 1 + pr/299/docs/search.index/18e4b0fb83df.js | 1 + pr/299/docs/search.index/19c9a628ac91.js | 1 + pr/299/docs/search.index/19f55295ffb7.js | 1 + pr/299/docs/search.index/1a01681e7d77.js | 1 + pr/299/docs/search.index/1b6ac9e761aa.js | 1 + pr/299/docs/search.index/1b82b05116c9.js | 1 + pr/299/docs/search.index/1b97b26c3ac1.js | 1 + pr/299/docs/search.index/1ce4c2454b0a.js | 1 + pr/299/docs/search.index/1ea1f2d3747a.js | 1 + pr/299/docs/search.index/2098306f4d34.js | 1 + pr/299/docs/search.index/21a899f4f99b.js | 1 + pr/299/docs/search.index/25a54c1ab6e0.js | 1 + pr/299/docs/search.index/266311e2892f.js | 1 + pr/299/docs/search.index/266827f9a2f0.js | 1 + pr/299/docs/search.index/267d2567696a.js | 1 + pr/299/docs/search.index/27f8ec34bd1f.js | 1 + pr/299/docs/search.index/281cb83843d4.js | 1 + pr/299/docs/search.index/28827f9488f7.js | 1 + pr/299/docs/search.index/2adb3d0bf2d8.js | 1 + pr/299/docs/search.index/2b92810a6565.js | 1 + pr/299/docs/search.index/2bed9cec1d26.js | 1 + pr/299/docs/search.index/2e7d78ea0cfe.js | 1 + pr/299/docs/search.index/2e882e6bf78f.js | 1 + pr/299/docs/search.index/2f0bd76574d0.js | 1 + pr/299/docs/search.index/2f2a8a6c3151.js | 1 + pr/299/docs/search.index/30399b5c8304.js | 1 + pr/299/docs/search.index/30a16458d826.js | 1 + pr/299/docs/search.index/30e9b37f1a9e.js | 1 + pr/299/docs/search.index/311e77def7dc.js | 1 + pr/299/docs/search.index/3161d4c1c9e3.js | 1 + pr/299/docs/search.index/325f77b8029e.js | 1 + pr/299/docs/search.index/32c27beceb5f.js | 1 + pr/299/docs/search.index/3407cc8b99a3.js | 1 + pr/299/docs/search.index/34726ae306d9.js | 1 + pr/299/docs/search.index/34bb2b1a0ab9.js | 1 + pr/299/docs/search.index/35cd5d54f947.js | 1 + pr/299/docs/search.index/36de07894256.js | 1 + pr/299/docs/search.index/3748eeff0c66.js | 1 + pr/299/docs/search.index/378531389b4a.js | 1 + pr/299/docs/search.index/37cfa41c8509.js | 1 + pr/299/docs/search.index/3872754cddc0.js | 1 + pr/299/docs/search.index/38ae726b5e74.js | 1 + pr/299/docs/search.index/39c20340df78.js | 1 + pr/299/docs/search.index/3a74000719ff.js | 1 + pr/299/docs/search.index/3b786721c45c.js | 1 + pr/299/docs/search.index/3bd565fd40d0.js | 1 + pr/299/docs/search.index/3d41efe337a8.js | 1 + pr/299/docs/search.index/3d4983faf394.js | 1 + pr/299/docs/search.index/3eb044d68081.js | 1 + pr/299/docs/search.index/3f1d867bdbec.js | 1 + pr/299/docs/search.index/3f2790b84249.js | 1 + pr/299/docs/search.index/3f76acc9c4c0.js | 1 + pr/299/docs/search.index/3fe667d21e90.js | 1 + pr/299/docs/search.index/402127170d10.js | 1 + pr/299/docs/search.index/40672655bcf1.js | 1 + pr/299/docs/search.index/4083ed670548.js | 1 + pr/299/docs/search.index/40d41dda421f.js | 1 + pr/299/docs/search.index/40e5df003d92.js | 1 + pr/299/docs/search.index/40fc47699d53.js | 1 + pr/299/docs/search.index/4154473306f6.js | 1 + pr/299/docs/search.index/4365fc23c2e2.js | 1 + pr/299/docs/search.index/442799c14c6f.js | 1 + pr/299/docs/search.index/44a79dbac07c.js | 1 + pr/299/docs/search.index/45caa4397354.js | 1 + pr/299/docs/search.index/45e872f24307.js | 1 + pr/299/docs/search.index/45f4fbda75f7.js | 1 + pr/299/docs/search.index/47fa58ac1f7e.js | 1 + pr/299/docs/search.index/481e68f578df.js | 1 + pr/299/docs/search.index/4821942f57b5.js | 1 + pr/299/docs/search.index/48b9c2df65c3.js | 1 + pr/299/docs/search.index/49d47034aeca.js | 1 + pr/299/docs/search.index/49f51c99ba82.js | 1 + pr/299/docs/search.index/4a662a061b37.js | 1 + pr/299/docs/search.index/4a809c839d91.js | 1 + pr/299/docs/search.index/4ac32d6f5fe0.js | 1 + pr/299/docs/search.index/4b168eb35e34.js | 1 + pr/299/docs/search.index/4b2753b82fcd.js | 1 + pr/299/docs/search.index/4b3a70d5ace1.js | 1 + pr/299/docs/search.index/4bc2f7dbadf5.js | 1 + pr/299/docs/search.index/4bf63ac4398b.js | 1 + pr/299/docs/search.index/4c291219c272.js | 1 + pr/299/docs/search.index/4c96afa0c0be.js | 1 + pr/299/docs/search.index/4d63d12f11e5.js | 1 + pr/299/docs/search.index/4e0c6b87a738.js | 1 + pr/299/docs/search.index/4e720ea46bc3.js | 1 + pr/299/docs/search.index/4f554ad18979.js | 1 + pr/299/docs/search.index/5037f3c0b285.js | 1 + pr/299/docs/search.index/50c4c9dd70c1.js | 1 + pr/299/docs/search.index/512cfed374c5.js | 1 + pr/299/docs/search.index/5161b276f515.js | 1 + pr/299/docs/search.index/520fc5a0c8db.js | 1 + pr/299/docs/search.index/52a47b654b95.js | 1 + pr/299/docs/search.index/52a660a476b8.js | 1 + pr/299/docs/search.index/53206e8cf266.js | 1 + pr/299/docs/search.index/53c1218b1076.js | 1 + pr/299/docs/search.index/53d3e59dd54f.js | 1 + pr/299/docs/search.index/5452a8cd2f09.js | 1 + pr/299/docs/search.index/5456fb44b43c.js | 1 + pr/299/docs/search.index/54c99bfc02b4.js | 1 + pr/299/docs/search.index/550f9f8ba786.js | 1 + pr/299/docs/search.index/5535895d7e1a.js | 1 + pr/299/docs/search.index/55eb9c789f72.js | 1 + pr/299/docs/search.index/570e64122582.js | 1 + pr/299/docs/search.index/5726b451fa74.js | 1 + pr/299/docs/search.index/587c8d82fabf.js | 1 + pr/299/docs/search.index/59c419e706d0.js | 1 + pr/299/docs/search.index/5a5100ed4a84.js | 1 + pr/299/docs/search.index/5a77bebba663.js | 1 + pr/299/docs/search.index/5c2e611e8037.js | 1 + pr/299/docs/search.index/5cc356801927.js | 1 + pr/299/docs/search.index/5d03104f5b36.js | 1 + pr/299/docs/search.index/5dcae36ab7a9.js | 1 + pr/299/docs/search.index/5e27fec6e119.js | 1 + pr/299/docs/search.index/5e990d199296.js | 1 + pr/299/docs/search.index/5f3aaa0e45e4.js | 1 + pr/299/docs/search.index/5f5fc12a19de.js | 1 + pr/299/docs/search.index/602d50ba42fc.js | 1 + pr/299/docs/search.index/6043b7b1f86b.js | 1 + pr/299/docs/search.index/60bbd1af8668.js | 1 + pr/299/docs/search.index/6133a24a21bd.js | 1 + pr/299/docs/search.index/61b0c88b9449.js | 1 + pr/299/docs/search.index/61d6cf934531.js | 1 + pr/299/docs/search.index/61e64371195c.js | 1 + pr/299/docs/search.index/61e80bc02385.js | 1 + pr/299/docs/search.index/6373129d4310.js | 1 + pr/299/docs/search.index/6383b6d7c4f5.js | 1 + pr/299/docs/search.index/63a6d2637da3.js | 1 + pr/299/docs/search.index/64129e8e0e98.js | 1 + pr/299/docs/search.index/657321230ec5.js | 1 + pr/299/docs/search.index/659cd1b8f458.js | 1 + pr/299/docs/search.index/65b95eb2cbb1.js | 1 + pr/299/docs/search.index/66080551a4d4.js | 1 + pr/299/docs/search.index/660ac0c7b41d.js | 1 + pr/299/docs/search.index/663d3ccd45e8.js | 1 + pr/299/docs/search.index/672d94eeb5ad.js | 1 + pr/299/docs/search.index/679f4284e04c.js | 1 + pr/299/docs/search.index/67d96877f75a.js | 1 + pr/299/docs/search.index/67f7a312869a.js | 1 + pr/299/docs/search.index/681c1ab8da48.js | 1 + pr/299/docs/search.index/682a70b084b3.js | 1 + pr/299/docs/search.index/69247c51b33f.js | 1 + pr/299/docs/search.index/6978b4ea0875.js | 1 + pr/299/docs/search.index/6a74a6193ac1.js | 1 + pr/299/docs/search.index/6bc821d036fc.js | 1 + pr/299/docs/search.index/6bcf985f9cc0.js | 1 + pr/299/docs/search.index/6c19dc66c62e.js | 1 + pr/299/docs/search.index/6c5b31781505.js | 1 + pr/299/docs/search.index/6c87bbe255e2.js | 1 + pr/299/docs/search.index/6cc490e2f6c0.js | 1 + pr/299/docs/search.index/6e48f0d21453.js | 1 + pr/299/docs/search.index/70212e5b30bb.js | 1 + pr/299/docs/search.index/71646ec45998.js | 1 + pr/299/docs/search.index/71e93f057075.js | 1 + pr/299/docs/search.index/729a3ef57c7a.js | 1 + pr/299/docs/search.index/729ca138be4c.js | 1 + pr/299/docs/search.index/74c41d060fa6.js | 1 + pr/299/docs/search.index/7601f1395e7f.js | 1 + pr/299/docs/search.index/76f46ceef4bd.js | 1 + pr/299/docs/search.index/77281c5ced0c.js | 1 + pr/299/docs/search.index/77607e6e8760.js | 1 + pr/299/docs/search.index/789bd808f118.js | 1 + pr/299/docs/search.index/78e2e859c206.js | 1 + pr/299/docs/search.index/7a4fbb02a71b.js | 1 + pr/299/docs/search.index/7b1c227ec211.js | 1 + pr/299/docs/search.index/7b6c1a81028e.js | 1 + pr/299/docs/search.index/7bb9d7afc6de.js | 1 + pr/299/docs/search.index/7bc6215ea521.js | 1 + pr/299/docs/search.index/7c19fbc10374.js | 1 + pr/299/docs/search.index/7c3ea2f4edb6.js | 1 + pr/299/docs/search.index/7c6d85f865c7.js | 1 + pr/299/docs/search.index/7c7b9eca657f.js | 1 + pr/299/docs/search.index/7ca0ef420c00.js | 1 + pr/299/docs/search.index/7cada5462b17.js | 1 + pr/299/docs/search.index/7ce0db9b2df8.js | 1 + pr/299/docs/search.index/7da0e5fc56e2.js | 1 + pr/299/docs/search.index/7de8caeba04a.js | 1 + pr/299/docs/search.index/7fbcc8b0cc40.js | 1 + pr/299/docs/search.index/7fc1150b9f0e.js | 1 + .../docs/search.index/alias/a762150c532c.js | 1 + .../search.index/crateNames/8a381fcf2bd4.js | 1 + pr/299/docs/search.index/desc/ee2830ff879e.js | 1 + .../docs/search.index/entry/510fc07218fc.js | 1 + .../search.index/function/a8b301585994.js | 1 + .../generic_inverted_index/770510825e02.js | 1 + pr/299/docs/search.index/name/8263a00c4c8a.js | 1 + .../normalizedName/479d9533fdd1.js | 1 + pr/299/docs/search.index/path/19a883ba3413.js | 1 + pr/299/docs/search.index/root.js | 1 + pr/299/docs/search.index/type/a66ceff1e845.js | 1 + pr/299/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/299/docs/set_up_connection/fn.client.html | 1 + pr/299/docs/set_up_connection/fn.main.html | 1 + pr/299/docs/set_up_connection/fn.server.html | 1 + pr/299/docs/set_up_connection/index.html | 1 + .../docs/set_up_connection/sidebar-items.js | 1 + pr/299/docs/settings.html | 1 + pr/299/docs/src-files.js | 2 + pr/299/docs/src/bench/lib.rs.html | 256 + pr/299/docs/src/bench/stats.rs.html | 95 + pr/299/docs/src/bulk/bulk.rs.html | 219 + .../docs/src/certificate/certificate.rs.html | 106 + .../src/data_transfer/data-transfer.rs.html | 56 + pr/299/docs/src/iroh_quinn/connection.rs.html | 1759 +++++ pr/299/docs/src/iroh_quinn/endpoint.rs.html | 934 +++ pr/299/docs/src/iroh_quinn/incoming.rs.html | 152 + pr/299/docs/src/iroh_quinn/lib.rs.html | 140 + pr/299/docs/src/iroh_quinn/mutex.rs.html | 163 + pr/299/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 | 443 ++ .../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 | 486 ++ .../docs/src/iroh_quinn_proto/coding.rs.html | 146 + .../src/iroh_quinn_proto/config/mod.rs.html | 703 ++ .../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 | 647 ++ .../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 | 215 + .../iroh_quinn_proto/connection/mod.rs.html | 6871 +++++++++++++++++ .../iroh_quinn_proto/connection/mtud.rs.html | 980 +++ .../connection/pacing.rs.html | 325 + .../connection/packet_builder.rs.html | 375 + .../connection/packet_crypto.rs.html | 197 + .../iroh_quinn_proto/connection/paths.rs.html | 978 +++ .../iroh_quinn_proto/connection/qlog.rs.html | 1075 +++ .../connection/send_buffer.rs.html | 752 ++ .../connection/spaces.rs.html | 1334 ++++ .../iroh_quinn_proto/connection/state.rs.html | 357 + .../iroh_quinn_proto/connection/stats.rs.html | 219 + .../connection/streams/mod.rs.html | 528 ++ .../connection/streams/recv.rs.html | 543 ++ .../connection/streams/send.rs.html | 402 + .../connection/streams/state.rs.html | 2099 +++++ .../iroh_quinn_proto/connection/timer.rs.html | 496 ++ .../connection/transmit_buf.rs.html | 244 + .../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 | 1376 ++++ .../docs/src/iroh_quinn_proto/frame.rs.html | 2291 ++++++ .../docs/src/iroh_quinn_proto/iroh_hp.rs.html | 513 ++ pr/299/docs/src/iroh_quinn_proto/lib.rs.html | 417 + .../docs/src/iroh_quinn_proto/packet.rs.html | 1053 +++ .../range_set/array_range_set.rs.html | 212 + .../iroh_quinn_proto/range_set/mod.rs.html | 5 + .../docs/src/iroh_quinn_proto/shared.rs.html | 199 + .../docs/src/iroh_quinn_proto/token.rs.html | 506 ++ .../token_memory_cache.rs.html | 247 + .../iroh_quinn_proto/transport_error.rs.html | 165 + .../transport_parameters.rs.html | 978 +++ .../docs/src/iroh_quinn_proto/varint.rs.html | 218 + .../docs/src/iroh_quinn_udp/cmsg/mod.rs.html | 157 + .../docs/src/iroh_quinn_udp/cmsg/unix.rs.html | 81 + pr/299/docs/src/iroh_quinn_udp/lib.rs.html | 240 + pr/299/docs/src/iroh_quinn_udp/unix.rs.html | 1101 +++ pr/299/docs/src/perf/client.rs.html | 419 + pr/299/docs/src/perf/lib.rs.html | 219 + pr/299/docs/src/perf/noprotection.rs.html | 205 + pr/299/docs/src/perf/server.rs.html | 202 + pr/299/docs/src/perf/stats.rs.html | 413 + pr/299/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/299/docs/static.files/favicon-044be391.svg | 24 + .../static.files/favicon-32x32-eab170b8.png | Bin 0 -> 690 bytes pr/299/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/299/docs/static.files/rustdoc-ca0dd0c4.css | 86 + .../static.files/scrape-examples-2bbcccac.js | 1 + pr/299/docs/static.files/search-8d3311b9.js | 5 + pr/299/docs/static.files/settings-c38705f0.js | 17 + .../docs/static.files/src-script-813739b1.js | 1 + pr/299/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/299/docs/trait.impl/core/cmp/trait.Eq.js | 9 + pr/299/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_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 + 894 files changed, 62030 insertions(+) create mode 100644 pr/299/docs/.lock create mode 100644 pr/299/docs/bench/all.html create mode 100644 pr/299/docs/bench/enum.CipherSuite.html create mode 100644 pr/299/docs/bench/fn.configure_tracing_subscriber.html create mode 100644 pr/299/docs/bench/fn.connect_client.html create mode 100644 pr/299/docs/bench/fn.drain_stream.html create mode 100644 pr/299/docs/bench/fn.rt.html create mode 100644 pr/299/docs/bench/fn.send_data_on_stream.html create mode 100644 pr/299/docs/bench/fn.server_endpoint.html create mode 100644 pr/299/docs/bench/fn.transport_config.html create mode 100644 pr/299/docs/bench/index.html create mode 100644 pr/299/docs/bench/sidebar-items.js create mode 100644 pr/299/docs/bench/stats/fn.throughput_bps.html create mode 100644 pr/299/docs/bench/stats/index.html create mode 100644 pr/299/docs/bench/stats/sidebar-items.js create mode 100644 pr/299/docs/bench/stats/struct.Stats.html create mode 100644 pr/299/docs/bench/stats/struct.StreamStats.html create mode 100644 pr/299/docs/bench/stats/struct.TransferResult.html create mode 100644 pr/299/docs/bench/struct.Opt.html create mode 100644 pr/299/docs/bulk/all.html create mode 100644 pr/299/docs/bulk/fn.client.html create mode 100644 pr/299/docs/bulk/fn.handle_client_stream.html create mode 100644 pr/299/docs/bulk/fn.main.html create mode 100644 pr/299/docs/bulk/fn.server.html create mode 100644 pr/299/docs/bulk/index.html create mode 100644 pr/299/docs/bulk/sidebar-items.js create mode 100644 pr/299/docs/bulk/struct.ClientStats.html create mode 100644 pr/299/docs/certificate/all.html create mode 100644 pr/299/docs/certificate/fn.configure_client.html create mode 100644 pr/299/docs/certificate/fn.generate_self_signed_cert.html create mode 100644 pr/299/docs/certificate/fn.main.html create mode 100644 pr/299/docs/certificate/fn.read_certs_from_file.html create mode 100644 pr/299/docs/certificate/index.html create mode 100644 pr/299/docs/certificate/sidebar-items.js create mode 100644 pr/299/docs/certificate/struct.SkipServerVerification.html create mode 100644 pr/299/docs/crates.js create mode 100644 pr/299/docs/data_transfer/all.html create mode 100644 pr/299/docs/data_transfer/fn.main.html create mode 100644 pr/299/docs/data_transfer/fn.open_bidirectional_stream.html create mode 100644 pr/299/docs/data_transfer/fn.open_unidirectional_stream.html create mode 100644 pr/299/docs/data_transfer/fn.receive_bidirectional_stream.html create mode 100644 pr/299/docs/data_transfer/fn.receive_datagram.html create mode 100644 pr/299/docs/data_transfer/fn.receive_unidirectional_stream.html create mode 100644 pr/299/docs/data_transfer/fn.send_unreliable.html create mode 100644 pr/299/docs/data_transfer/index.html create mode 100644 pr/299/docs/data_transfer/sidebar-items.js create mode 100644 pr/299/docs/help.html create mode 100644 pr/299/docs/iroh_quinn/all.html create mode 100644 pr/299/docs/iroh_quinn/congestion/index.html create mode 100644 pr/299/docs/iroh_quinn/congestion/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn/congestion/struct.Bbr.html create mode 100644 pr/299/docs/iroh_quinn/congestion/struct.BbrConfig.html create mode 100644 pr/299/docs/iroh_quinn/congestion/struct.ControllerMetrics.html create mode 100644 pr/299/docs/iroh_quinn/congestion/struct.Cubic.html create mode 100644 pr/299/docs/iroh_quinn/congestion/struct.CubicConfig.html create mode 100644 pr/299/docs/iroh_quinn/congestion/struct.NewReno.html create mode 100644 pr/299/docs/iroh_quinn/congestion/struct.NewRenoConfig.html create mode 100644 pr/299/docs/iroh_quinn/congestion/trait.Controller.html create mode 100644 pr/299/docs/iroh_quinn/congestion/trait.ControllerFactory.html create mode 100644 pr/299/docs/iroh_quinn/connection/enum.SendDatagramError.html create mode 100644 pr/299/docs/iroh_quinn/connection/struct.AcceptBi.html create mode 100644 pr/299/docs/iroh_quinn/connection/struct.AcceptUni.html create mode 100644 pr/299/docs/iroh_quinn/connection/struct.Connecting.html create mode 100644 pr/299/docs/iroh_quinn/connection/struct.Connection.html create mode 100644 pr/299/docs/iroh_quinn/connection/struct.OnClosed.html create mode 100644 pr/299/docs/iroh_quinn/connection/struct.OpenBi.html create mode 100644 pr/299/docs/iroh_quinn/connection/struct.OpenUni.html create mode 100644 pr/299/docs/iroh_quinn/connection/struct.ReadDatagram.html create mode 100644 pr/299/docs/iroh_quinn/connection/struct.SendDatagram.html create mode 100644 pr/299/docs/iroh_quinn/connection/struct.WeakConnectionHandle.html create mode 100644 pr/299/docs/iroh_quinn/connection/struct.ZeroRttAccepted.html create mode 100644 pr/299/docs/iroh_quinn/crypto/index.html create mode 100644 pr/299/docs/iroh_quinn/crypto/rustls/enum.Error.html create mode 100644 pr/299/docs/iroh_quinn/crypto/rustls/index.html create mode 100644 pr/299/docs/iroh_quinn/crypto/rustls/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn/crypto/rustls/struct.HandshakeData.html create mode 100644 pr/299/docs/iroh_quinn/crypto/rustls/struct.NoInitialCipherSuite.html create mode 100644 pr/299/docs/iroh_quinn/crypto/rustls/struct.QuicClientConfig.html create mode 100644 pr/299/docs/iroh_quinn/crypto/rustls/struct.QuicServerConfig.html create mode 100644 pr/299/docs/iroh_quinn/crypto/rustls/struct.TlsSession.html create mode 100644 pr/299/docs/iroh_quinn/crypto/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn/crypto/struct.CryptoError.html create mode 100644 pr/299/docs/iroh_quinn/crypto/struct.ExportKeyingMaterialError.html create mode 100644 pr/299/docs/iroh_quinn/crypto/struct.KeyPair.html create mode 100644 pr/299/docs/iroh_quinn/crypto/struct.Keys.html create mode 100644 pr/299/docs/iroh_quinn/crypto/struct.UnsupportedVersion.html create mode 100644 pr/299/docs/iroh_quinn/crypto/trait.AeadKey.html create mode 100644 pr/299/docs/iroh_quinn/crypto/trait.ClientConfig.html create mode 100644 pr/299/docs/iroh_quinn/crypto/trait.HandshakeTokenKey.html create mode 100644 pr/299/docs/iroh_quinn/crypto/trait.HeaderKey.html create mode 100644 pr/299/docs/iroh_quinn/crypto/trait.HmacKey.html create mode 100644 pr/299/docs/iroh_quinn/crypto/trait.PacketKey.html create mode 100644 pr/299/docs/iroh_quinn/crypto/trait.ServerConfig.html create mode 100644 pr/299/docs/iroh_quinn/crypto/trait.Session.html create mode 100644 pr/299/docs/iroh_quinn/endpoint/struct.Accept.html create mode 100644 pr/299/docs/iroh_quinn/endpoint/struct.Endpoint.html create mode 100644 pr/299/docs/iroh_quinn/endpoint/struct.EndpointStats.html create mode 100644 pr/299/docs/iroh_quinn/enum.ConfigError.html create mode 100644 pr/299/docs/iroh_quinn/enum.ConnectError.html create mode 100644 pr/299/docs/iroh_quinn/enum.ConnectionError.html create mode 100644 pr/299/docs/iroh_quinn/enum.Dir.html create mode 100644 pr/299/docs/iroh_quinn/enum.EcnCodepoint.html create mode 100644 pr/299/docs/iroh_quinn/enum.FrameType.html create mode 100644 pr/299/docs/iroh_quinn/enum.ReadError.html create mode 100644 pr/299/docs/iroh_quinn/enum.ReadExactError.html create mode 100644 pr/299/docs/iroh_quinn/enum.ReadToEndError.html create mode 100644 pr/299/docs/iroh_quinn/enum.ResetError.html create mode 100644 pr/299/docs/iroh_quinn/enum.SendDatagramError.html create mode 100644 pr/299/docs/iroh_quinn/enum.Side.html create mode 100644 pr/299/docs/iroh_quinn/enum.StoppedError.html create mode 100644 pr/299/docs/iroh_quinn/enum.WriteError.html create mode 100644 pr/299/docs/iroh_quinn/fn.default_runtime.html create mode 100644 pr/299/docs/iroh_quinn/incoming/struct.Incoming.html create mode 100644 pr/299/docs/iroh_quinn/incoming/struct.IncomingFuture.html create mode 100644 pr/299/docs/iroh_quinn/incoming/struct.RetryError.html create mode 100644 pr/299/docs/iroh_quinn/index.html create mode 100644 pr/299/docs/iroh_quinn/path/struct.OpenPath.html create mode 100644 pr/299/docs/iroh_quinn/path/struct.Path.html create mode 100644 pr/299/docs/iroh_quinn/recv_stream/enum.ReadError.html create mode 100644 pr/299/docs/iroh_quinn/recv_stream/enum.ReadExactError.html create mode 100644 pr/299/docs/iroh_quinn/recv_stream/enum.ReadToEndError.html create mode 100644 pr/299/docs/iroh_quinn/recv_stream/enum.ResetError.html create mode 100644 pr/299/docs/iroh_quinn/recv_stream/struct.RecvStream.html create mode 100644 pr/299/docs/iroh_quinn/recv_stream/struct.UnorderedRecvStream.html create mode 100644 pr/299/docs/iroh_quinn/runtime/fn.default_runtime.html create mode 100644 pr/299/docs/iroh_quinn/runtime/smol/struct.SmolRuntime.html create mode 100644 pr/299/docs/iroh_quinn/runtime/tokio/struct.TokioRuntime.html create mode 100644 pr/299/docs/iroh_quinn/runtime/trait.AsyncTimer.html create mode 100644 pr/299/docs/iroh_quinn/runtime/trait.AsyncUdpSocket.html create mode 100644 pr/299/docs/iroh_quinn/runtime/trait.Runtime.html create mode 100644 pr/299/docs/iroh_quinn/runtime/trait.UdpSender.html create mode 100644 pr/299/docs/iroh_quinn/send_stream/enum.StoppedError.html create mode 100644 pr/299/docs/iroh_quinn/send_stream/enum.WriteError.html create mode 100644 pr/299/docs/iroh_quinn/send_stream/struct.SendStream.html create mode 100644 pr/299/docs/iroh_quinn/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn/struct.Accept.html create mode 100644 pr/299/docs/iroh_quinn/struct.AcceptBi.html create mode 100644 pr/299/docs/iroh_quinn/struct.AcceptUni.html create mode 100644 pr/299/docs/iroh_quinn/struct.AckFrequencyConfig.html create mode 100644 pr/299/docs/iroh_quinn/struct.ApplicationClose.html create mode 100644 pr/299/docs/iroh_quinn/struct.BloomTokenLog.html create mode 100644 pr/299/docs/iroh_quinn/struct.Chunk.html create mode 100644 pr/299/docs/iroh_quinn/struct.ClientConfig.html create mode 100644 pr/299/docs/iroh_quinn/struct.ClosedStream.html create mode 100644 pr/299/docs/iroh_quinn/struct.Connecting.html create mode 100644 pr/299/docs/iroh_quinn/struct.Connection.html create mode 100644 pr/299/docs/iroh_quinn/struct.ConnectionClose.html create mode 100644 pr/299/docs/iroh_quinn/struct.ConnectionId.html create mode 100644 pr/299/docs/iroh_quinn/struct.ConnectionStats.html create mode 100644 pr/299/docs/iroh_quinn/struct.Endpoint.html create mode 100644 pr/299/docs/iroh_quinn/struct.EndpointConfig.html create mode 100644 pr/299/docs/iroh_quinn/struct.EndpointStats.html create mode 100644 pr/299/docs/iroh_quinn/struct.FrameStats.html create mode 100644 pr/299/docs/iroh_quinn/struct.IdleTimeout.html create mode 100644 pr/299/docs/iroh_quinn/struct.Incoming.html create mode 100644 pr/299/docs/iroh_quinn/struct.IncomingFuture.html create mode 100644 pr/299/docs/iroh_quinn/struct.MtuDiscoveryConfig.html create mode 100644 pr/299/docs/iroh_quinn/struct.NoneTokenLog.html create mode 100644 pr/299/docs/iroh_quinn/struct.NoneTokenStore.html create mode 100644 pr/299/docs/iroh_quinn/struct.OnClosed.html create mode 100644 pr/299/docs/iroh_quinn/struct.OpenBi.html create mode 100644 pr/299/docs/iroh_quinn/struct.OpenPath.html create mode 100644 pr/299/docs/iroh_quinn/struct.OpenUni.html create mode 100644 pr/299/docs/iroh_quinn/struct.Path.html create mode 100644 pr/299/docs/iroh_quinn/struct.PathId.html create mode 100644 pr/299/docs/iroh_quinn/struct.PathStats.html create mode 100644 pr/299/docs/iroh_quinn/struct.QlogConfig.html create mode 100644 pr/299/docs/iroh_quinn/struct.QlogFileFactory.html create mode 100644 pr/299/docs/iroh_quinn/struct.ReadDatagram.html create mode 100644 pr/299/docs/iroh_quinn/struct.RecvStream.html create mode 100644 pr/299/docs/iroh_quinn/struct.RetryError.html create mode 100644 pr/299/docs/iroh_quinn/struct.SendDatagram.html create mode 100644 pr/299/docs/iroh_quinn/struct.SendStream.html create mode 100644 pr/299/docs/iroh_quinn/struct.ServerConfig.html create mode 100644 pr/299/docs/iroh_quinn/struct.SmolRuntime.html create mode 100644 pr/299/docs/iroh_quinn/struct.StdSystemTime.html create mode 100644 pr/299/docs/iroh_quinn/struct.StreamId.html create mode 100644 pr/299/docs/iroh_quinn/struct.TokenMemoryCache.html create mode 100644 pr/299/docs/iroh_quinn/struct.TokenReuseError.html create mode 100644 pr/299/docs/iroh_quinn/struct.TokioRuntime.html create mode 100644 pr/299/docs/iroh_quinn/struct.Transmit.html create mode 100644 pr/299/docs/iroh_quinn/struct.TransportConfig.html create mode 100644 pr/299/docs/iroh_quinn/struct.TransportErrorCode.html create mode 100644 pr/299/docs/iroh_quinn/struct.UdpStats.html create mode 100644 pr/299/docs/iroh_quinn/struct.UnorderedRecvStream.html create mode 100644 pr/299/docs/iroh_quinn/struct.ValidationTokenConfig.html create mode 100644 pr/299/docs/iroh_quinn/struct.VarInt.html create mode 100644 pr/299/docs/iroh_quinn/struct.VarIntBoundsExceeded.html create mode 100644 pr/299/docs/iroh_quinn/struct.WeakConnectionHandle.html create mode 100644 pr/299/docs/iroh_quinn/struct.Written.html create mode 100644 pr/299/docs/iroh_quinn/struct.ZeroRttAccepted.html create mode 100644 pr/299/docs/iroh_quinn/trait.AsyncTimer.html create mode 100644 pr/299/docs/iroh_quinn/trait.AsyncUdpSocket.html create mode 100644 pr/299/docs/iroh_quinn/trait.ConnectionIdGenerator.html create mode 100644 pr/299/docs/iroh_quinn/trait.QlogFactory.html create mode 100644 pr/299/docs/iroh_quinn/trait.Runtime.html create mode 100644 pr/299/docs/iroh_quinn/trait.TimeSource.html create mode 100644 pr/299/docs/iroh_quinn/trait.TokenLog.html create mode 100644 pr/299/docs/iroh_quinn/trait.TokenStore.html create mode 100644 pr/299/docs/iroh_quinn/trait.UdpSender.html create mode 100644 pr/299/docs/iroh_quinn_proto/all.html create mode 100644 pr/299/docs/iroh_quinn_proto/bench_exports/index.html create mode 100644 pr/299/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html create mode 100644 pr/299/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html create mode 100644 pr/299/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/index.html create mode 100644 pr/299/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn_proto/bench_exports/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn_proto/bloom_token_log/struct.BloomTokenLog.html create mode 100644 pr/299/docs/iroh_quinn_proto/cid_generator/struct.HashedConnectionIdGenerator.html create mode 100644 pr/299/docs/iroh_quinn_proto/cid_generator/struct.InvalidCid.html create mode 100644 pr/299/docs/iroh_quinn_proto/cid_generator/struct.RandomConnectionIdGenerator.html create mode 100644 pr/299/docs/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.html create mode 100644 pr/299/docs/iroh_quinn_proto/coding/index.html create mode 100644 pr/299/docs/iroh_quinn_proto/coding/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html create mode 100644 pr/299/docs/iroh_quinn_proto/coding/trait.Decodable.html create mode 100644 pr/299/docs/iroh_quinn_proto/coding/trait.Encodable.html create mode 100644 pr/299/docs/iroh_quinn_proto/coding/type.Result.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/enum.ConfigError.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/qlog/struct.QlogConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/qlog/struct.QlogFileFactory.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/qlog/trait.QlogFactory.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/struct.ClientConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/struct.EndpointConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/struct.ServerConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/struct.StdSystemTime.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/struct.ValidationTokenConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/trait.TimeSource.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/transport/struct.AckFrequencyConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/transport/struct.IdleTimeout.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/transport/struct.MtuDiscoveryConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/config/transport/struct.TransportConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/bbr/struct.Bbr.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/bbr/struct.BbrConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/cubic/struct.Cubic.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/cubic/struct.CubicConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/index.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/new_reno/struct.NewReno.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/new_reno/struct.NewRenoConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/struct.Bbr.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/struct.BbrConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/struct.ControllerMetrics.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/struct.Cubic.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/struct.CubicConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/struct.NewReno.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/struct.NewRenoConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/trait.Controller.html create mode 100644 pr/299/docs/iroh_quinn_proto/congestion/trait.ControllerFactory.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/assembler/struct.Chunk.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/datagrams/enum.SendDatagramError.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/datagrams/struct.Datagrams.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/enum.ClosePathError.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/enum.ConnectionError.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/enum.Event.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/enum.PathError.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/paths/enum.PathEvent.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/paths/enum.PathStatus.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/paths/enum.SetPathStatusError.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/paths/struct.ClosedPath.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/paths/struct.PathId.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/paths/struct.RttEstimator.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/fn.get_into_many_segments.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/fn.get_loop_many_segments.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/index.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/stats/struct.ConnectionStats.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/stats/struct.FrameStats.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/stats/struct.PathStats.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/stats/struct.UdpStats.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/streams/enum.StreamEvent.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadError.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadableError.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/streams/recv/struct.Chunks.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/streams/send/enum.FinishError.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/streams/send/enum.WriteError.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/streams/send/struct.Written.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/streams/struct.ClosedStream.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/streams/struct.RecvStream.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/streams/struct.SendStream.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/streams/struct.ShouldTransmit.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/streams/struct.Streams.html create mode 100644 pr/299/docs/iroh_quinn_proto/connection/struct.Connection.html create mode 100644 pr/299/docs/iroh_quinn_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html create mode 100644 pr/299/docs/iroh_quinn_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/index.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/rustls/enum.Error.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/rustls/index.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/rustls/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/rustls/struct.HandshakeData.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/rustls/struct.NoInitialCipherSuite.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/rustls/struct.QuicClientConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/rustls/struct.QuicServerConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/rustls/struct.TlsSession.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/struct.CryptoError.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/struct.ExportKeyingMaterialError.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/struct.KeyPair.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/struct.Keys.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/struct.UnsupportedVersion.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/trait.AeadKey.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/trait.ClientConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/trait.HeaderKey.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/trait.HmacKey.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/trait.PacketKey.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/trait.ServerConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/crypto/trait.Session.html create mode 100644 pr/299/docs/iroh_quinn_proto/endpoint/enum.ConnectError.html create mode 100644 pr/299/docs/iroh_quinn_proto/endpoint/enum.DatagramEvent.html create mode 100644 pr/299/docs/iroh_quinn_proto/endpoint/struct.AcceptError.html create mode 100644 pr/299/docs/iroh_quinn_proto/endpoint/struct.ConnectionHandle.html create mode 100644 pr/299/docs/iroh_quinn_proto/endpoint/struct.Endpoint.html create mode 100644 pr/299/docs/iroh_quinn_proto/endpoint/struct.Incoming.html create mode 100644 pr/299/docs/iroh_quinn_proto/endpoint/struct.RetryError.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.ClosePathError.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.ConfigError.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.ConnectError.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.ConnectionError.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.DatagramEvent.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.Dir.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.EcnCodepoint.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.Event.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.FinishError.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.FrameType.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.LongType.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.PacketDecodeError.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.PathError.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.PathEvent.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.PathStatus.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.ProtectedHeader.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.ReadError.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.ReadableError.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.SendDatagramError.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.SetPathStatusError.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.Side.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.StreamEvent.html create mode 100644 pr/299/docs/iroh_quinn_proto/enum.WriteError.html create mode 100644 pr/299/docs/iroh_quinn_proto/frame/enum.FrameType.html create mode 100644 pr/299/docs/iroh_quinn_proto/frame/struct.ApplicationClose.html create mode 100644 pr/299/docs/iroh_quinn_proto/frame/struct.ConnectionClose.html create mode 100644 pr/299/docs/iroh_quinn_proto/frame/struct.Datagram.html create mode 100644 pr/299/docs/iroh_quinn_proto/index.html create mode 100644 pr/299/docs/iroh_quinn_proto/iroh_hp/enum.Error.html create mode 100644 pr/299/docs/iroh_quinn_proto/iroh_hp/enum.Event.html create mode 100644 pr/299/docs/iroh_quinn_proto/iroh_hp/index.html create mode 100644 pr/299/docs/iroh_quinn_proto/iroh_hp/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn_proto/packet/enum.LongType.html create mode 100644 pr/299/docs/iroh_quinn_proto/packet/enum.PacketDecodeError.html create mode 100644 pr/299/docs/iroh_quinn_proto/packet/enum.ProtectedHeader.html create mode 100644 pr/299/docs/iroh_quinn_proto/packet/struct.FixedLengthConnectionIdParser.html create mode 100644 pr/299/docs/iroh_quinn_proto/packet/struct.PartialDecode.html create mode 100644 pr/299/docs/iroh_quinn_proto/packet/struct.ProtectedInitialHeader.html create mode 100644 pr/299/docs/iroh_quinn_proto/packet/trait.ConnectionIdParser.html create mode 100644 pr/299/docs/iroh_quinn_proto/shared/enum.EcnCodepoint.html create mode 100644 pr/299/docs/iroh_quinn_proto/shared/struct.ConnectionEvent.html create mode 100644 pr/299/docs/iroh_quinn_proto/shared/struct.ConnectionId.html create mode 100644 pr/299/docs/iroh_quinn_proto/shared/struct.EndpointEvent.html create mode 100644 pr/299/docs/iroh_quinn_proto/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn_proto/struct.AcceptError.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.AckFrequencyConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.ApplicationClose.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.BloomTokenLog.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.Chunk.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.Chunks.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.ClientConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.ClosedPath.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.ClosedStream.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.Connection.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.ConnectionClose.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.ConnectionEvent.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.ConnectionHandle.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.ConnectionId.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.ConnectionStats.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.Datagram.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.Datagrams.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.Endpoint.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.EndpointConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.EndpointEvent.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.FourTuple.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.FrameStats.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.HashedConnectionIdGenerator.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.IdleTimeout.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.Incoming.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.InvalidCid.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.MtuDiscoveryConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.NoneTokenLog.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.NoneTokenStore.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.PartialDecode.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.PathId.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.PathStats.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.QlogConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.QlogFileFactory.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.RandomConnectionIdGenerator.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.RecvStream.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.RetryError.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.RttEstimator.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.SendStream.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.ServerConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.ShouldTransmit.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.StdSystemTime.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.StreamId.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.Streams.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.TokenMemoryCache.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.TokenReuseError.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.Transmit.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.TransportConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.TransportError.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.TransportErrorCode.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.UdpStats.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.ValidationTokenConfig.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.VarInt.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.VarIntBoundsExceeded.html create mode 100644 pr/299/docs/iroh_quinn_proto/struct.Written.html create mode 100644 pr/299/docs/iroh_quinn_proto/token/struct.NoneTokenLog.html create mode 100644 pr/299/docs/iroh_quinn_proto/token/struct.NoneTokenStore.html create mode 100644 pr/299/docs/iroh_quinn_proto/token/struct.TokenReuseError.html create mode 100644 pr/299/docs/iroh_quinn_proto/token/trait.TokenLog.html create mode 100644 pr/299/docs/iroh_quinn_proto/token/trait.TokenStore.html create mode 100644 pr/299/docs/iroh_quinn_proto/token_memory_cache/struct.TokenMemoryCache.html create mode 100644 pr/299/docs/iroh_quinn_proto/trait.ConnectionIdGenerator.html create mode 100644 pr/299/docs/iroh_quinn_proto/trait.ConnectionIdParser.html create mode 100644 pr/299/docs/iroh_quinn_proto/trait.QlogFactory.html create mode 100644 pr/299/docs/iroh_quinn_proto/trait.TimeSource.html create mode 100644 pr/299/docs/iroh_quinn_proto/trait.TokenLog.html create mode 100644 pr/299/docs/iroh_quinn_proto/trait.TokenStore.html create mode 100644 pr/299/docs/iroh_quinn_proto/transport_error/struct.Code.html create mode 100644 pr/299/docs/iroh_quinn_proto/transport_error/struct.Error.html create mode 100644 pr/299/docs/iroh_quinn_proto/transport_parameters/enum.Error.html create mode 100644 pr/299/docs/iroh_quinn_proto/transport_parameters/index.html create mode 100644 pr/299/docs/iroh_quinn_proto/transport_parameters/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn_proto/transport_parameters/struct.TransportParameters.html create mode 100644 pr/299/docs/iroh_quinn_proto/varint/struct.VarInt.html create mode 100644 pr/299/docs/iroh_quinn_proto/varint/struct.VarIntBoundsExceeded.html create mode 100644 pr/299/docs/iroh_quinn_udp/all.html create mode 100644 pr/299/docs/iroh_quinn_udp/constant.BATCH_SIZE.html create mode 100644 pr/299/docs/iroh_quinn_udp/enum.EcnCodepoint.html create mode 100644 pr/299/docs/iroh_quinn_udp/imp/struct.UdpSocketState.html create mode 100644 pr/299/docs/iroh_quinn_udp/index.html create mode 100644 pr/299/docs/iroh_quinn_udp/sidebar-items.js create mode 100644 pr/299/docs/iroh_quinn_udp/struct.RecvMeta.html create mode 100644 pr/299/docs/iroh_quinn_udp/struct.Transmit.html create mode 100644 pr/299/docs/iroh_quinn_udp/struct.UdpSockRef.html create mode 100644 pr/299/docs/iroh_quinn_udp/struct.UdpSocketState.html create mode 100644 pr/299/docs/perf/all.html create mode 100644 pr/299/docs/perf/client/fn.run.html create mode 100644 pr/299/docs/perf/client/index.html create mode 100644 pr/299/docs/perf/client/sidebar-items.js create mode 100644 pr/299/docs/perf/client/struct.Opt.html create mode 100644 pr/299/docs/perf/enum.CongestionAlgorithm.html create mode 100644 pr/299/docs/perf/fn.parse_byte_size.html create mode 100644 pr/299/docs/perf/index.html create mode 100644 pr/299/docs/perf/noprotection/index.html create mode 100644 pr/299/docs/perf/noprotection/sidebar-items.js create mode 100644 pr/299/docs/perf/noprotection/struct.NoProtectionClientConfig.html create mode 100644 pr/299/docs/perf/noprotection/struct.NoProtectionServerConfig.html create mode 100644 pr/299/docs/perf/server/fn.run.html create mode 100644 pr/299/docs/perf/server/index.html create mode 100644 pr/299/docs/perf/server/sidebar-items.js create mode 100644 pr/299/docs/perf/server/struct.Opt.html create mode 100644 pr/299/docs/perf/sidebar-items.js create mode 100644 pr/299/docs/perf/static.PERF_CIPHER_SUITES.html create mode 100644 pr/299/docs/perf/stats/index.html create mode 100644 pr/299/docs/perf/stats/sidebar-items.js create mode 100644 pr/299/docs/perf/stats/struct.OpenStreamStats.html create mode 100644 pr/299/docs/perf/stats/struct.Stats.html create mode 100644 pr/299/docs/perf/stats/struct.StreamStats.html create mode 100644 pr/299/docs/perf/struct.CommonOpt.html create mode 100644 pr/299/docs/quinn_perf/all.html create mode 100644 pr/299/docs/quinn_perf/enum.Commands.html create mode 100644 pr/299/docs/quinn_perf/fn.main.html create mode 100644 pr/299/docs/quinn_perf/index.html create mode 100644 pr/299/docs/quinn_perf/sidebar-items.js create mode 100644 pr/299/docs/quinn_perf/struct.Cli.html create mode 100644 pr/299/docs/search.index/00c07f35c6ce.js create mode 100644 pr/299/docs/search.index/01806ef5b5d6.js create mode 100644 pr/299/docs/search.index/01d48ddd05cf.js create mode 100644 pr/299/docs/search.index/01f5bffda89c.js create mode 100644 pr/299/docs/search.index/02f80e846705.js create mode 100644 pr/299/docs/search.index/030aa7192f47.js create mode 100644 pr/299/docs/search.index/030abe455d20.js create mode 100644 pr/299/docs/search.index/035c9d3c036b.js create mode 100644 pr/299/docs/search.index/04219d6c5f1f.js create mode 100644 pr/299/docs/search.index/045e5265a54f.js create mode 100644 pr/299/docs/search.index/04786c4690da.js create mode 100644 pr/299/docs/search.index/054086f1f577.js create mode 100644 pr/299/docs/search.index/05f4c9420eb7.js create mode 100644 pr/299/docs/search.index/0615a2bf5050.js create mode 100644 pr/299/docs/search.index/07b12fe46ce8.js create mode 100644 pr/299/docs/search.index/07bca17b1c6a.js create mode 100644 pr/299/docs/search.index/081380447331.js create mode 100644 pr/299/docs/search.index/0933b5050388.js create mode 100644 pr/299/docs/search.index/096a32f3fb06.js create mode 100644 pr/299/docs/search.index/09c6ace039aa.js create mode 100644 pr/299/docs/search.index/0aebd591215a.js create mode 100644 pr/299/docs/search.index/0b9da5e00812.js create mode 100644 pr/299/docs/search.index/0d24876322d0.js create mode 100644 pr/299/docs/search.index/0d377ac14863.js create mode 100644 pr/299/docs/search.index/0d5508bf0207.js create mode 100644 pr/299/docs/search.index/0dd31cbab4cc.js create mode 100644 pr/299/docs/search.index/0e61017bc68b.js create mode 100644 pr/299/docs/search.index/0fa74a34d33c.js create mode 100644 pr/299/docs/search.index/103f1ba9c2d5.js create mode 100644 pr/299/docs/search.index/10e19c9358f8.js create mode 100644 pr/299/docs/search.index/1198e3f6eeb3.js create mode 100644 pr/299/docs/search.index/125b8dc7d658.js create mode 100644 pr/299/docs/search.index/1344897c571c.js create mode 100644 pr/299/docs/search.index/1499361f517d.js create mode 100644 pr/299/docs/search.index/1566e3c31611.js create mode 100644 pr/299/docs/search.index/16681f81c6a9.js create mode 100644 pr/299/docs/search.index/16f90e0495ca.js create mode 100644 pr/299/docs/search.index/17747f0de527.js create mode 100644 pr/299/docs/search.index/182d04459856.js create mode 100644 pr/299/docs/search.index/18e4b0fb83df.js create mode 100644 pr/299/docs/search.index/19c9a628ac91.js create mode 100644 pr/299/docs/search.index/19f55295ffb7.js create mode 100644 pr/299/docs/search.index/1a01681e7d77.js create mode 100644 pr/299/docs/search.index/1b6ac9e761aa.js create mode 100644 pr/299/docs/search.index/1b82b05116c9.js create mode 100644 pr/299/docs/search.index/1b97b26c3ac1.js create mode 100644 pr/299/docs/search.index/1ce4c2454b0a.js create mode 100644 pr/299/docs/search.index/1ea1f2d3747a.js create mode 100644 pr/299/docs/search.index/2098306f4d34.js create mode 100644 pr/299/docs/search.index/21a899f4f99b.js create mode 100644 pr/299/docs/search.index/25a54c1ab6e0.js create mode 100644 pr/299/docs/search.index/266311e2892f.js create mode 100644 pr/299/docs/search.index/266827f9a2f0.js create mode 100644 pr/299/docs/search.index/267d2567696a.js create mode 100644 pr/299/docs/search.index/27f8ec34bd1f.js create mode 100644 pr/299/docs/search.index/281cb83843d4.js create mode 100644 pr/299/docs/search.index/28827f9488f7.js create mode 100644 pr/299/docs/search.index/2adb3d0bf2d8.js create mode 100644 pr/299/docs/search.index/2b92810a6565.js create mode 100644 pr/299/docs/search.index/2bed9cec1d26.js create mode 100644 pr/299/docs/search.index/2e7d78ea0cfe.js create mode 100644 pr/299/docs/search.index/2e882e6bf78f.js create mode 100644 pr/299/docs/search.index/2f0bd76574d0.js create mode 100644 pr/299/docs/search.index/2f2a8a6c3151.js create mode 100644 pr/299/docs/search.index/30399b5c8304.js create mode 100644 pr/299/docs/search.index/30a16458d826.js create mode 100644 pr/299/docs/search.index/30e9b37f1a9e.js create mode 100644 pr/299/docs/search.index/311e77def7dc.js create mode 100644 pr/299/docs/search.index/3161d4c1c9e3.js create mode 100644 pr/299/docs/search.index/325f77b8029e.js create mode 100644 pr/299/docs/search.index/32c27beceb5f.js create mode 100644 pr/299/docs/search.index/3407cc8b99a3.js create mode 100644 pr/299/docs/search.index/34726ae306d9.js create mode 100644 pr/299/docs/search.index/34bb2b1a0ab9.js create mode 100644 pr/299/docs/search.index/35cd5d54f947.js create mode 100644 pr/299/docs/search.index/36de07894256.js create mode 100644 pr/299/docs/search.index/3748eeff0c66.js create mode 100644 pr/299/docs/search.index/378531389b4a.js create mode 100644 pr/299/docs/search.index/37cfa41c8509.js create mode 100644 pr/299/docs/search.index/3872754cddc0.js create mode 100644 pr/299/docs/search.index/38ae726b5e74.js create mode 100644 pr/299/docs/search.index/39c20340df78.js create mode 100644 pr/299/docs/search.index/3a74000719ff.js create mode 100644 pr/299/docs/search.index/3b786721c45c.js create mode 100644 pr/299/docs/search.index/3bd565fd40d0.js create mode 100644 pr/299/docs/search.index/3d41efe337a8.js create mode 100644 pr/299/docs/search.index/3d4983faf394.js create mode 100644 pr/299/docs/search.index/3eb044d68081.js create mode 100644 pr/299/docs/search.index/3f1d867bdbec.js create mode 100644 pr/299/docs/search.index/3f2790b84249.js create mode 100644 pr/299/docs/search.index/3f76acc9c4c0.js create mode 100644 pr/299/docs/search.index/3fe667d21e90.js create mode 100644 pr/299/docs/search.index/402127170d10.js create mode 100644 pr/299/docs/search.index/40672655bcf1.js create mode 100644 pr/299/docs/search.index/4083ed670548.js create mode 100644 pr/299/docs/search.index/40d41dda421f.js create mode 100644 pr/299/docs/search.index/40e5df003d92.js create mode 100644 pr/299/docs/search.index/40fc47699d53.js create mode 100644 pr/299/docs/search.index/4154473306f6.js create mode 100644 pr/299/docs/search.index/4365fc23c2e2.js create mode 100644 pr/299/docs/search.index/442799c14c6f.js create mode 100644 pr/299/docs/search.index/44a79dbac07c.js create mode 100644 pr/299/docs/search.index/45caa4397354.js create mode 100644 pr/299/docs/search.index/45e872f24307.js create mode 100644 pr/299/docs/search.index/45f4fbda75f7.js create mode 100644 pr/299/docs/search.index/47fa58ac1f7e.js create mode 100644 pr/299/docs/search.index/481e68f578df.js create mode 100644 pr/299/docs/search.index/4821942f57b5.js create mode 100644 pr/299/docs/search.index/48b9c2df65c3.js create mode 100644 pr/299/docs/search.index/49d47034aeca.js create mode 100644 pr/299/docs/search.index/49f51c99ba82.js create mode 100644 pr/299/docs/search.index/4a662a061b37.js create mode 100644 pr/299/docs/search.index/4a809c839d91.js create mode 100644 pr/299/docs/search.index/4ac32d6f5fe0.js create mode 100644 pr/299/docs/search.index/4b168eb35e34.js create mode 100644 pr/299/docs/search.index/4b2753b82fcd.js create mode 100644 pr/299/docs/search.index/4b3a70d5ace1.js create mode 100644 pr/299/docs/search.index/4bc2f7dbadf5.js create mode 100644 pr/299/docs/search.index/4bf63ac4398b.js create mode 100644 pr/299/docs/search.index/4c291219c272.js create mode 100644 pr/299/docs/search.index/4c96afa0c0be.js create mode 100644 pr/299/docs/search.index/4d63d12f11e5.js create mode 100644 pr/299/docs/search.index/4e0c6b87a738.js create mode 100644 pr/299/docs/search.index/4e720ea46bc3.js create mode 100644 pr/299/docs/search.index/4f554ad18979.js create mode 100644 pr/299/docs/search.index/5037f3c0b285.js create mode 100644 pr/299/docs/search.index/50c4c9dd70c1.js create mode 100644 pr/299/docs/search.index/512cfed374c5.js create mode 100644 pr/299/docs/search.index/5161b276f515.js create mode 100644 pr/299/docs/search.index/520fc5a0c8db.js create mode 100644 pr/299/docs/search.index/52a47b654b95.js create mode 100644 pr/299/docs/search.index/52a660a476b8.js create mode 100644 pr/299/docs/search.index/53206e8cf266.js create mode 100644 pr/299/docs/search.index/53c1218b1076.js create mode 100644 pr/299/docs/search.index/53d3e59dd54f.js create mode 100644 pr/299/docs/search.index/5452a8cd2f09.js create mode 100644 pr/299/docs/search.index/5456fb44b43c.js create mode 100644 pr/299/docs/search.index/54c99bfc02b4.js create mode 100644 pr/299/docs/search.index/550f9f8ba786.js create mode 100644 pr/299/docs/search.index/5535895d7e1a.js create mode 100644 pr/299/docs/search.index/55eb9c789f72.js create mode 100644 pr/299/docs/search.index/570e64122582.js create mode 100644 pr/299/docs/search.index/5726b451fa74.js create mode 100644 pr/299/docs/search.index/587c8d82fabf.js create mode 100644 pr/299/docs/search.index/59c419e706d0.js create mode 100644 pr/299/docs/search.index/5a5100ed4a84.js create mode 100644 pr/299/docs/search.index/5a77bebba663.js create mode 100644 pr/299/docs/search.index/5c2e611e8037.js create mode 100644 pr/299/docs/search.index/5cc356801927.js create mode 100644 pr/299/docs/search.index/5d03104f5b36.js create mode 100644 pr/299/docs/search.index/5dcae36ab7a9.js create mode 100644 pr/299/docs/search.index/5e27fec6e119.js create mode 100644 pr/299/docs/search.index/5e990d199296.js create mode 100644 pr/299/docs/search.index/5f3aaa0e45e4.js create mode 100644 pr/299/docs/search.index/5f5fc12a19de.js create mode 100644 pr/299/docs/search.index/602d50ba42fc.js create mode 100644 pr/299/docs/search.index/6043b7b1f86b.js create mode 100644 pr/299/docs/search.index/60bbd1af8668.js create mode 100644 pr/299/docs/search.index/6133a24a21bd.js create mode 100644 pr/299/docs/search.index/61b0c88b9449.js create mode 100644 pr/299/docs/search.index/61d6cf934531.js create mode 100644 pr/299/docs/search.index/61e64371195c.js create mode 100644 pr/299/docs/search.index/61e80bc02385.js create mode 100644 pr/299/docs/search.index/6373129d4310.js create mode 100644 pr/299/docs/search.index/6383b6d7c4f5.js create mode 100644 pr/299/docs/search.index/63a6d2637da3.js create mode 100644 pr/299/docs/search.index/64129e8e0e98.js create mode 100644 pr/299/docs/search.index/657321230ec5.js create mode 100644 pr/299/docs/search.index/659cd1b8f458.js create mode 100644 pr/299/docs/search.index/65b95eb2cbb1.js create mode 100644 pr/299/docs/search.index/66080551a4d4.js create mode 100644 pr/299/docs/search.index/660ac0c7b41d.js create mode 100644 pr/299/docs/search.index/663d3ccd45e8.js create mode 100644 pr/299/docs/search.index/672d94eeb5ad.js create mode 100644 pr/299/docs/search.index/679f4284e04c.js create mode 100644 pr/299/docs/search.index/67d96877f75a.js create mode 100644 pr/299/docs/search.index/67f7a312869a.js create mode 100644 pr/299/docs/search.index/681c1ab8da48.js create mode 100644 pr/299/docs/search.index/682a70b084b3.js create mode 100644 pr/299/docs/search.index/69247c51b33f.js create mode 100644 pr/299/docs/search.index/6978b4ea0875.js create mode 100644 pr/299/docs/search.index/6a74a6193ac1.js create mode 100644 pr/299/docs/search.index/6bc821d036fc.js create mode 100644 pr/299/docs/search.index/6bcf985f9cc0.js create mode 100644 pr/299/docs/search.index/6c19dc66c62e.js create mode 100644 pr/299/docs/search.index/6c5b31781505.js create mode 100644 pr/299/docs/search.index/6c87bbe255e2.js create mode 100644 pr/299/docs/search.index/6cc490e2f6c0.js create mode 100644 pr/299/docs/search.index/6e48f0d21453.js create mode 100644 pr/299/docs/search.index/70212e5b30bb.js create mode 100644 pr/299/docs/search.index/71646ec45998.js create mode 100644 pr/299/docs/search.index/71e93f057075.js create mode 100644 pr/299/docs/search.index/729a3ef57c7a.js create mode 100644 pr/299/docs/search.index/729ca138be4c.js create mode 100644 pr/299/docs/search.index/74c41d060fa6.js create mode 100644 pr/299/docs/search.index/7601f1395e7f.js create mode 100644 pr/299/docs/search.index/76f46ceef4bd.js create mode 100644 pr/299/docs/search.index/77281c5ced0c.js create mode 100644 pr/299/docs/search.index/77607e6e8760.js create mode 100644 pr/299/docs/search.index/789bd808f118.js create mode 100644 pr/299/docs/search.index/78e2e859c206.js create mode 100644 pr/299/docs/search.index/7a4fbb02a71b.js create mode 100644 pr/299/docs/search.index/7b1c227ec211.js create mode 100644 pr/299/docs/search.index/7b6c1a81028e.js create mode 100644 pr/299/docs/search.index/7bb9d7afc6de.js create mode 100644 pr/299/docs/search.index/7bc6215ea521.js create mode 100644 pr/299/docs/search.index/7c19fbc10374.js create mode 100644 pr/299/docs/search.index/7c3ea2f4edb6.js create mode 100644 pr/299/docs/search.index/7c6d85f865c7.js create mode 100644 pr/299/docs/search.index/7c7b9eca657f.js create mode 100644 pr/299/docs/search.index/7ca0ef420c00.js create mode 100644 pr/299/docs/search.index/7cada5462b17.js create mode 100644 pr/299/docs/search.index/7ce0db9b2df8.js create mode 100644 pr/299/docs/search.index/7da0e5fc56e2.js create mode 100644 pr/299/docs/search.index/7de8caeba04a.js create mode 100644 pr/299/docs/search.index/7fbcc8b0cc40.js create mode 100644 pr/299/docs/search.index/7fc1150b9f0e.js create mode 100644 pr/299/docs/search.index/alias/a762150c532c.js create mode 100644 pr/299/docs/search.index/crateNames/8a381fcf2bd4.js create mode 100644 pr/299/docs/search.index/desc/ee2830ff879e.js create mode 100644 pr/299/docs/search.index/entry/510fc07218fc.js create mode 100644 pr/299/docs/search.index/function/a8b301585994.js create mode 100644 pr/299/docs/search.index/generic_inverted_index/770510825e02.js create mode 100644 pr/299/docs/search.index/name/8263a00c4c8a.js create mode 100644 pr/299/docs/search.index/normalizedName/479d9533fdd1.js create mode 100644 pr/299/docs/search.index/path/19a883ba3413.js create mode 100644 pr/299/docs/search.index/root.js create mode 100644 pr/299/docs/search.index/type/a66ceff1e845.js create mode 100644 pr/299/docs/set_up_connection/all.html create mode 100644 pr/299/docs/set_up_connection/constant.CLIENT_ADDR.html create mode 100644 pr/299/docs/set_up_connection/constant.LOCALHOST_V4.html create mode 100644 pr/299/docs/set_up_connection/constant.SERVER_ADDR.html create mode 100644 pr/299/docs/set_up_connection/constant.SERVER_NAME.html create mode 100644 pr/299/docs/set_up_connection/fn.client.html create mode 100644 pr/299/docs/set_up_connection/fn.main.html create mode 100644 pr/299/docs/set_up_connection/fn.server.html create mode 100644 pr/299/docs/set_up_connection/index.html create mode 100644 pr/299/docs/set_up_connection/sidebar-items.js create mode 100644 pr/299/docs/settings.html create mode 100644 pr/299/docs/src-files.js create mode 100644 pr/299/docs/src/bench/lib.rs.html create mode 100644 pr/299/docs/src/bench/stats.rs.html create mode 100644 pr/299/docs/src/bulk/bulk.rs.html create mode 100644 pr/299/docs/src/certificate/certificate.rs.html create mode 100644 pr/299/docs/src/data_transfer/data-transfer.rs.html create mode 100644 pr/299/docs/src/iroh_quinn/connection.rs.html create mode 100644 pr/299/docs/src/iroh_quinn/endpoint.rs.html create mode 100644 pr/299/docs/src/iroh_quinn/incoming.rs.html create mode 100644 pr/299/docs/src/iroh_quinn/lib.rs.html create mode 100644 pr/299/docs/src/iroh_quinn/mutex.rs.html create mode 100644 pr/299/docs/src/iroh_quinn/path.rs.html create mode 100644 pr/299/docs/src/iroh_quinn/recv_stream.rs.html create mode 100644 pr/299/docs/src/iroh_quinn/runtime/mod.rs.html create mode 100644 pr/299/docs/src/iroh_quinn/runtime/smol.rs.html create mode 100644 pr/299/docs/src/iroh_quinn/runtime/tokio.rs.html create mode 100644 pr/299/docs/src/iroh_quinn/send_stream.rs.html create mode 100644 pr/299/docs/src/iroh_quinn/work_limiter.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/address_discovery.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/bloom_token_log.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/cid_generator.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/cid_queue.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/coding.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/config/mod.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/config/qlog.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/config/transport.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/congestion.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/congestion/bbr/bw_estimation.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/congestion/bbr/min_max.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/congestion/bbr/mod.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/congestion/cubic.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/congestion/new_reno.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/ack_frequency.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/assembler.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/cid_state.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/datagrams.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/mod.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/mtud.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/pacing.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/packet_crypto.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/paths.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/qlog.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/send_buffer.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/spaces.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/state.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/stats.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/streams/recv.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/streams/send.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/streams/state.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/timer.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/connection/transmit_buf.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/constant_time.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/crypto.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/crypto/ring_like.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/crypto/rustls.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/endpoint.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/frame.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/iroh_hp.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/lib.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/packet.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/range_set/array_range_set.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/range_set/mod.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/shared.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/token.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/token_memory_cache.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/transport_error.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/transport_parameters.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_proto/varint.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_udp/cmsg/mod.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_udp/cmsg/unix.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_udp/lib.rs.html create mode 100644 pr/299/docs/src/iroh_quinn_udp/unix.rs.html create mode 100644 pr/299/docs/src/perf/client.rs.html create mode 100644 pr/299/docs/src/perf/lib.rs.html create mode 100644 pr/299/docs/src/perf/noprotection.rs.html create mode 100644 pr/299/docs/src/perf/server.rs.html create mode 100644 pr/299/docs/src/perf/stats.rs.html create mode 100644 pr/299/docs/src/quinn_perf/perf.rs.html create mode 100644 pr/299/docs/src/set_up_connection/set-up-connection.rs.html create mode 100644 pr/299/docs/static.files/COPYRIGHT-7fb11f4e.txt create mode 100644 pr/299/docs/static.files/FiraMono-Medium-86f75c8c.woff2 create mode 100644 pr/299/docs/static.files/FiraMono-Regular-87c26294.woff2 create mode 100644 pr/299/docs/static.files/FiraSans-Italic-81dc35de.woff2 create mode 100644 pr/299/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt create mode 100644 pr/299/docs/static.files/FiraSans-Medium-e1aa3f0a.woff2 create mode 100644 pr/299/docs/static.files/FiraSans-MediumItalic-ccf7e434.woff2 create mode 100644 pr/299/docs/static.files/FiraSans-Regular-0fe48ade.woff2 create mode 100644 pr/299/docs/static.files/LICENSE-APACHE-a60eea81.txt create mode 100644 pr/299/docs/static.files/LICENSE-MIT-23f18e03.txt create mode 100644 pr/299/docs/static.files/NanumBarunGothic-13b3dcba.ttf.woff2 create mode 100644 pr/299/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt create mode 100644 pr/299/docs/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 create mode 100644 pr/299/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt create mode 100644 pr/299/docs/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 create mode 100644 pr/299/docs/static.files/SourceCodePro-Semibold-aa29a496.ttf.woff2 create mode 100644 pr/299/docs/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 create mode 100644 pr/299/docs/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 create mode 100644 pr/299/docs/static.files/SourceSerif4-LICENSE-a2cfd9d5.md create mode 100644 pr/299/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 create mode 100644 pr/299/docs/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 create mode 100644 pr/299/docs/static.files/favicon-044be391.svg create mode 100644 pr/299/docs/static.files/favicon-32x32-eab170b8.png create mode 100644 pr/299/docs/static.files/main-ce535bd0.js create mode 100644 pr/299/docs/static.files/normalize-9960930a.css create mode 100644 pr/299/docs/static.files/noscript-263c88ec.css create mode 100644 pr/299/docs/static.files/rust-logo-9a9549ea.svg create mode 100644 pr/299/docs/static.files/rustdoc-ca0dd0c4.css create mode 100644 pr/299/docs/static.files/scrape-examples-2bbcccac.js create mode 100644 pr/299/docs/static.files/search-8d3311b9.js create mode 100644 pr/299/docs/static.files/settings-c38705f0.js create mode 100644 pr/299/docs/static.files/src-script-813739b1.js create mode 100644 pr/299/docs/static.files/storage-e2aeef58.js create mode 100644 pr/299/docs/static.files/stringdex-828709d0.js create mode 100644 pr/299/docs/trait.impl/arbitrary/trait.Arbitrary.js create mode 100644 pr/299/docs/trait.impl/clap_builder/derive/trait.Args.js create mode 100644 pr/299/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js create mode 100644 pr/299/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js create mode 100644 pr/299/docs/trait.impl/clap_builder/derive/trait.Parser.js create mode 100644 pr/299/docs/trait.impl/clap_builder/derive/trait.Subcommand.js create mode 100644 pr/299/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js create mode 100644 pr/299/docs/trait.impl/core/clone/trait.Clone.js create mode 100644 pr/299/docs/trait.impl/core/cmp/trait.Eq.js create mode 100644 pr/299/docs/trait.impl/core/cmp/trait.Ord.js create mode 100644 pr/299/docs/trait.impl/core/cmp/trait.PartialEq.js create mode 100644 pr/299/docs/trait.impl/core/cmp/trait.PartialOrd.js create mode 100644 pr/299/docs/trait.impl/core/convert/trait.From.js create mode 100644 pr/299/docs/trait.impl/core/convert/trait.TryFrom.js create mode 100644 pr/299/docs/trait.impl/core/default/trait.Default.js create mode 100644 pr/299/docs/trait.impl/core/error/trait.Error.js create mode 100644 pr/299/docs/trait.impl/core/fmt/trait.Debug.js create mode 100644 pr/299/docs/trait.impl/core/fmt/trait.Display.js create mode 100644 pr/299/docs/trait.impl/core/future/future/trait.Future.js create mode 100644 pr/299/docs/trait.impl/core/future/into_future/trait.IntoFuture.js create mode 100644 pr/299/docs/trait.impl/core/hash/trait.Hash.js create mode 100644 pr/299/docs/trait.impl/core/marker/trait.Copy.js create mode 100644 pr/299/docs/trait.impl/core/marker/trait.Freeze.js create mode 100644 pr/299/docs/trait.impl/core/marker/trait.Send.js create mode 100644 pr/299/docs/trait.impl/core/marker/trait.StructuralPartialEq.js create mode 100644 pr/299/docs/trait.impl/core/marker/trait.Sync.js create mode 100644 pr/299/docs/trait.impl/core/marker/trait.Unpin.js create mode 100644 pr/299/docs/trait.impl/core/marker/trait.UnsafeUnpin.js create mode 100644 pr/299/docs/trait.impl/core/ops/bit/trait.Not.js create mode 100644 pr/299/docs/trait.impl/core/ops/deref/trait.Deref.js create mode 100644 pr/299/docs/trait.impl/core/ops/deref/trait.DerefMut.js create mode 100644 pr/299/docs/trait.impl/core/ops/drop/trait.Drop.js create mode 100644 pr/299/docs/trait.impl/core/ops/index/trait.Index.js create mode 100644 pr/299/docs/trait.impl/core/ops/index/trait.IndexMut.js create mode 100644 pr/299/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js create mode 100644 pr/299/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js create mode 100644 pr/299/docs/trait.impl/core/str/traits/trait.FromStr.js create mode 100644 pr/299/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js create mode 100644 pr/299/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js create mode 100644 pr/299/docs/trait.impl/identity_hash/trait.IdentityHashable.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn/runtime/trait.AsyncTimer.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn/runtime/trait.Runtime.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/coding/trait.Decodable.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/coding/trait.Encodable.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/config/qlog/trait.QlogFactory.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/config/trait.TimeSource.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/congestion/trait.Controller.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/congestion/trait.ControllerFactory.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.AeadKey.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.ClientConfig.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.HeaderKey.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.HmacKey.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.PacketKey.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.ServerConfig.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.Session.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/packet/trait.ConnectionIdParser.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/token/trait.TokenLog.js create mode 100644 pr/299/docs/trait.impl/iroh_quinn_proto/token/trait.TokenStore.js create mode 100644 pr/299/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js create mode 100644 pr/299/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js create mode 100644 pr/299/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js create mode 100644 pr/299/docs/type.impl/core/result/enum.Result.js diff --git a/pr/299/docs/.lock b/pr/299/docs/.lock new file mode 100644 index 000000000..e69de29bb diff --git a/pr/299/docs/bench/all.html b/pr/299/docs/bench/all.html new file mode 100644 index 000000000..6778d9fe3 --- /dev/null +++ b/pr/299/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/299/docs/bench/enum.CipherSuite.html b/pr/299/docs/bench/enum.CipherSuite.html new file mode 100644 index 000000000..6f65dd21b --- /dev/null +++ b/pr/299/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/299/docs/bench/fn.configure_tracing_subscriber.html b/pr/299/docs/bench/fn.configure_tracing_subscriber.html new file mode 100644 index 000000000..93f1c308a --- /dev/null +++ b/pr/299/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/299/docs/bench/fn.connect_client.html b/pr/299/docs/bench/fn.connect_client.html new file mode 100644 index 000000000..1953d8b68 --- /dev/null +++ b/pr/299/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/299/docs/bench/fn.drain_stream.html b/pr/299/docs/bench/fn.drain_stream.html new file mode 100644 index 000000000..5cd0ee437 --- /dev/null +++ b/pr/299/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/299/docs/bench/fn.rt.html b/pr/299/docs/bench/fn.rt.html new file mode 100644 index 000000000..982c7e743 --- /dev/null +++ b/pr/299/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/299/docs/bench/fn.send_data_on_stream.html b/pr/299/docs/bench/fn.send_data_on_stream.html new file mode 100644 index 000000000..e61f81d0b --- /dev/null +++ b/pr/299/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/299/docs/bench/fn.server_endpoint.html b/pr/299/docs/bench/fn.server_endpoint.html new file mode 100644 index 000000000..605fd3cf2 --- /dev/null +++ b/pr/299/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/299/docs/bench/fn.transport_config.html b/pr/299/docs/bench/fn.transport_config.html new file mode 100644 index 000000000..411bd33d7 --- /dev/null +++ b/pr/299/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/299/docs/bench/index.html b/pr/299/docs/bench/index.html new file mode 100644 index 000000000..4e57465a1 --- /dev/null +++ b/pr/299/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/299/docs/bench/sidebar-items.js b/pr/299/docs/bench/sidebar-items.js new file mode 100644 index 000000000..f56525d6b --- /dev/null +++ b/pr/299/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/299/docs/bench/stats/fn.throughput_bps.html b/pr/299/docs/bench/stats/fn.throughput_bps.html new file mode 100644 index 000000000..0a49b335e --- /dev/null +++ b/pr/299/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/299/docs/bench/stats/index.html b/pr/299/docs/bench/stats/index.html new file mode 100644 index 000000000..3655ae338 --- /dev/null +++ b/pr/299/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/299/docs/bench/stats/sidebar-items.js b/pr/299/docs/bench/stats/sidebar-items.js new file mode 100644 index 000000000..6272a13ae --- /dev/null +++ b/pr/299/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/299/docs/bench/stats/struct.Stats.html b/pr/299/docs/bench/stats/struct.Stats.html new file mode 100644 index 000000000..0a4807d3e --- /dev/null +++ b/pr/299/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/299/docs/bench/stats/struct.StreamStats.html b/pr/299/docs/bench/stats/struct.StreamStats.html new file mode 100644 index 000000000..80b2f0bf3 --- /dev/null +++ b/pr/299/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/299/docs/bench/stats/struct.TransferResult.html b/pr/299/docs/bench/stats/struct.TransferResult.html new file mode 100644 index 000000000..090628d4d --- /dev/null +++ b/pr/299/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/299/docs/bench/struct.Opt.html b/pr/299/docs/bench/struct.Opt.html new file mode 100644 index 000000000..82be8dcfd --- /dev/null +++ b/pr/299/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/299/docs/bulk/all.html b/pr/299/docs/bulk/all.html new file mode 100644 index 000000000..0a6875d72 --- /dev/null +++ b/pr/299/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/299/docs/bulk/fn.client.html b/pr/299/docs/bulk/fn.client.html new file mode 100644 index 000000000..6a2f1e48a --- /dev/null +++ b/pr/299/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/299/docs/bulk/fn.handle_client_stream.html b/pr/299/docs/bulk/fn.handle_client_stream.html new file mode 100644 index 000000000..ba79a8757 --- /dev/null +++ b/pr/299/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/299/docs/bulk/fn.main.html b/pr/299/docs/bulk/fn.main.html new file mode 100644 index 000000000..e8cfaa7ca --- /dev/null +++ b/pr/299/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/299/docs/bulk/fn.server.html b/pr/299/docs/bulk/fn.server.html new file mode 100644 index 000000000..2ea0860dd --- /dev/null +++ b/pr/299/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/299/docs/bulk/index.html b/pr/299/docs/bulk/index.html new file mode 100644 index 000000000..6b9249f74 --- /dev/null +++ b/pr/299/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/299/docs/bulk/sidebar-items.js b/pr/299/docs/bulk/sidebar-items.js new file mode 100644 index 000000000..4b4dceb17 --- /dev/null +++ b/pr/299/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/299/docs/bulk/struct.ClientStats.html b/pr/299/docs/bulk/struct.ClientStats.html new file mode 100644 index 000000000..91c85df24 --- /dev/null +++ b/pr/299/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 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/299/docs/certificate/all.html b/pr/299/docs/certificate/all.html new file mode 100644 index 000000000..d6e1b79d5 --- /dev/null +++ b/pr/299/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/299/docs/certificate/fn.configure_client.html b/pr/299/docs/certificate/fn.configure_client.html new file mode 100644 index 000000000..95e203add --- /dev/null +++ b/pr/299/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/299/docs/certificate/fn.generate_self_signed_cert.html b/pr/299/docs/certificate/fn.generate_self_signed_cert.html new file mode 100644 index 000000000..e91269156 --- /dev/null +++ b/pr/299/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/299/docs/certificate/fn.main.html b/pr/299/docs/certificate/fn.main.html new file mode 100644 index 000000000..461b95b57 --- /dev/null +++ b/pr/299/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/299/docs/certificate/fn.read_certs_from_file.html b/pr/299/docs/certificate/fn.read_certs_from_file.html new file mode 100644 index 000000000..bd63cf355 --- /dev/null +++ b/pr/299/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/299/docs/certificate/index.html b/pr/299/docs/certificate/index.html new file mode 100644 index 000000000..eb429342b --- /dev/null +++ b/pr/299/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/299/docs/certificate/sidebar-items.js b/pr/299/docs/certificate/sidebar-items.js new file mode 100644 index 000000000..0f8f69b2b --- /dev/null +++ b/pr/299/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/299/docs/certificate/struct.SkipServerVerification.html b/pr/299/docs/certificate/struct.SkipServerVerification.html new file mode 100644 index 000000000..c35462c07 --- /dev/null +++ b/pr/299/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/299/docs/crates.js b/pr/299/docs/crates.js new file mode 100644 index 000000000..25a54490d --- /dev/null +++ b/pr/299/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/299/docs/data_transfer/all.html b/pr/299/docs/data_transfer/all.html new file mode 100644 index 000000000..fd64bbb8f --- /dev/null +++ b/pr/299/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/299/docs/data_transfer/fn.main.html b/pr/299/docs/data_transfer/fn.main.html new file mode 100644 index 000000000..8bcfacdf5 --- /dev/null +++ b/pr/299/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/299/docs/data_transfer/fn.open_bidirectional_stream.html b/pr/299/docs/data_transfer/fn.open_bidirectional_stream.html new file mode 100644 index 000000000..2c097384f --- /dev/null +++ b/pr/299/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/299/docs/data_transfer/fn.open_unidirectional_stream.html b/pr/299/docs/data_transfer/fn.open_unidirectional_stream.html new file mode 100644 index 000000000..0d5cfbb84 --- /dev/null +++ b/pr/299/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/299/docs/data_transfer/fn.receive_bidirectional_stream.html b/pr/299/docs/data_transfer/fn.receive_bidirectional_stream.html new file mode 100644 index 000000000..0180f8dca --- /dev/null +++ b/pr/299/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/299/docs/data_transfer/fn.receive_datagram.html b/pr/299/docs/data_transfer/fn.receive_datagram.html new file mode 100644 index 000000000..f5467d0f5 --- /dev/null +++ b/pr/299/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/299/docs/data_transfer/fn.receive_unidirectional_stream.html b/pr/299/docs/data_transfer/fn.receive_unidirectional_stream.html new file mode 100644 index 000000000..5d00e7b8f --- /dev/null +++ b/pr/299/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/299/docs/data_transfer/fn.send_unreliable.html b/pr/299/docs/data_transfer/fn.send_unreliable.html new file mode 100644 index 000000000..ab8421938 --- /dev/null +++ b/pr/299/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/299/docs/data_transfer/index.html b/pr/299/docs/data_transfer/index.html new file mode 100644 index 000000000..bc41bd1c4 --- /dev/null +++ b/pr/299/docs/data_transfer/index.html @@ -0,0 +1 @@ +data_transfer - Rust

Crate data_transfer

\ No newline at end of file diff --git a/pr/299/docs/data_transfer/sidebar-items.js b/pr/299/docs/data_transfer/sidebar-items.js new file mode 100644 index 000000000..4a3b9f284 --- /dev/null +++ b/pr/299/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/299/docs/help.html b/pr/299/docs/help.html new file mode 100644 index 000000000..99d8cb979 --- /dev/null +++ b/pr/299/docs/help.html @@ -0,0 +1 @@ +Help

All

Rustdoc help

Back
\ No newline at end of file diff --git a/pr/299/docs/iroh_quinn/all.html b/pr/299/docs/iroh_quinn/all.html new file mode 100644 index 000000000..f28f87dc5 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/congestion/index.html b/pr/299/docs/iroh_quinn/congestion/index.html new file mode 100644 index 000000000..38ff69dad --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/congestion/sidebar-items.js b/pr/299/docs/iroh_quinn/congestion/sidebar-items.js new file mode 100644 index 000000000..888f550d4 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/congestion/struct.Bbr.html b/pr/299/docs/iroh_quinn/congestion/struct.Bbr.html new file mode 100644 index 000000000..990c58c07 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/congestion/struct.BbrConfig.html b/pr/299/docs/iroh_quinn/congestion/struct.BbrConfig.html new file mode 100644 index 000000000..3b267cdc4 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/congestion/struct.ControllerMetrics.html b/pr/299/docs/iroh_quinn/congestion/struct.ControllerMetrics.html new file mode 100644 index 000000000..09244b55e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/congestion/struct.Cubic.html b/pr/299/docs/iroh_quinn/congestion/struct.Cubic.html new file mode 100644 index 000000000..b39ee37ea --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/congestion/struct.CubicConfig.html b/pr/299/docs/iroh_quinn/congestion/struct.CubicConfig.html new file mode 100644 index 000000000..19a3bcba7 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/congestion/struct.NewReno.html b/pr/299/docs/iroh_quinn/congestion/struct.NewReno.html new file mode 100644 index 000000000..a29a548f7 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/congestion/struct.NewRenoConfig.html b/pr/299/docs/iroh_quinn/congestion/struct.NewRenoConfig.html new file mode 100644 index 000000000..7333c4b5d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/congestion/trait.Controller.html b/pr/299/docs/iroh_quinn/congestion/trait.Controller.html new file mode 100644 index 000000000..4d9a8a8e9 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/congestion/trait.ControllerFactory.html b/pr/299/docs/iroh_quinn/congestion/trait.ControllerFactory.html new file mode 100644 index 000000000..6707640ab --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/connection/enum.SendDatagramError.html b/pr/299/docs/iroh_quinn/connection/enum.SendDatagramError.html new file mode 100644 index 000000000..8400f90c1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/connection/struct.AcceptBi.html b/pr/299/docs/iroh_quinn/connection/struct.AcceptBi.html new file mode 100644 index 000000000..d2f02bb8c --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/connection/struct.AcceptUni.html b/pr/299/docs/iroh_quinn/connection/struct.AcceptUni.html new file mode 100644 index 000000000..175c32716 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/connection/struct.Connecting.html b/pr/299/docs/iroh_quinn/connection/struct.Connecting.html new file mode 100644 index 000000000..019f5b84c --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/connection/struct.Connection.html b/pr/299/docs/iroh_quinn/connection/struct.Connection.html new file mode 100644 index 000000000..b2c74be20 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/connection/struct.OnClosed.html b/pr/299/docs/iroh_quinn/connection/struct.OnClosed.html new file mode 100644 index 000000000..c7c8fb856 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/connection/struct.OpenBi.html b/pr/299/docs/iroh_quinn/connection/struct.OpenBi.html new file mode 100644 index 000000000..7b7818a64 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/connection/struct.OpenUni.html b/pr/299/docs/iroh_quinn/connection/struct.OpenUni.html new file mode 100644 index 000000000..eedabce57 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/connection/struct.ReadDatagram.html b/pr/299/docs/iroh_quinn/connection/struct.ReadDatagram.html new file mode 100644 index 000000000..c7e97d172 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/connection/struct.SendDatagram.html b/pr/299/docs/iroh_quinn/connection/struct.SendDatagram.html new file mode 100644 index 000000000..78249fc09 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/connection/struct.WeakConnectionHandle.html b/pr/299/docs/iroh_quinn/connection/struct.WeakConnectionHandle.html new file mode 100644 index 000000000..1496376f0 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/connection/struct.ZeroRttAccepted.html b/pr/299/docs/iroh_quinn/connection/struct.ZeroRttAccepted.html new file mode 100644 index 000000000..5c7de33e5 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/index.html b/pr/299/docs/iroh_quinn/crypto/index.html new file mode 100644 index 000000000..22dee581f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/rustls/enum.Error.html b/pr/299/docs/iroh_quinn/crypto/rustls/enum.Error.html new file mode 100644 index 000000000..59ce9ffff --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/rustls/index.html b/pr/299/docs/iroh_quinn/crypto/rustls/index.html new file mode 100644 index 000000000..35a3c6577 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/rustls/sidebar-items.js b/pr/299/docs/iroh_quinn/crypto/rustls/sidebar-items.js new file mode 100644 index 000000000..65f98d4d4 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/rustls/struct.HandshakeData.html b/pr/299/docs/iroh_quinn/crypto/rustls/struct.HandshakeData.html new file mode 100644 index 000000000..cdb7c70fc --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/rustls/struct.NoInitialCipherSuite.html b/pr/299/docs/iroh_quinn/crypto/rustls/struct.NoInitialCipherSuite.html new file mode 100644 index 000000000..ee260d560 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/rustls/struct.QuicClientConfig.html b/pr/299/docs/iroh_quinn/crypto/rustls/struct.QuicClientConfig.html new file mode 100644 index 000000000..01245bde8 --- /dev/null +++ b/pr/299/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::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/299/docs/iroh_quinn/crypto/rustls/struct.QuicServerConfig.html b/pr/299/docs/iroh_quinn/crypto/rustls/struct.QuicServerConfig.html new file mode 100644 index 000000000..3a89edfc5 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/rustls/struct.TlsSession.html b/pr/299/docs/iroh_quinn/crypto/rustls/struct.TlsSession.html new file mode 100644 index 000000000..3e69b9872 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/sidebar-items.js b/pr/299/docs/iroh_quinn/crypto/sidebar-items.js new file mode 100644 index 000000000..375413e92 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/struct.CryptoError.html b/pr/299/docs/iroh_quinn/crypto/struct.CryptoError.html new file mode 100644 index 000000000..b60f9c45c --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/struct.ExportKeyingMaterialError.html b/pr/299/docs/iroh_quinn/crypto/struct.ExportKeyingMaterialError.html new file mode 100644 index 000000000..e24d2196e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/struct.KeyPair.html b/pr/299/docs/iroh_quinn/crypto/struct.KeyPair.html new file mode 100644 index 000000000..c4dc97199 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/struct.Keys.html b/pr/299/docs/iroh_quinn/crypto/struct.Keys.html new file mode 100644 index 000000000..232814912 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/struct.UnsupportedVersion.html b/pr/299/docs/iroh_quinn/crypto/struct.UnsupportedVersion.html new file mode 100644 index 000000000..709220217 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/trait.AeadKey.html b/pr/299/docs/iroh_quinn/crypto/trait.AeadKey.html new file mode 100644 index 000000000..51ac071fe --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/trait.ClientConfig.html b/pr/299/docs/iroh_quinn/crypto/trait.ClientConfig.html new file mode 100644 index 000000000..46555b5ff --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/trait.HandshakeTokenKey.html b/pr/299/docs/iroh_quinn/crypto/trait.HandshakeTokenKey.html new file mode 100644 index 000000000..85576ce8e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/trait.HeaderKey.html b/pr/299/docs/iroh_quinn/crypto/trait.HeaderKey.html new file mode 100644 index 000000000..bc22e9b18 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/trait.HmacKey.html b/pr/299/docs/iroh_quinn/crypto/trait.HmacKey.html new file mode 100644 index 000000000..684cfc6a1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/trait.PacketKey.html b/pr/299/docs/iroh_quinn/crypto/trait.PacketKey.html new file mode 100644 index 000000000..b4580faf0 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/trait.ServerConfig.html b/pr/299/docs/iroh_quinn/crypto/trait.ServerConfig.html new file mode 100644 index 000000000..45f057eae --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/crypto/trait.Session.html b/pr/299/docs/iroh_quinn/crypto/trait.Session.html new file mode 100644 index 000000000..490c06363 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/endpoint/struct.Accept.html b/pr/299/docs/iroh_quinn/endpoint/struct.Accept.html new file mode 100644 index 000000000..4a9a29dbf --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/endpoint/struct.Endpoint.html b/pr/299/docs/iroh_quinn/endpoint/struct.Endpoint.html new file mode 100644 index 000000000..8f85d0127 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/endpoint/struct.EndpointStats.html b/pr/299/docs/iroh_quinn/endpoint/struct.EndpointStats.html new file mode 100644 index 000000000..c95830426 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.ConfigError.html b/pr/299/docs/iroh_quinn/enum.ConfigError.html new file mode 100644 index 000000000..659f797b9 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.ConnectError.html b/pr/299/docs/iroh_quinn/enum.ConnectError.html new file mode 100644 index 000000000..d0f8ace66 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.ConnectionError.html b/pr/299/docs/iroh_quinn/enum.ConnectionError.html new file mode 100644 index 000000000..12363ed23 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.Dir.html b/pr/299/docs/iroh_quinn/enum.Dir.html new file mode 100644 index 000000000..f7e187e1f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.EcnCodepoint.html b/pr/299/docs/iroh_quinn/enum.EcnCodepoint.html new file mode 100644 index 000000000..9dc3f3563 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.FrameType.html b/pr/299/docs/iroh_quinn/enum.FrameType.html new file mode 100644 index 000000000..75df56caf --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.ReadError.html b/pr/299/docs/iroh_quinn/enum.ReadError.html new file mode 100644 index 000000000..f07ed9270 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.ReadExactError.html b/pr/299/docs/iroh_quinn/enum.ReadExactError.html new file mode 100644 index 000000000..cfa14f40f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.ReadToEndError.html b/pr/299/docs/iroh_quinn/enum.ReadToEndError.html new file mode 100644 index 000000000..82b309e87 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.ResetError.html b/pr/299/docs/iroh_quinn/enum.ResetError.html new file mode 100644 index 000000000..8702e7efd --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.SendDatagramError.html b/pr/299/docs/iroh_quinn/enum.SendDatagramError.html new file mode 100644 index 000000000..bd22b081c --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.Side.html b/pr/299/docs/iroh_quinn/enum.Side.html new file mode 100644 index 000000000..e2c824d18 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.StoppedError.html b/pr/299/docs/iroh_quinn/enum.StoppedError.html new file mode 100644 index 000000000..cf9fcd7f7 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/enum.WriteError.html b/pr/299/docs/iroh_quinn/enum.WriteError.html new file mode 100644 index 000000000..fd9bfb6a1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/fn.default_runtime.html b/pr/299/docs/iroh_quinn/fn.default_runtime.html new file mode 100644 index 000000000..d741ac157 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/incoming/struct.Incoming.html b/pr/299/docs/iroh_quinn/incoming/struct.Incoming.html new file mode 100644 index 000000000..abd44a6e1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/incoming/struct.IncomingFuture.html b/pr/299/docs/iroh_quinn/incoming/struct.IncomingFuture.html new file mode 100644 index 000000000..4471430fd --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/incoming/struct.RetryError.html b/pr/299/docs/iroh_quinn/incoming/struct.RetryError.html new file mode 100644 index 000000000..5376fb9c1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/index.html b/pr/299/docs/iroh_quinn/index.html new file mode 100644 index 000000000..da174a383 --- /dev/null +++ b/pr/299/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
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
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
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/299/docs/iroh_quinn/path/struct.OpenPath.html b/pr/299/docs/iroh_quinn/path/struct.OpenPath.html new file mode 100644 index 000000000..98307dd6b --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/path/struct.Path.html b/pr/299/docs/iroh_quinn/path/struct.Path.html new file mode 100644 index 000000000..ca4fa66c4 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/recv_stream/enum.ReadError.html b/pr/299/docs/iroh_quinn/recv_stream/enum.ReadError.html new file mode 100644 index 000000000..a7c5a50f0 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/recv_stream/enum.ReadExactError.html b/pr/299/docs/iroh_quinn/recv_stream/enum.ReadExactError.html new file mode 100644 index 000000000..5ff8c04ac --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/recv_stream/enum.ReadToEndError.html b/pr/299/docs/iroh_quinn/recv_stream/enum.ReadToEndError.html new file mode 100644 index 000000000..0297f93ff --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/recv_stream/enum.ResetError.html b/pr/299/docs/iroh_quinn/recv_stream/enum.ResetError.html new file mode 100644 index 000000000..9f60c8827 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/recv_stream/struct.RecvStream.html b/pr/299/docs/iroh_quinn/recv_stream/struct.RecvStream.html new file mode 100644 index 000000000..2842cf45e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/recv_stream/struct.UnorderedRecvStream.html b/pr/299/docs/iroh_quinn/recv_stream/struct.UnorderedRecvStream.html new file mode 100644 index 000000000..d3cfadcfc --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/runtime/fn.default_runtime.html b/pr/299/docs/iroh_quinn/runtime/fn.default_runtime.html new file mode 100644 index 000000000..0b519171e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/runtime/smol/struct.SmolRuntime.html b/pr/299/docs/iroh_quinn/runtime/smol/struct.SmolRuntime.html new file mode 100644 index 000000000..1bf6a4945 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/runtime/tokio/struct.TokioRuntime.html b/pr/299/docs/iroh_quinn/runtime/tokio/struct.TokioRuntime.html new file mode 100644 index 000000000..d4a7961a5 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/runtime/trait.AsyncTimer.html b/pr/299/docs/iroh_quinn/runtime/trait.AsyncTimer.html new file mode 100644 index 000000000..2a738be07 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/runtime/trait.AsyncUdpSocket.html b/pr/299/docs/iroh_quinn/runtime/trait.AsyncUdpSocket.html new file mode 100644 index 000000000..47814c9b7 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/runtime/trait.Runtime.html b/pr/299/docs/iroh_quinn/runtime/trait.Runtime.html new file mode 100644 index 000000000..bdd2f356e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/runtime/trait.UdpSender.html b/pr/299/docs/iroh_quinn/runtime/trait.UdpSender.html new file mode 100644 index 000000000..b63b9c497 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/send_stream/enum.StoppedError.html b/pr/299/docs/iroh_quinn/send_stream/enum.StoppedError.html new file mode 100644 index 000000000..931afce4d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/send_stream/enum.WriteError.html b/pr/299/docs/iroh_quinn/send_stream/enum.WriteError.html new file mode 100644 index 000000000..b701786e6 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/send_stream/struct.SendStream.html b/pr/299/docs/iroh_quinn/send_stream/struct.SendStream.html new file mode 100644 index 000000000..fec51cd9d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/sidebar-items.js b/pr/299/docs/iroh_quinn/sidebar-items.js new file mode 100644 index 000000000..ae9bffc7f --- /dev/null +++ b/pr/299/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","ApplicationClose","BloomTokenLog","Chunk","ClientConfig","ClosedStream","Connecting","Connection","ConnectionClose","ConnectionId","ConnectionStats","Endpoint","EndpointConfig","EndpointStats","FrameStats","IdleTimeout","Incoming","IncomingFuture","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/299/docs/iroh_quinn/struct.Accept.html b/pr/299/docs/iroh_quinn/struct.Accept.html new file mode 100644 index 000000000..65581f2d2 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.AcceptBi.html b/pr/299/docs/iroh_quinn/struct.AcceptBi.html new file mode 100644 index 000000000..c8b3e6379 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.AcceptUni.html b/pr/299/docs/iroh_quinn/struct.AcceptUni.html new file mode 100644 index 000000000..f072fa9f0 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.AckFrequencyConfig.html b/pr/299/docs/iroh_quinn/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..39aea5364 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.ApplicationClose.html b/pr/299/docs/iroh_quinn/struct.ApplicationClose.html new file mode 100644 index 000000000..e97d2a24c --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.BloomTokenLog.html b/pr/299/docs/iroh_quinn/struct.BloomTokenLog.html new file mode 100644 index 000000000..2d461c3a0 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.Chunk.html b/pr/299/docs/iroh_quinn/struct.Chunk.html new file mode 100644 index 000000000..f06dfdff1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.ClientConfig.html b/pr/299/docs/iroh_quinn/struct.ClientConfig.html new file mode 100644 index 000000000..8d2af413a --- /dev/null +++ b/pr/299/docs/iroh_quinn/struct.ClientConfig.html @@ -0,0 +1,50 @@ +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 with_platform_verifier() -> ClientConfig

👎Deprecated since 0.11.13: use try_with_platform_verifier() instead

Create a client configuration that trusts the platform’s native roots

+
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/299/docs/iroh_quinn/struct.ClosedStream.html b/pr/299/docs/iroh_quinn/struct.ClosedStream.html new file mode 100644 index 000000000..a447d24e2 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.Connecting.html b/pr/299/docs/iroh_quinn/struct.Connecting.html new file mode 100644 index 000000000..781fb0b2f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.Connection.html b/pr/299/docs/iroh_quinn/struct.Connection.html new file mode 100644 index 000000000..d485e59b1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.ConnectionClose.html b/pr/299/docs/iroh_quinn/struct.ConnectionClose.html new file mode 100644 index 000000000..40bd50103 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.ConnectionId.html b/pr/299/docs/iroh_quinn/struct.ConnectionId.html new file mode 100644 index 000000000..75d6fba72 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.ConnectionStats.html b/pr/299/docs/iroh_quinn/struct.ConnectionStats.html new file mode 100644 index 000000000..6088bddd3 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.Endpoint.html b/pr/299/docs/iroh_quinn/struct.Endpoint.html new file mode 100644 index 000000000..3f81be0ef --- /dev/null +++ b/pr/299/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(&mut 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/299/docs/iroh_quinn/struct.EndpointConfig.html b/pr/299/docs/iroh_quinn/struct.EndpointConfig.html new file mode 100644 index 000000000..8264b2983 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.EndpointStats.html b/pr/299/docs/iroh_quinn/struct.EndpointStats.html new file mode 100644 index 000000000..f5c80c9c9 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.FrameStats.html b/pr/299/docs/iroh_quinn/struct.FrameStats.html new file mode 100644 index 000000000..b6fb3da55 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.IdleTimeout.html b/pr/299/docs/iroh_quinn/struct.IdleTimeout.html new file mode 100644 index 000000000..cb731c528 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.Incoming.html b/pr/299/docs/iroh_quinn/struct.Incoming.html new file mode 100644 index 000000000..107f18bf7 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.IncomingFuture.html b/pr/299/docs/iroh_quinn/struct.IncomingFuture.html new file mode 100644 index 000000000..2a9b89fab --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.MtuDiscoveryConfig.html b/pr/299/docs/iroh_quinn/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..a4ab595b0 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.NoneTokenLog.html b/pr/299/docs/iroh_quinn/struct.NoneTokenLog.html new file mode 100644 index 000000000..9d0e0a661 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.NoneTokenStore.html b/pr/299/docs/iroh_quinn/struct.NoneTokenStore.html new file mode 100644 index 000000000..2fd9c75f9 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.OnClosed.html b/pr/299/docs/iroh_quinn/struct.OnClosed.html new file mode 100644 index 000000000..b59cb94ea --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.OpenBi.html b/pr/299/docs/iroh_quinn/struct.OpenBi.html new file mode 100644 index 000000000..5f5d6ad41 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.OpenPath.html b/pr/299/docs/iroh_quinn/struct.OpenPath.html new file mode 100644 index 000000000..d78f1a3b4 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.OpenUni.html b/pr/299/docs/iroh_quinn/struct.OpenUni.html new file mode 100644 index 000000000..af61824b4 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.Path.html b/pr/299/docs/iroh_quinn/struct.Path.html new file mode 100644 index 000000000..7e9a721c2 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.PathId.html b/pr/299/docs/iroh_quinn/struct.PathId.html new file mode 100644 index 000000000..3a2767ea9 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.PathStats.html b/pr/299/docs/iroh_quinn/struct.PathStats.html new file mode 100644 index 000000000..0726d2d4a --- /dev/null +++ b/pr/299/docs/iroh_quinn/struct.PathStats.html @@ -0,0 +1,54 @@ +PathStats in iroh_quinn - Rust

PathStats

Struct PathStats 

Source
#[non_exhaustive]
pub struct PathStats { + pub rtt: Duration, + pub cwnd: u64, + pub congestion_events: u64, + pub lost_packets: u64, + pub lost_bytes: u64, + pub sent_packets: 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)

+
§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_packets: u64

The amount of packets sent on this path

+
§sent_plpmtud_probes: u64

The amount of PLPMTUD probe packets sent on this path (also counted by sent_packets)

+
§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/299/docs/iroh_quinn/struct.QlogConfig.html b/pr/299/docs/iroh_quinn/struct.QlogConfig.html new file mode 100644 index 000000000..8c37642c3 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.QlogFileFactory.html b/pr/299/docs/iroh_quinn/struct.QlogFileFactory.html new file mode 100644 index 000000000..df1cdfe3c --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.ReadDatagram.html b/pr/299/docs/iroh_quinn/struct.ReadDatagram.html new file mode 100644 index 000000000..68960c5e7 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.RecvStream.html b/pr/299/docs/iroh_quinn/struct.RecvStream.html new file mode 100644 index 000000000..1476aa787 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.RetryError.html b/pr/299/docs/iroh_quinn/struct.RetryError.html new file mode 100644 index 000000000..1395a99c2 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.SendDatagram.html b/pr/299/docs/iroh_quinn/struct.SendDatagram.html new file mode 100644 index 000000000..8addb6100 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.SendStream.html b/pr/299/docs/iroh_quinn/struct.SendStream.html new file mode 100644 index 000000000..5baebf4ad --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.ServerConfig.html b/pr/299/docs/iroh_quinn/struct.ServerConfig.html new file mode 100644 index 000000000..502399a23 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.SmolRuntime.html b/pr/299/docs/iroh_quinn/struct.SmolRuntime.html new file mode 100644 index 000000000..bd63cabd8 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.StdSystemTime.html b/pr/299/docs/iroh_quinn/struct.StdSystemTime.html new file mode 100644 index 000000000..b9fa366ad --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.StreamId.html b/pr/299/docs/iroh_quinn/struct.StreamId.html new file mode 100644 index 000000000..3400cb891 --- /dev/null +++ b/pr/299/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
Source§

fn arbitrary_take_rest(u: Unstructured<'arbitrary>) -> Result<StreamId, 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
Source§

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/299/docs/iroh_quinn/struct.TokenMemoryCache.html b/pr/299/docs/iroh_quinn/struct.TokenMemoryCache.html new file mode 100644 index 000000000..1762bb976 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.TokenReuseError.html b/pr/299/docs/iroh_quinn/struct.TokenReuseError.html new file mode 100644 index 000000000..c5aad2809 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.TokioRuntime.html b/pr/299/docs/iroh_quinn/struct.TokioRuntime.html new file mode 100644 index 000000000..a82d38979 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.Transmit.html b/pr/299/docs/iroh_quinn/struct.Transmit.html new file mode 100644 index 000000000..02c38b955 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.TransportConfig.html b/pr/299/docs/iroh_quinn/struct.TransportConfig.html new file mode 100644 index 000000000..b62a74604 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.TransportErrorCode.html b/pr/299/docs/iroh_quinn/struct.TransportErrorCode.html new file mode 100644 index 000000000..c3a461bc6 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.UdpStats.html b/pr/299/docs/iroh_quinn/struct.UdpStats.html new file mode 100644 index 000000000..9bd2b974b --- /dev/null +++ b/pr/299/docs/iroh_quinn/struct.UdpStats.html @@ -0,0 +1,36 @@ +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 Copy 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
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/299/docs/iroh_quinn/struct.UnorderedRecvStream.html b/pr/299/docs/iroh_quinn/struct.UnorderedRecvStream.html new file mode 100644 index 000000000..ab3bb3189 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.ValidationTokenConfig.html b/pr/299/docs/iroh_quinn/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..d112e449f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.VarInt.html b/pr/299/docs/iroh_quinn/struct.VarInt.html new file mode 100644 index 000000000..3765cbde0 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.VarIntBoundsExceeded.html b/pr/299/docs/iroh_quinn/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..bbd712096 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.WeakConnectionHandle.html b/pr/299/docs/iroh_quinn/struct.WeakConnectionHandle.html new file mode 100644 index 000000000..46aa2f331 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.Written.html b/pr/299/docs/iroh_quinn/struct.Written.html new file mode 100644 index 000000000..daafa80a8 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/struct.ZeroRttAccepted.html b/pr/299/docs/iroh_quinn/struct.ZeroRttAccepted.html new file mode 100644 index 000000000..c5d9c8129 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/trait.AsyncTimer.html b/pr/299/docs/iroh_quinn/trait.AsyncTimer.html new file mode 100644 index 000000000..c8f214dd7 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/trait.AsyncUdpSocket.html b/pr/299/docs/iroh_quinn/trait.AsyncUdpSocket.html new file mode 100644 index 000000000..f6885a6ca --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/trait.ConnectionIdGenerator.html b/pr/299/docs/iroh_quinn/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..e7e811495 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/trait.QlogFactory.html b/pr/299/docs/iroh_quinn/trait.QlogFactory.html new file mode 100644 index 000000000..d5252406d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/trait.Runtime.html b/pr/299/docs/iroh_quinn/trait.Runtime.html new file mode 100644 index 000000000..f256e13f0 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/trait.TimeSource.html b/pr/299/docs/iroh_quinn/trait.TimeSource.html new file mode 100644 index 000000000..f902865de --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/trait.TokenLog.html b/pr/299/docs/iroh_quinn/trait.TokenLog.html new file mode 100644 index 000000000..0b58ab6ec --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/trait.TokenStore.html b/pr/299/docs/iroh_quinn/trait.TokenStore.html new file mode 100644 index 000000000..b39308556 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn/trait.UdpSender.html b/pr/299/docs/iroh_quinn/trait.UdpSender.html new file mode 100644 index 000000000..7076f3a68 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/all.html b/pr/299/docs/iroh_quinn_proto/all.html new file mode 100644 index 000000000..dc1a2d901 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/bench_exports/index.html b/pr/299/docs/iroh_quinn_proto/bench_exports/index.html new file mode 100644 index 000000000..ed857d58b --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html b/pr/299/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_into_many_segments.html new file mode 100644 index 000000000..50470093c --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html b/pr/299/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/fn.get_loop_many_segments.html new file mode 100644 index 000000000..28284de52 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/index.html b/pr/299/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/index.html new file mode 100644 index 000000000..00ca26f44 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/sidebar-items.js b/pr/299/docs/iroh_quinn_proto/bench_exports/send_buffer_benches/sidebar-items.js new file mode 100644 index 000000000..24edbdcf5 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/bench_exports/sidebar-items.js b/pr/299/docs/iroh_quinn_proto/bench_exports/sidebar-items.js new file mode 100644 index 000000000..cb9cfb5fa --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/bloom_token_log/struct.BloomTokenLog.html b/pr/299/docs/iroh_quinn_proto/bloom_token_log/struct.BloomTokenLog.html new file mode 100644 index 000000000..b31125b10 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/cid_generator/struct.HashedConnectionIdGenerator.html b/pr/299/docs/iroh_quinn_proto/cid_generator/struct.HashedConnectionIdGenerator.html new file mode 100644 index 000000000..cf0d92970 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/cid_generator/struct.InvalidCid.html b/pr/299/docs/iroh_quinn_proto/cid_generator/struct.InvalidCid.html new file mode 100644 index 000000000..3cc694461 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/cid_generator/struct.RandomConnectionIdGenerator.html b/pr/299/docs/iroh_quinn_proto/cid_generator/struct.RandomConnectionIdGenerator.html new file mode 100644 index 000000000..a85350b7b --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.html b/pr/299/docs/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..ce1c7d62c --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/coding/index.html b/pr/299/docs/iroh_quinn_proto/coding/index.html new file mode 100644 index 000000000..97aa6bfc0 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/coding/sidebar-items.js b/pr/299/docs/iroh_quinn_proto/coding/sidebar-items.js new file mode 100644 index 000000000..e6d58e381 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html b/pr/299/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html new file mode 100644 index 000000000..ef3e8364d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/coding/trait.Decodable.html b/pr/299/docs/iroh_quinn_proto/coding/trait.Decodable.html new file mode 100644 index 000000000..16e4e3c82 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/coding/trait.Encodable.html b/pr/299/docs/iroh_quinn_proto/coding/trait.Encodable.html new file mode 100644 index 000000000..6c519c194 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/coding/type.Result.html b/pr/299/docs/iroh_quinn_proto/coding/type.Result.html new file mode 100644 index 000000000..185875b69 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/enum.ConfigError.html b/pr/299/docs/iroh_quinn_proto/config/enum.ConfigError.html new file mode 100644 index 000000000..a2318c2f1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/qlog/struct.QlogConfig.html b/pr/299/docs/iroh_quinn_proto/config/qlog/struct.QlogConfig.html new file mode 100644 index 000000000..1e9d560a8 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/qlog/struct.QlogFileFactory.html b/pr/299/docs/iroh_quinn_proto/config/qlog/struct.QlogFileFactory.html new file mode 100644 index 000000000..6a7bb652e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/qlog/trait.QlogFactory.html b/pr/299/docs/iroh_quinn_proto/config/qlog/trait.QlogFactory.html new file mode 100644 index 000000000..e58263549 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/struct.ClientConfig.html b/pr/299/docs/iroh_quinn_proto/config/struct.ClientConfig.html new file mode 100644 index 000000000..72ad29942 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/struct.EndpointConfig.html b/pr/299/docs/iroh_quinn_proto/config/struct.EndpointConfig.html new file mode 100644 index 000000000..f787522dd --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/struct.ServerConfig.html b/pr/299/docs/iroh_quinn_proto/config/struct.ServerConfig.html new file mode 100644 index 000000000..54e34e58b --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/struct.StdSystemTime.html b/pr/299/docs/iroh_quinn_proto/config/struct.StdSystemTime.html new file mode 100644 index 000000000..a036660a0 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/struct.ValidationTokenConfig.html b/pr/299/docs/iroh_quinn_proto/config/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..71c171cbb --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/trait.TimeSource.html b/pr/299/docs/iroh_quinn_proto/config/trait.TimeSource.html new file mode 100644 index 000000000..531e889ff --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html b/pr/299/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/299/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/299/docs/iroh_quinn_proto/config/transport/struct.AckFrequencyConfig.html b/pr/299/docs/iroh_quinn_proto/config/transport/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..f1cfe6e06 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/transport/struct.IdleTimeout.html b/pr/299/docs/iroh_quinn_proto/config/transport/struct.IdleTimeout.html new file mode 100644 index 000000000..d3b3774c0 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/transport/struct.MtuDiscoveryConfig.html b/pr/299/docs/iroh_quinn_proto/config/transport/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..8f76f4934 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/config/transport/struct.TransportConfig.html b/pr/299/docs/iroh_quinn_proto/config/transport/struct.TransportConfig.html new file mode 100644 index 000000000..b849996c3 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/bbr/struct.Bbr.html b/pr/299/docs/iroh_quinn_proto/congestion/bbr/struct.Bbr.html new file mode 100644 index 000000000..ab7174506 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/bbr/struct.BbrConfig.html b/pr/299/docs/iroh_quinn_proto/congestion/bbr/struct.BbrConfig.html new file mode 100644 index 000000000..0d5073995 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/cubic/struct.Cubic.html b/pr/299/docs/iroh_quinn_proto/congestion/cubic/struct.Cubic.html new file mode 100644 index 000000000..687e48b93 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/cubic/struct.CubicConfig.html b/pr/299/docs/iroh_quinn_proto/congestion/cubic/struct.CubicConfig.html new file mode 100644 index 000000000..5c78ca8d6 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/index.html b/pr/299/docs/iroh_quinn_proto/congestion/index.html new file mode 100644 index 000000000..591662f1f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/new_reno/struct.NewReno.html b/pr/299/docs/iroh_quinn_proto/congestion/new_reno/struct.NewReno.html new file mode 100644 index 000000000..2722e12a4 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/new_reno/struct.NewRenoConfig.html b/pr/299/docs/iroh_quinn_proto/congestion/new_reno/struct.NewRenoConfig.html new file mode 100644 index 000000000..50d3f5805 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/sidebar-items.js b/pr/299/docs/iroh_quinn_proto/congestion/sidebar-items.js new file mode 100644 index 000000000..888f550d4 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/struct.Bbr.html b/pr/299/docs/iroh_quinn_proto/congestion/struct.Bbr.html new file mode 100644 index 000000000..3487f2cd9 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/struct.BbrConfig.html b/pr/299/docs/iroh_quinn_proto/congestion/struct.BbrConfig.html new file mode 100644 index 000000000..a12fb6965 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/struct.ControllerMetrics.html b/pr/299/docs/iroh_quinn_proto/congestion/struct.ControllerMetrics.html new file mode 100644 index 000000000..59e99e093 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/struct.Cubic.html b/pr/299/docs/iroh_quinn_proto/congestion/struct.Cubic.html new file mode 100644 index 000000000..17c3030b3 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/struct.CubicConfig.html b/pr/299/docs/iroh_quinn_proto/congestion/struct.CubicConfig.html new file mode 100644 index 000000000..0c60fb969 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/struct.NewReno.html b/pr/299/docs/iroh_quinn_proto/congestion/struct.NewReno.html new file mode 100644 index 000000000..7d8fa8071 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/struct.NewRenoConfig.html b/pr/299/docs/iroh_quinn_proto/congestion/struct.NewRenoConfig.html new file mode 100644 index 000000000..9d618e249 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/trait.Controller.html b/pr/299/docs/iroh_quinn_proto/congestion/trait.Controller.html new file mode 100644 index 000000000..b57591df3 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/congestion/trait.ControllerFactory.html b/pr/299/docs/iroh_quinn_proto/congestion/trait.ControllerFactory.html new file mode 100644 index 000000000..8e98dac9d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/assembler/struct.Chunk.html b/pr/299/docs/iroh_quinn_proto/connection/assembler/struct.Chunk.html new file mode 100644 index 000000000..f7ac7a578 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/datagrams/enum.SendDatagramError.html b/pr/299/docs/iroh_quinn_proto/connection/datagrams/enum.SendDatagramError.html new file mode 100644 index 000000000..addce8553 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/datagrams/struct.Datagrams.html b/pr/299/docs/iroh_quinn_proto/connection/datagrams/struct.Datagrams.html new file mode 100644 index 000000000..240a4cef6 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/enum.ClosePathError.html b/pr/299/docs/iroh_quinn_proto/connection/enum.ClosePathError.html new file mode 100644 index 000000000..73bb84ef9 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/enum.ConnectionError.html b/pr/299/docs/iroh_quinn_proto/connection/enum.ConnectionError.html new file mode 100644 index 000000000..abbb38eab --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/enum.Event.html b/pr/299/docs/iroh_quinn_proto/connection/enum.Event.html new file mode 100644 index 000000000..d1dc43357 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/enum.PathError.html b/pr/299/docs/iroh_quinn_proto/connection/enum.PathError.html new file mode 100644 index 000000000..c45b49c08 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/paths/enum.PathEvent.html b/pr/299/docs/iroh_quinn_proto/connection/paths/enum.PathEvent.html new file mode 100644 index 000000000..5002bde8e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/paths/enum.PathStatus.html b/pr/299/docs/iroh_quinn_proto/connection/paths/enum.PathStatus.html new file mode 100644 index 000000000..a886eee7b --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/paths/enum.SetPathStatusError.html b/pr/299/docs/iroh_quinn_proto/connection/paths/enum.SetPathStatusError.html new file mode 100644 index 000000000..60829b195 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/paths/struct.ClosedPath.html b/pr/299/docs/iroh_quinn_proto/connection/paths/struct.ClosedPath.html new file mode 100644 index 000000000..63b45525e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/paths/struct.PathId.html b/pr/299/docs/iroh_quinn_proto/connection/paths/struct.PathId.html new file mode 100644 index 000000000..2fd79824a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/paths/struct.RttEstimator.html b/pr/299/docs/iroh_quinn_proto/connection/paths/struct.RttEstimator.html new file mode 100644 index 000000000..21f95bb18 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/fn.get_into_many_segments.html b/pr/299/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/299/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/299/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/fn.get_loop_many_segments.html b/pr/299/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/299/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/299/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/index.html b/pr/299/docs/iroh_quinn_proto/connection/send_buffer/send_buffer_benches/index.html new file mode 100644 index 000000000..4a1c7ea83 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/stats/struct.ConnectionStats.html b/pr/299/docs/iroh_quinn_proto/connection/stats/struct.ConnectionStats.html new file mode 100644 index 000000000..2102eceea --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/stats/struct.FrameStats.html b/pr/299/docs/iroh_quinn_proto/connection/stats/struct.FrameStats.html new file mode 100644 index 000000000..9385ca7df --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/stats/struct.PathStats.html b/pr/299/docs/iroh_quinn_proto/connection/stats/struct.PathStats.html new file mode 100644 index 000000000..e561e9dcf --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/stats/struct.UdpStats.html b/pr/299/docs/iroh_quinn_proto/connection/stats/struct.UdpStats.html new file mode 100644 index 000000000..f606e4f17 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/streams/enum.StreamEvent.html b/pr/299/docs/iroh_quinn_proto/connection/streams/enum.StreamEvent.html new file mode 100644 index 000000000..03531d047 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadError.html b/pr/299/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadError.html new file mode 100644 index 000000000..ebeb73d2e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadableError.html b/pr/299/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadableError.html new file mode 100644 index 000000000..41f875c3e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/streams/recv/struct.Chunks.html b/pr/299/docs/iroh_quinn_proto/connection/streams/recv/struct.Chunks.html new file mode 100644 index 000000000..2c3a1e2c2 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/streams/send/enum.FinishError.html b/pr/299/docs/iroh_quinn_proto/connection/streams/send/enum.FinishError.html new file mode 100644 index 000000000..404821c56 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/streams/send/enum.WriteError.html b/pr/299/docs/iroh_quinn_proto/connection/streams/send/enum.WriteError.html new file mode 100644 index 000000000..a3a2799d8 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/streams/send/struct.Written.html b/pr/299/docs/iroh_quinn_proto/connection/streams/send/struct.Written.html new file mode 100644 index 000000000..886ef15f4 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/streams/struct.ClosedStream.html b/pr/299/docs/iroh_quinn_proto/connection/streams/struct.ClosedStream.html new file mode 100644 index 000000000..c276926d2 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/streams/struct.RecvStream.html b/pr/299/docs/iroh_quinn_proto/connection/streams/struct.RecvStream.html new file mode 100644 index 000000000..db77de12a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/streams/struct.SendStream.html b/pr/299/docs/iroh_quinn_proto/connection/streams/struct.SendStream.html new file mode 100644 index 000000000..84c2e122d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/streams/struct.ShouldTransmit.html b/pr/299/docs/iroh_quinn_proto/connection/streams/struct.ShouldTransmit.html new file mode 100644 index 000000000..8023ab946 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/streams/struct.Streams.html b/pr/299/docs/iroh_quinn_proto/connection/streams/struct.Streams.html new file mode 100644 index 000000000..b3a078714 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/connection/struct.Connection.html b/pr/299/docs/iroh_quinn_proto/connection/struct.Connection.html new file mode 100644 index 000000000..6e3774403 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html b/pr/299/docs/iroh_quinn_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html new file mode 100644 index 000000000..20952ccea --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html b/pr/299/docs/iroh_quinn_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html new file mode 100644 index 000000000..b3e942496 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/index.html b/pr/299/docs/iroh_quinn_proto/crypto/index.html new file mode 100644 index 000000000..f315f347c --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/rustls/enum.Error.html b/pr/299/docs/iroh_quinn_proto/crypto/rustls/enum.Error.html new file mode 100644 index 000000000..20765085f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/rustls/index.html b/pr/299/docs/iroh_quinn_proto/crypto/rustls/index.html new file mode 100644 index 000000000..96919ee18 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/rustls/sidebar-items.js b/pr/299/docs/iroh_quinn_proto/crypto/rustls/sidebar-items.js new file mode 100644 index 000000000..65f98d4d4 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/rustls/struct.HandshakeData.html b/pr/299/docs/iroh_quinn_proto/crypto/rustls/struct.HandshakeData.html new file mode 100644 index 000000000..e170d4694 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/rustls/struct.NoInitialCipherSuite.html b/pr/299/docs/iroh_quinn_proto/crypto/rustls/struct.NoInitialCipherSuite.html new file mode 100644 index 000000000..929b72d9f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/rustls/struct.QuicClientConfig.html b/pr/299/docs/iroh_quinn_proto/crypto/rustls/struct.QuicClientConfig.html new file mode 100644 index 000000000..353aed3a5 --- /dev/null +++ b/pr/299/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::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/299/docs/iroh_quinn_proto/crypto/rustls/struct.QuicServerConfig.html b/pr/299/docs/iroh_quinn_proto/crypto/rustls/struct.QuicServerConfig.html new file mode 100644 index 000000000..bb1d795f3 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/rustls/struct.TlsSession.html b/pr/299/docs/iroh_quinn_proto/crypto/rustls/struct.TlsSession.html new file mode 100644 index 000000000..d2e01165f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/sidebar-items.js b/pr/299/docs/iroh_quinn_proto/crypto/sidebar-items.js new file mode 100644 index 000000000..375413e92 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/struct.CryptoError.html b/pr/299/docs/iroh_quinn_proto/crypto/struct.CryptoError.html new file mode 100644 index 000000000..096170b26 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/struct.ExportKeyingMaterialError.html b/pr/299/docs/iroh_quinn_proto/crypto/struct.ExportKeyingMaterialError.html new file mode 100644 index 000000000..9fca4b488 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/struct.KeyPair.html b/pr/299/docs/iroh_quinn_proto/crypto/struct.KeyPair.html new file mode 100644 index 000000000..fbfd5d65d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/struct.Keys.html b/pr/299/docs/iroh_quinn_proto/crypto/struct.Keys.html new file mode 100644 index 000000000..cfb062097 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/struct.UnsupportedVersion.html b/pr/299/docs/iroh_quinn_proto/crypto/struct.UnsupportedVersion.html new file mode 100644 index 000000000..0d7e13f7e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/trait.AeadKey.html b/pr/299/docs/iroh_quinn_proto/crypto/trait.AeadKey.html new file mode 100644 index 000000000..e1abdf405 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/trait.ClientConfig.html b/pr/299/docs/iroh_quinn_proto/crypto/trait.ClientConfig.html new file mode 100644 index 000000000..b45349488 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.html b/pr/299/docs/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.html new file mode 100644 index 000000000..ca4a39808 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/trait.HeaderKey.html b/pr/299/docs/iroh_quinn_proto/crypto/trait.HeaderKey.html new file mode 100644 index 000000000..cc36c1859 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/trait.HmacKey.html b/pr/299/docs/iroh_quinn_proto/crypto/trait.HmacKey.html new file mode 100644 index 000000000..26cf8079b --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/trait.PacketKey.html b/pr/299/docs/iroh_quinn_proto/crypto/trait.PacketKey.html new file mode 100644 index 000000000..737f4ef77 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/trait.ServerConfig.html b/pr/299/docs/iroh_quinn_proto/crypto/trait.ServerConfig.html new file mode 100644 index 000000000..d540cbd8e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/crypto/trait.Session.html b/pr/299/docs/iroh_quinn_proto/crypto/trait.Session.html new file mode 100644 index 000000000..cb10cecaa --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/endpoint/enum.ConnectError.html b/pr/299/docs/iroh_quinn_proto/endpoint/enum.ConnectError.html new file mode 100644 index 000000000..d80e75b8e --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/endpoint/enum.DatagramEvent.html b/pr/299/docs/iroh_quinn_proto/endpoint/enum.DatagramEvent.html new file mode 100644 index 000000000..dd7b60907 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/endpoint/struct.AcceptError.html b/pr/299/docs/iroh_quinn_proto/endpoint/struct.AcceptError.html new file mode 100644 index 000000000..dc615b45d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/endpoint/struct.ConnectionHandle.html b/pr/299/docs/iroh_quinn_proto/endpoint/struct.ConnectionHandle.html new file mode 100644 index 000000000..72a5a9b92 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/endpoint/struct.Endpoint.html b/pr/299/docs/iroh_quinn_proto/endpoint/struct.Endpoint.html new file mode 100644 index 000000000..80d25b39b --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/endpoint/struct.Incoming.html b/pr/299/docs/iroh_quinn_proto/endpoint/struct.Incoming.html new file mode 100644 index 000000000..4c2ebf7d4 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/endpoint/struct.RetryError.html b/pr/299/docs/iroh_quinn_proto/endpoint/struct.RetryError.html new file mode 100644 index 000000000..8df786829 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.ClosePathError.html b/pr/299/docs/iroh_quinn_proto/enum.ClosePathError.html new file mode 100644 index 000000000..c19ac2a20 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.ConfigError.html b/pr/299/docs/iroh_quinn_proto/enum.ConfigError.html new file mode 100644 index 000000000..dad9b87e9 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.ConnectError.html b/pr/299/docs/iroh_quinn_proto/enum.ConnectError.html new file mode 100644 index 000000000..428ecd896 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.ConnectionError.html b/pr/299/docs/iroh_quinn_proto/enum.ConnectionError.html new file mode 100644 index 000000000..b0aa0679a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.DatagramEvent.html b/pr/299/docs/iroh_quinn_proto/enum.DatagramEvent.html new file mode 100644 index 000000000..84cf0f163 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.Dir.html b/pr/299/docs/iroh_quinn_proto/enum.Dir.html new file mode 100644 index 000000000..277b0a09f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.EcnCodepoint.html b/pr/299/docs/iroh_quinn_proto/enum.EcnCodepoint.html new file mode 100644 index 000000000..bc04c010d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.Event.html b/pr/299/docs/iroh_quinn_proto/enum.Event.html new file mode 100644 index 000000000..4dd782a5a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.FinishError.html b/pr/299/docs/iroh_quinn_proto/enum.FinishError.html new file mode 100644 index 000000000..660398d07 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.FrameType.html b/pr/299/docs/iroh_quinn_proto/enum.FrameType.html new file mode 100644 index 000000000..a48b71e56 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.LongType.html b/pr/299/docs/iroh_quinn_proto/enum.LongType.html new file mode 100644 index 000000000..cbd494dbb --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.PacketDecodeError.html b/pr/299/docs/iroh_quinn_proto/enum.PacketDecodeError.html new file mode 100644 index 000000000..f9f1e48f1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.PathError.html b/pr/299/docs/iroh_quinn_proto/enum.PathError.html new file mode 100644 index 000000000..6a9238ff5 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.PathEvent.html b/pr/299/docs/iroh_quinn_proto/enum.PathEvent.html new file mode 100644 index 000000000..639a9586b --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.PathStatus.html b/pr/299/docs/iroh_quinn_proto/enum.PathStatus.html new file mode 100644 index 000000000..5318c4db4 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.ProtectedHeader.html b/pr/299/docs/iroh_quinn_proto/enum.ProtectedHeader.html new file mode 100644 index 000000000..649046f60 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.ReadError.html b/pr/299/docs/iroh_quinn_proto/enum.ReadError.html new file mode 100644 index 000000000..74f8a512f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.ReadableError.html b/pr/299/docs/iroh_quinn_proto/enum.ReadableError.html new file mode 100644 index 000000000..8213c3e0a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.SendDatagramError.html b/pr/299/docs/iroh_quinn_proto/enum.SendDatagramError.html new file mode 100644 index 000000000..c3f0463e1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.SetPathStatusError.html b/pr/299/docs/iroh_quinn_proto/enum.SetPathStatusError.html new file mode 100644 index 000000000..0140b49ad --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.Side.html b/pr/299/docs/iroh_quinn_proto/enum.Side.html new file mode 100644 index 000000000..2f7721962 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.StreamEvent.html b/pr/299/docs/iroh_quinn_proto/enum.StreamEvent.html new file mode 100644 index 000000000..98a80dc3d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/enum.WriteError.html b/pr/299/docs/iroh_quinn_proto/enum.WriteError.html new file mode 100644 index 000000000..6047c4ea6 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/frame/enum.FrameType.html b/pr/299/docs/iroh_quinn_proto/frame/enum.FrameType.html new file mode 100644 index 000000000..26072c98b --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/frame/struct.ApplicationClose.html b/pr/299/docs/iroh_quinn_proto/frame/struct.ApplicationClose.html new file mode 100644 index 000000000..6785c96b6 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/frame/struct.ConnectionClose.html b/pr/299/docs/iroh_quinn_proto/frame/struct.ConnectionClose.html new file mode 100644 index 000000000..9839746e5 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/frame/struct.Datagram.html b/pr/299/docs/iroh_quinn_proto/frame/struct.Datagram.html new file mode 100644 index 000000000..96b3bbb73 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/index.html b/pr/299/docs/iroh_quinn_proto/index.html new file mode 100644 index 000000000..447107eab --- /dev/null +++ b/pr/299/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
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
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
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
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
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/299/docs/iroh_quinn_proto/iroh_hp/enum.Error.html b/pr/299/docs/iroh_quinn_proto/iroh_hp/enum.Error.html new file mode 100644 index 000000000..e3a8c133a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/iroh_hp/enum.Event.html b/pr/299/docs/iroh_quinn_proto/iroh_hp/enum.Event.html new file mode 100644 index 000000000..29ca12c48 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/iroh_hp/index.html b/pr/299/docs/iroh_quinn_proto/iroh_hp/index.html new file mode 100644 index 000000000..7ca8317c0 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/iroh_hp/sidebar-items.js b/pr/299/docs/iroh_quinn_proto/iroh_hp/sidebar-items.js new file mode 100644 index 000000000..e03b07ea1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/packet/enum.LongType.html b/pr/299/docs/iroh_quinn_proto/packet/enum.LongType.html new file mode 100644 index 000000000..dcf81f61f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/packet/enum.PacketDecodeError.html b/pr/299/docs/iroh_quinn_proto/packet/enum.PacketDecodeError.html new file mode 100644 index 000000000..494edb286 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/packet/enum.ProtectedHeader.html b/pr/299/docs/iroh_quinn_proto/packet/enum.ProtectedHeader.html new file mode 100644 index 000000000..4e6ca3502 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/packet/struct.FixedLengthConnectionIdParser.html b/pr/299/docs/iroh_quinn_proto/packet/struct.FixedLengthConnectionIdParser.html new file mode 100644 index 000000000..63a343905 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/packet/struct.PartialDecode.html b/pr/299/docs/iroh_quinn_proto/packet/struct.PartialDecode.html new file mode 100644 index 000000000..8ea383e01 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/packet/struct.ProtectedInitialHeader.html b/pr/299/docs/iroh_quinn_proto/packet/struct.ProtectedInitialHeader.html new file mode 100644 index 000000000..e2c23e247 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/packet/trait.ConnectionIdParser.html b/pr/299/docs/iroh_quinn_proto/packet/trait.ConnectionIdParser.html new file mode 100644 index 000000000..2d526fcd6 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/shared/enum.EcnCodepoint.html b/pr/299/docs/iroh_quinn_proto/shared/enum.EcnCodepoint.html new file mode 100644 index 000000000..31fd913dc --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/shared/struct.ConnectionEvent.html b/pr/299/docs/iroh_quinn_proto/shared/struct.ConnectionEvent.html new file mode 100644 index 000000000..18bfd544f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/shared/struct.ConnectionId.html b/pr/299/docs/iroh_quinn_proto/shared/struct.ConnectionId.html new file mode 100644 index 000000000..67aa4df61 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/shared/struct.EndpointEvent.html b/pr/299/docs/iroh_quinn_proto/shared/struct.EndpointEvent.html new file mode 100644 index 000000000..4b4f3e586 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/sidebar-items.js b/pr/299/docs/iroh_quinn_proto/sidebar-items.js new file mode 100644 index 000000000..4c6d9f4fa --- /dev/null +++ b/pr/299/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","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","Datagrams","Endpoint","EndpointConfig","EndpointEvent","FixedLengthConnectionIdParser","FourTuple","FrameStats","HashedConnectionIdGenerator","IdleTimeout","Incoming","InvalidCid","MtuDiscoveryConfig","NoneTokenLog","NoneTokenStore","PartialDecode","PathId","PathStats","ProtectedInitialHeader","QlogConfig","QlogFileFactory","RandomConnectionIdGenerator","RecvStream","RetryError","RttEstimator","SendStream","ServerConfig","ShouldTransmit","StdSystemTime","StreamId","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/299/docs/iroh_quinn_proto/struct.AcceptError.html b/pr/299/docs/iroh_quinn_proto/struct.AcceptError.html new file mode 100644 index 000000000..7be3486e3 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.AckFrequencyConfig.html b/pr/299/docs/iroh_quinn_proto/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..3b8fd2463 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.ApplicationClose.html b/pr/299/docs/iroh_quinn_proto/struct.ApplicationClose.html new file mode 100644 index 000000000..e2e9674e7 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.BloomTokenLog.html b/pr/299/docs/iroh_quinn_proto/struct.BloomTokenLog.html new file mode 100644 index 000000000..1f352b52f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.Chunk.html b/pr/299/docs/iroh_quinn_proto/struct.Chunk.html new file mode 100644 index 000000000..3cf405208 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.Chunks.html b/pr/299/docs/iroh_quinn_proto/struct.Chunks.html new file mode 100644 index 000000000..559792dda --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.ClientConfig.html b/pr/299/docs/iroh_quinn_proto/struct.ClientConfig.html new file mode 100644 index 000000000..5e7d545b9 --- /dev/null +++ b/pr/299/docs/iroh_quinn_proto/struct.ClientConfig.html @@ -0,0 +1,47 @@ +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 with_platform_verifier() -> Self

👎Deprecated since 0.11.13: use try_with_platform_verifier() instead

Create a client configuration that trusts the platform’s native roots

+
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/299/docs/iroh_quinn_proto/struct.ClosedPath.html b/pr/299/docs/iroh_quinn_proto/struct.ClosedPath.html new file mode 100644 index 000000000..1dc5bce25 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.ClosedStream.html b/pr/299/docs/iroh_quinn_proto/struct.ClosedStream.html new file mode 100644 index 000000000..247ee08fd --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.Connection.html b/pr/299/docs/iroh_quinn_proto/struct.Connection.html new file mode 100644 index 000000000..b0cdc5029 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.ConnectionClose.html b/pr/299/docs/iroh_quinn_proto/struct.ConnectionClose.html new file mode 100644 index 000000000..89feaf10a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.ConnectionEvent.html b/pr/299/docs/iroh_quinn_proto/struct.ConnectionEvent.html new file mode 100644 index 000000000..e317ca983 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.ConnectionHandle.html b/pr/299/docs/iroh_quinn_proto/struct.ConnectionHandle.html new file mode 100644 index 000000000..d1ca9f2a7 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.ConnectionId.html b/pr/299/docs/iroh_quinn_proto/struct.ConnectionId.html new file mode 100644 index 000000000..6228c6140 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.ConnectionStats.html b/pr/299/docs/iroh_quinn_proto/struct.ConnectionStats.html new file mode 100644 index 000000000..cd892b6e1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.Datagram.html b/pr/299/docs/iroh_quinn_proto/struct.Datagram.html new file mode 100644 index 000000000..c03678b80 --- /dev/null +++ b/pr/299/docs/iroh_quinn_proto/struct.Datagram.html @@ -0,0 +1,30 @@ +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 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, 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/299/docs/iroh_quinn_proto/struct.Datagrams.html b/pr/299/docs/iroh_quinn_proto/struct.Datagrams.html new file mode 100644 index 000000000..24d7b019a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.Endpoint.html b/pr/299/docs/iroh_quinn_proto/struct.Endpoint.html new file mode 100644 index 000000000..f8bd5e32f --- /dev/null +++ b/pr/299/docs/iroh_quinn_proto/struct.Endpoint.html @@ -0,0 +1,83 @@ +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, + rng_seed: Option<[u8; 32]>, +) -> 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.

+

If rng_seed is provided, it will be used to initialize the endpoint’s rng (having priority +over the rng seed configured in EndpointConfig). Note that the rng_seed parameter will +be removed in a future release, so prefer setting it to None and configuring rng seeds +using EndpointConfig::rng_seed.

+
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/299/docs/iroh_quinn_proto/struct.EndpointConfig.html b/pr/299/docs/iroh_quinn_proto/struct.EndpointConfig.html new file mode 100644 index 000000000..50830c687 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.EndpointEvent.html b/pr/299/docs/iroh_quinn_proto/struct.EndpointEvent.html new file mode 100644 index 000000000..5ba222115 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html b/pr/299/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html new file mode 100644 index 000000000..4c7502c43 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.FourTuple.html b/pr/299/docs/iroh_quinn_proto/struct.FourTuple.html new file mode 100644 index 000000000..bf77a177d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.FrameStats.html b/pr/299/docs/iroh_quinn_proto/struct.FrameStats.html new file mode 100644 index 000000000..adad219dd --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.HashedConnectionIdGenerator.html b/pr/299/docs/iroh_quinn_proto/struct.HashedConnectionIdGenerator.html new file mode 100644 index 000000000..71e6e2820 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.IdleTimeout.html b/pr/299/docs/iroh_quinn_proto/struct.IdleTimeout.html new file mode 100644 index 000000000..dd762ac5a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.Incoming.html b/pr/299/docs/iroh_quinn_proto/struct.Incoming.html new file mode 100644 index 000000000..961800b4f --- /dev/null +++ b/pr/299/docs/iroh_quinn_proto/struct.Incoming.html @@ -0,0 +1,37 @@ +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

+

This has the same behavior as [Connection::local_ip].

+
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/299/docs/iroh_quinn_proto/struct.InvalidCid.html b/pr/299/docs/iroh_quinn_proto/struct.InvalidCid.html new file mode 100644 index 000000000..15b72eae8 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.MtuDiscoveryConfig.html b/pr/299/docs/iroh_quinn_proto/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..d9665f60f --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.NoneTokenLog.html b/pr/299/docs/iroh_quinn_proto/struct.NoneTokenLog.html new file mode 100644 index 000000000..39c959fc2 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.NoneTokenStore.html b/pr/299/docs/iroh_quinn_proto/struct.NoneTokenStore.html new file mode 100644 index 000000000..34ac91044 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.PartialDecode.html b/pr/299/docs/iroh_quinn_proto/struct.PartialDecode.html new file mode 100644 index 000000000..52b9d3ebc --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.PathId.html b/pr/299/docs/iroh_quinn_proto/struct.PathId.html new file mode 100644 index 000000000..394c67c30 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.PathStats.html b/pr/299/docs/iroh_quinn_proto/struct.PathStats.html new file mode 100644 index 000000000..6568dc40a --- /dev/null +++ b/pr/299/docs/iroh_quinn_proto/struct.PathStats.html @@ -0,0 +1,54 @@ +PathStats in iroh_quinn_proto - Rust

PathStats

Struct PathStats 

Source
#[non_exhaustive]
pub struct PathStats { + pub rtt: Duration, + pub cwnd: u64, + pub congestion_events: u64, + pub lost_packets: u64, + pub lost_bytes: u64, + pub sent_packets: 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)

+
§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_packets: u64

The amount of packets sent on this path

+
§sent_plpmtud_probes: u64

The amount of PLPMTUD probe packets sent on this path (also counted by sent_packets)

+
§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/299/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html b/pr/299/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html new file mode 100644 index 000000000..cd5ce3d70 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.QlogConfig.html b/pr/299/docs/iroh_quinn_proto/struct.QlogConfig.html new file mode 100644 index 000000000..72429d45c --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.QlogFileFactory.html b/pr/299/docs/iroh_quinn_proto/struct.QlogFileFactory.html new file mode 100644 index 000000000..edeeb554a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.RandomConnectionIdGenerator.html b/pr/299/docs/iroh_quinn_proto/struct.RandomConnectionIdGenerator.html new file mode 100644 index 000000000..c2385a7ba --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.RecvStream.html b/pr/299/docs/iroh_quinn_proto/struct.RecvStream.html new file mode 100644 index 000000000..2292e2fdd --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.RetryError.html b/pr/299/docs/iroh_quinn_proto/struct.RetryError.html new file mode 100644 index 000000000..0352a119d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.RttEstimator.html b/pr/299/docs/iroh_quinn_proto/struct.RttEstimator.html new file mode 100644 index 000000000..614452d5d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.SendStream.html b/pr/299/docs/iroh_quinn_proto/struct.SendStream.html new file mode 100644 index 000000000..1a6c79db1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.ServerConfig.html b/pr/299/docs/iroh_quinn_proto/struct.ServerConfig.html new file mode 100644 index 000000000..22c1eca09 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.ShouldTransmit.html b/pr/299/docs/iroh_quinn_proto/struct.ShouldTransmit.html new file mode 100644 index 000000000..4f1561520 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.StdSystemTime.html b/pr/299/docs/iroh_quinn_proto/struct.StdSystemTime.html new file mode 100644 index 000000000..4d4721e26 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.StreamId.html b/pr/299/docs/iroh_quinn_proto/struct.StreamId.html new file mode 100644 index 000000000..c3cb92a08 --- /dev/null +++ b/pr/299/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

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
Source§

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/299/docs/iroh_quinn_proto/struct.Streams.html b/pr/299/docs/iroh_quinn_proto/struct.Streams.html new file mode 100644 index 000000000..e9c4634d6 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.TokenMemoryCache.html b/pr/299/docs/iroh_quinn_proto/struct.TokenMemoryCache.html new file mode 100644 index 000000000..d826f700a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.TokenReuseError.html b/pr/299/docs/iroh_quinn_proto/struct.TokenReuseError.html new file mode 100644 index 000000000..f9f3a3702 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.Transmit.html b/pr/299/docs/iroh_quinn_proto/struct.Transmit.html new file mode 100644 index 000000000..bd722850a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.TransportConfig.html b/pr/299/docs/iroh_quinn_proto/struct.TransportConfig.html new file mode 100644 index 000000000..9a20088f7 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.TransportError.html b/pr/299/docs/iroh_quinn_proto/struct.TransportError.html new file mode 100644 index 000000000..c02bdd412 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.TransportErrorCode.html b/pr/299/docs/iroh_quinn_proto/struct.TransportErrorCode.html new file mode 100644 index 000000000..edb99b831 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.UdpStats.html b/pr/299/docs/iroh_quinn_proto/struct.UdpStats.html new file mode 100644 index 000000000..01764d80a --- /dev/null +++ b/pr/299/docs/iroh_quinn_proto/struct.UdpStats.html @@ -0,0 +1,36 @@ +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 Copy 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
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/299/docs/iroh_quinn_proto/struct.ValidationTokenConfig.html b/pr/299/docs/iroh_quinn_proto/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..3622887b9 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.VarInt.html b/pr/299/docs/iroh_quinn_proto/struct.VarInt.html new file mode 100644 index 000000000..4265325b3 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.VarIntBoundsExceeded.html b/pr/299/docs/iroh_quinn_proto/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..b70dd0785 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/struct.Written.html b/pr/299/docs/iroh_quinn_proto/struct.Written.html new file mode 100644 index 000000000..fb6ec6f22 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/token/struct.NoneTokenLog.html b/pr/299/docs/iroh_quinn_proto/token/struct.NoneTokenLog.html new file mode 100644 index 000000000..8b2bbfeeb --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/token/struct.NoneTokenStore.html b/pr/299/docs/iroh_quinn_proto/token/struct.NoneTokenStore.html new file mode 100644 index 000000000..db4ca3380 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/token/struct.TokenReuseError.html b/pr/299/docs/iroh_quinn_proto/token/struct.TokenReuseError.html new file mode 100644 index 000000000..9439eab73 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/token/trait.TokenLog.html b/pr/299/docs/iroh_quinn_proto/token/trait.TokenLog.html new file mode 100644 index 000000000..f296a9087 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/token/trait.TokenStore.html b/pr/299/docs/iroh_quinn_proto/token/trait.TokenStore.html new file mode 100644 index 000000000..a8e1acc4d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/token_memory_cache/struct.TokenMemoryCache.html b/pr/299/docs/iroh_quinn_proto/token_memory_cache/struct.TokenMemoryCache.html new file mode 100644 index 000000000..339118bbf --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/trait.ConnectionIdGenerator.html b/pr/299/docs/iroh_quinn_proto/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..12479fd74 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/trait.ConnectionIdParser.html b/pr/299/docs/iroh_quinn_proto/trait.ConnectionIdParser.html new file mode 100644 index 000000000..9503bd58a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/trait.QlogFactory.html b/pr/299/docs/iroh_quinn_proto/trait.QlogFactory.html new file mode 100644 index 000000000..2830f4fd3 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/trait.TimeSource.html b/pr/299/docs/iroh_quinn_proto/trait.TimeSource.html new file mode 100644 index 000000000..ad97fe27d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/trait.TokenLog.html b/pr/299/docs/iroh_quinn_proto/trait.TokenLog.html new file mode 100644 index 000000000..0a440a60d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/trait.TokenStore.html b/pr/299/docs/iroh_quinn_proto/trait.TokenStore.html new file mode 100644 index 000000000..af8cf71ba --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/transport_error/struct.Code.html b/pr/299/docs/iroh_quinn_proto/transport_error/struct.Code.html new file mode 100644 index 000000000..402d12b55 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/transport_error/struct.Error.html b/pr/299/docs/iroh_quinn_proto/transport_error/struct.Error.html new file mode 100644 index 000000000..a6bb9ca48 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/transport_parameters/enum.Error.html b/pr/299/docs/iroh_quinn_proto/transport_parameters/enum.Error.html new file mode 100644 index 000000000..a60d1a52d --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/transport_parameters/index.html b/pr/299/docs/iroh_quinn_proto/transport_parameters/index.html new file mode 100644 index 000000000..b8239f12a --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/transport_parameters/sidebar-items.js b/pr/299/docs/iroh_quinn_proto/transport_parameters/sidebar-items.js new file mode 100644 index 000000000..be0904c68 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/transport_parameters/struct.TransportParameters.html b/pr/299/docs/iroh_quinn_proto/transport_parameters/struct.TransportParameters.html new file mode 100644 index 000000000..854345bff --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/varint/struct.VarInt.html b/pr/299/docs/iroh_quinn_proto/varint/struct.VarInt.html new file mode 100644 index 000000000..678acb585 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_proto/varint/struct.VarIntBoundsExceeded.html b/pr/299/docs/iroh_quinn_proto/varint/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..95add4901 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_udp/all.html b/pr/299/docs/iroh_quinn_udp/all.html new file mode 100644 index 000000000..bf279c3f6 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_udp/constant.BATCH_SIZE.html b/pr/299/docs/iroh_quinn_udp/constant.BATCH_SIZE.html new file mode 100644 index 000000000..aeff14292 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_udp/enum.EcnCodepoint.html b/pr/299/docs/iroh_quinn_udp/enum.EcnCodepoint.html new file mode 100644 index 000000000..c130f9df1 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_udp/imp/struct.UdpSocketState.html b/pr/299/docs/iroh_quinn_udp/imp/struct.UdpSocketState.html new file mode 100644 index 000000000..ef7f29570 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_udp/index.html b/pr/299/docs/iroh_quinn_udp/index.html new file mode 100644 index 000000000..189d8f7f7 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_udp/sidebar-items.js b/pr/299/docs/iroh_quinn_udp/sidebar-items.js new file mode 100644 index 000000000..d1db6b942 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_udp/struct.RecvMeta.html b/pr/299/docs/iroh_quinn_udp/struct.RecvMeta.html new file mode 100644 index 000000000..db917c321 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_udp/struct.Transmit.html b/pr/299/docs/iroh_quinn_udp/struct.Transmit.html new file mode 100644 index 000000000..b0cf49ba4 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_udp/struct.UdpSockRef.html b/pr/299/docs/iroh_quinn_udp/struct.UdpSockRef.html new file mode 100644 index 000000000..046df30b2 --- /dev/null +++ b/pr/299/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/299/docs/iroh_quinn_udp/struct.UdpSocketState.html b/pr/299/docs/iroh_quinn_udp/struct.UdpSocketState.html new file mode 100644 index 000000000..0de55d35b --- /dev/null +++ b/pr/299/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/299/docs/perf/all.html b/pr/299/docs/perf/all.html new file mode 100644 index 000000000..e395c4e55 --- /dev/null +++ b/pr/299/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/299/docs/perf/client/fn.run.html b/pr/299/docs/perf/client/fn.run.html new file mode 100644 index 000000000..4e3af1a2f --- /dev/null +++ b/pr/299/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/299/docs/perf/client/index.html b/pr/299/docs/perf/client/index.html new file mode 100644 index 000000000..35b1a32ed --- /dev/null +++ b/pr/299/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/299/docs/perf/client/sidebar-items.js b/pr/299/docs/perf/client/sidebar-items.js new file mode 100644 index 000000000..3dceadfac --- /dev/null +++ b/pr/299/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/299/docs/perf/client/struct.Opt.html b/pr/299/docs/perf/client/struct.Opt.html new file mode 100644 index 000000000..d8ae2462b --- /dev/null +++ b/pr/299/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/299/docs/perf/enum.CongestionAlgorithm.html b/pr/299/docs/perf/enum.CongestionAlgorithm.html new file mode 100644 index 000000000..11f3251a3 --- /dev/null +++ b/pr/299/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/299/docs/perf/fn.parse_byte_size.html b/pr/299/docs/perf/fn.parse_byte_size.html new file mode 100644 index 000000000..89aa88fa3 --- /dev/null +++ b/pr/299/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/299/docs/perf/index.html b/pr/299/docs/perf/index.html new file mode 100644 index 000000000..7a3b1be19 --- /dev/null +++ b/pr/299/docs/perf/index.html @@ -0,0 +1 @@ +perf - Rust

Crate perf

\ No newline at end of file diff --git a/pr/299/docs/perf/noprotection/index.html b/pr/299/docs/perf/noprotection/index.html new file mode 100644 index 000000000..36fb17ad6 --- /dev/null +++ b/pr/299/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/299/docs/perf/noprotection/sidebar-items.js b/pr/299/docs/perf/noprotection/sidebar-items.js new file mode 100644 index 000000000..7ed1562e0 --- /dev/null +++ b/pr/299/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/299/docs/perf/noprotection/struct.NoProtectionClientConfig.html b/pr/299/docs/perf/noprotection/struct.NoProtectionClientConfig.html new file mode 100644 index 000000000..c08f386b2 --- /dev/null +++ b/pr/299/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/299/docs/perf/noprotection/struct.NoProtectionServerConfig.html b/pr/299/docs/perf/noprotection/struct.NoProtectionServerConfig.html new file mode 100644 index 000000000..b48da3739 --- /dev/null +++ b/pr/299/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/299/docs/perf/server/fn.run.html b/pr/299/docs/perf/server/fn.run.html new file mode 100644 index 000000000..d244218b7 --- /dev/null +++ b/pr/299/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/299/docs/perf/server/index.html b/pr/299/docs/perf/server/index.html new file mode 100644 index 000000000..88db7d540 --- /dev/null +++ b/pr/299/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/299/docs/perf/server/sidebar-items.js b/pr/299/docs/perf/server/sidebar-items.js new file mode 100644 index 000000000..3dceadfac --- /dev/null +++ b/pr/299/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/299/docs/perf/server/struct.Opt.html b/pr/299/docs/perf/server/struct.Opt.html new file mode 100644 index 000000000..b12408eff --- /dev/null +++ b/pr/299/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/299/docs/perf/sidebar-items.js b/pr/299/docs/perf/sidebar-items.js new file mode 100644 index 000000000..317d83630 --- /dev/null +++ b/pr/299/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/299/docs/perf/static.PERF_CIPHER_SUITES.html b/pr/299/docs/perf/static.PERF_CIPHER_SUITES.html new file mode 100644 index 000000000..4e1443460 --- /dev/null +++ b/pr/299/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/299/docs/perf/stats/index.html b/pr/299/docs/perf/stats/index.html new file mode 100644 index 000000000..15fd04ede --- /dev/null +++ b/pr/299/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/299/docs/perf/stats/sidebar-items.js b/pr/299/docs/perf/stats/sidebar-items.js new file mode 100644 index 000000000..5c7b90d68 --- /dev/null +++ b/pr/299/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/299/docs/perf/stats/struct.OpenStreamStats.html b/pr/299/docs/perf/stats/struct.OpenStreamStats.html new file mode 100644 index 000000000..bdf2eb565 --- /dev/null +++ b/pr/299/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/299/docs/perf/stats/struct.Stats.html b/pr/299/docs/perf/stats/struct.Stats.html new file mode 100644 index 000000000..ba457be9b --- /dev/null +++ b/pr/299/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/299/docs/perf/stats/struct.StreamStats.html b/pr/299/docs/perf/stats/struct.StreamStats.html new file mode 100644 index 000000000..ebf766fcc --- /dev/null +++ b/pr/299/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/299/docs/perf/struct.CommonOpt.html b/pr/299/docs/perf/struct.CommonOpt.html new file mode 100644 index 000000000..ff111011d --- /dev/null +++ b/pr/299/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/299/docs/quinn_perf/all.html b/pr/299/docs/quinn_perf/all.html new file mode 100644 index 000000000..0c76accc7 --- /dev/null +++ b/pr/299/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/299/docs/quinn_perf/enum.Commands.html b/pr/299/docs/quinn_perf/enum.Commands.html new file mode 100644 index 000000000..ba7c9e146 --- /dev/null +++ b/pr/299/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/299/docs/quinn_perf/fn.main.html b/pr/299/docs/quinn_perf/fn.main.html new file mode 100644 index 000000000..d02fe8057 --- /dev/null +++ b/pr/299/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/299/docs/quinn_perf/index.html b/pr/299/docs/quinn_perf/index.html new file mode 100644 index 000000000..520445aa4 --- /dev/null +++ b/pr/299/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/299/docs/quinn_perf/sidebar-items.js b/pr/299/docs/quinn_perf/sidebar-items.js new file mode 100644 index 000000000..9a5c272f9 --- /dev/null +++ b/pr/299/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/299/docs/quinn_perf/struct.Cli.html b/pr/299/docs/quinn_perf/struct.Cli.html new file mode 100644 index 000000000..a634bf9f3 --- /dev/null +++ b/pr/299/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/299/docs/search.index/00c07f35c6ce.js b/pr/299/docs/search.index/00c07f35c6ce.js new file mode 100644 index 000000000..63033b1b5 --- /dev/null +++ b/pr/299/docs/search.index/00c07f35c6ce.js @@ -0,0 +1 @@ +rn_("AQ0A9c8SAAABAAEAcQABAAECAPQxEAAAAQB1AQEAAQMA9JwQAAABAFoBAQDzAAJidQFNDwAAAQMA9C8QAAABAHUBAQDzhAJkcwECAPMcCQAAAQBUAPOAAml0ARgA86YTAAABAAEAAYACsLASzQABoMAAABJ6ZXXzgQJjbQEFAaBgAAARzHL03QsAAAEAbQMBAAEJAPb1EQAAAQDqAIAAMAABABOAArBgD/AAAWRuAQMBoHAAABIoZ/SEBwAAAQDqAQEAAQkA82UTAAABADUAAYECsIATcwABoGAAABOXZHMXAQCJhbDQE5kAAaBAAAATSbBgExsATQKQCmdeAACLsHAQVAABsLASNgABoKAAABH0sPAS/QABbYEP9HMAAAABAAEAAQA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/01806ef5b5d6.js b/pr/299/docs/search.index/01806ef5b5d6.js new file mode 100644 index 000000000..16ac96cb5 --- /dev/null +++ b/pr/299/docs/search.index/01806ef5b5d6.js @@ -0,0 +1 @@ +rn_("AYQCsCAQqwEFoJAAABMTbXABgAKgMAAAEDegUAAAEr1jcpcAAACFsCASjQABsMATRAABoGAAABECEEAOOzAAAAEAABIABgApBQgAABEBAP0RAQDaEgEALBMBAJwTAQA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/01d48ddd05cf.js b/pr/299/docs/search.index/01d48ddd05cf.js new file mode 100644 index 000000000..d42dba854 --- /dev/null +++ b/pr/299/docs/search.index/01d48ddd05cf.js @@ -0,0 +1 @@ +rn_("AQQBoFAAABNGcwGqEQAAAYICsEARzQABsEARzwABNDYBBgD0lRMAAAEACgABAPMCAXIBABMAAPMDAWX0rg8AAAEAqAABAFOEA7CwExwAAWFlaWOAA6BQAAATfGNldA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/01f5bffda89c.js b/pr/299/docs/search.index/01f5bffda89c.js new file mode 100644 index 000000000..4ec2a0c22 --- /dev/null +++ b/pr/299/docs/search.index/01f5bffda89c.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwDsEAIAcRIBAAMTAgABBADz7xAAAAEAAQAjAAKgYAAAEf9hcvMWEAAAAQCrAQGAAqEQAAAThbCAE30AAWl1NwQAgYexUBOgAAGACScAFs6wcBLNAAGhMAAAE5QKQBsjgAKgMAAAEaRudg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/02f80e846705.js b/pr/299/docs/search.index/02f80e846705.js new file mode 100644 index 000000000..329dbb0e2 --- /dev/null +++ b/pr/299/docs/search.index/02f80e846705.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAABYACQCLDwUAkxABABwRAgDWEQEABBIEAPASAQATEwAAQBMAAF8TAAAjgAOh8AAAE6mw8BOcAAFjZmcjgAKwUBKuAAFtbgEFAPPwEgAAAQBuAPMEAWMBsxAAAAEBAbBwE0QAAXA7MAAAAQAACQACAPwHBwD5DwEAAQIAOzAAAAEAAAsAAgDDEgUAdRMFAPMBAW07MAAAAQAADQAEAHkMAACVEAAA7REFADYTBQATAAKgMAAADH1lc/IgEQAAAQABggKwcBMkAAGgQAAAED9pc/cBAICGsJATCQABkEkI") \ No newline at end of file diff --git a/pr/299/docs/search.index/030aa7192f47.js b/pr/299/docs/search.index/030aa7192f47.js new file mode 100644 index 000000000..5c82d3ffe --- /dev/null +++ b/pr/299/docs/search.index/030aa7192f47.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAYAAgABBgUArBAAAAEEAbAAEiQAAXM7MAAAAQAACwACAJgSBwCBEwMARwAAAYaggAAAEnmxABOOAAGwQBHnAAGwgBOcAAGwUBImAAEVABb0Aw4AAAEAAQABAAEFAPM0DwAAAQABAGOAA7BAD7cAAW1ubwGBArAADIsAAbAQD7UAAXN18wACYWk7MAAAAQAACQAEAJAHAAAXDwAA7A8BAIYSBQABgAOwABLVAAGwABLXAAGwcBNHAAE0NmEBBAD0lRMAAAEACgABACOBAqBAAAAQhm1wAQIAOzAAAAEAAA4ABACTBwEAqwwBAHYQCQC0EAAAAQEAOzAAAAEAAAgAAwDcEQAARRIAAKITBgABAQA7MAAAAQAACgADAJkQAgCwEgMAihMDACOAAqAwAAAPimRn1wMAAMagYAAAEvpARYAIOzAAAAEAABgACQArDwEAPRABAHIQAQCCEAMA8hAGALQSAgAAEwAAVxMBAIYTAQABAwD2KgkAAFAANgYBALEAAQATgAKwcBMoAAFvcgEEAbCwE24AAWz0qQwAAAEA0QIBAAECADswAAABAAAMAAMACQkAAMMSBQB1EwUAAQEA83EPAAABADcCI4ACoLAAABMnZ3oBAgA6MAAAAQAAAAAACQAQAAAAABEBEf0R/hHaEtsSLBMtE5wTnRPzAAFv8n0JAAABAAEFAPQeDwAAAQDhAAEAgwCEoEAAAAyfsHASaAABsCAMKwABEAUCOzAAAAEAAAkAAwAaBQMAyAUCAO8QAgABgAKgIAAAEEywIBMoAAFkdAGEArCwE3EAAbAAEs0AAWFz04CEoLAAABNbQCgEAYACoDAAABEYsGAPMgABaHJH/wEAzaAgAAAF5qAgAAAF57BwEsEAAQNnNAc7MAAAAQAANwAYAP4EAAAyBQUAcwkAAHkMAADuDgEA2A8DAJUQAADcEQAA7REFAEUSAQBnEgAAbhIAAIUSAACWEgEAoBIAAKISAgCmEgEA+BIAAP8SAAALEwEAJBMBADYTBQBKEwAAohMGAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/030abe455d20.js b/pr/299/docs/search.index/030abe455d20.js new file mode 100644 index 000000000..8d3ecf49e --- /dev/null +++ b/pr/299/docs/search.index/030abe455d20.js @@ -0,0 +1 @@ +rn_("AQIA9dQQAAABAAEAAQABAAGAArBgEhYAAaAgAAAQJ295AQABoMAAABMnafRnEgAABwAXABsAAQMAOzAAAAEAAAUAAQCGEgUAAQYA9IoTAAABAAEAAQABAgD0JRAAAAEAmwABAAECAaBAAAASP3I7MAAAAQAABwACACMQAQC7EAUA84ACYW9nBwAAh7CAEj4AVbCgEq4AAQwwBzswAAABAAAOAAQAJAQFANgLAADnEAMAthEDAKOAhKDwAAASvqBQAAAPgpQBAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/035c9d3c036b.js b/pr/299/docs/search.index/035c9d3c036b.js new file mode 100644 index 000000000..985c921ac --- /dev/null +++ b/pr/299/docs/search.index/035c9d3c036b.js @@ -0,0 +1 @@ +rn_("AQACoDAAABBDoJAAABKFMGj1IhEAAG0BAQABAAEAE4ACoDAAAA8GZHIjAISwEAkIAuiwIAvEAO6gMAAADyoSABj14wUAAC4AAQABAAEAAQIA9JUTAAABAAoAAQABAgDzoA8AAAEAAQDzgAJpcgEBArBAEIkAAaBQAAASb2Vz9AIOAACcAgEAWgIBCwD2lBIAAAEAoABTABYAAQDzgAJsdNOAhKCQAAASSQFEBA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/04219d6c5f1f.js b/pr/299/docs/search.index/04219d6c5f1f.js new file mode 100644 index 000000000..1dc778fb3 --- /dev/null +++ b/pr/299/docs/search.index/04219d6c5f1f.js @@ -0,0 +1 @@ +rn_("AQMBsEAQiQABZQECDgAAAQUAOzAAAAEAAAYAAQCRDwYAAQMA9H4MAAABAAEAAQDzAAJwczowAAABAAAAAAAOABAAAADvBfAF8QXzBiIJEQwdDCsQLBAtEC4QvxHAERASERIzAAOwUA8KAMRkcHI7MAAAAQAADwAGAAIFAACwBQMA9g4AAAQRAwC6EQMA6REBAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/045e5265a54f.js b/pr/299/docs/search.index/045e5265a54f.js new file mode 100644 index 000000000..972497ccf --- /dev/null +++ b/pr/299/docs/search.index/045e5265a54f.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABEZZfOoDAAAmAMBAAEGAPQ9EAAAAQC5AAEAE4EDoAAAABCGsSAThgABYWRuARQA9aQTAAABAAEAAQABAAEGAbCAE0cAAXY7MAAAAQAABwACAPIQBAC0EgIAVwAAAYagwAAAEn+goAAAEqGgkAAAEm+wUA+1AAEF4AL0QgcAAAEAAQA3DAEGAPPaEQAAAQCaAPOAA3J0dgEDADswAAABAAAHAAIAsBIDAIoTAwDzAAFzAUoTAADzAAFzARgRAABnAQCAhqAwAAAL+rAAEmoAAaAQAAAMkBJADg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/04786c4690da.js b/pr/299/docs/search.index/04786c4690da.js new file mode 100644 index 000000000..7262a982c --- /dev/null +++ b/pr/299/docs/search.index/04786c4690da.js @@ -0,0 +1 @@ +rn_("AQIBsIASRwABdvR3CQAAAQB2CQEA8wABYwHzEQAAAQICoLAAABNAoLAAABNBY3Pz9w8AAAEAXgABgQKwUBEUAAGgwAAAExFoafOCAmVp84ACZW8=") \ No newline at end of file diff --git a/pr/299/docs/search.index/054086f1f577.js b/pr/299/docs/search.index/054086f1f577.js new file mode 100644 index 000000000..1da5a2a67 --- /dev/null +++ b/pr/299/docs/search.index/054086f1f577.js @@ -0,0 +1 @@ +rn_("AQQBsAASJAABczswAAABAAALAAIAmBIHAIETAwBHAAAFhqCAAAASebEAE44AAbBAEecAAbCAE5wAAbBQEiYAARUAFvQDDgAAAQABAAEAQwCEoCAAAAk8oNAAABKEoDAAAAkbFQAE88cBAAABANQF") \ No newline at end of file diff --git a/pr/299/docs/search.index/05f4c9420eb7.js b/pr/299/docs/search.index/05f4c9420eb7.js new file mode 100644 index 000000000..102e06240 --- /dev/null +++ b/pr/299/docs/search.index/05f4c9420eb7.js @@ -0,0 +1 @@ +rn_("AQUA9NERAAABAJoAAQABCAD0fRIAAAEAgwABADMAA7BAElIAAWFiZTowAAABAAAAAAAJABAAAABdB14HmAyZDAEPbA+1D7YPrRFvEg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/0615a2bf5050.js b/pr/299/docs/search.index/0615a2bf5050.js new file mode 100644 index 000000000..39844b80e --- /dev/null +++ b/pr/299/docs/search.index/0615a2bf5050.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAANYAAgDNCwAAtQzVAPMAAW07MAAAAQAA1gACAOkFAAAVBtUAAQMAOzAAAAEAAAcAAgDfDgUAVhIBAPMAAWM7MAAAAQAAVwAmAAgFCwC3BQEAHgkBAOMLAADpCwIAlAwAAJ8MAgCwDAEABg8AAAsPAQAODwEAFA8AAJgPAgCeDwEAow8BAMIPAAA2EAAAPBAAAIkQAQCWEAEAqRABAK0QAgAIEQEAGRECAB8RAAC+EQAAxREBAMoRAQAOEgEAGhIDADwSAQBMEgMAUhIBAKwSAQC9EgAAJhMAAEsTBABUEwIA84ACcncBCQDzZRMAAAEANQABDAGgoAAAE6l38pkTAAABAAEBAPMvDwAAAQABAPOAAnR1AYECsIATcwABoGAAABOXZHM3AgCBhqBAAAATSbBgExsATbAwEwkAAQKQDgGIArAAEtUAAbAAEtcAATQ2M4ADoDAAABCsZG5zYwADoBAAAAUnYWVvAXIAAAA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/07b12fe46ce8.js b/pr/299/docs/search.index/07b12fe46ce8.js new file mode 100644 index 000000000..3d079a1e2 --- /dev/null +++ b/pr/299/docs/search.index/07b12fe46ce8.js @@ -0,0 +1 @@ +rn_("AQECoCAAABD/oDAAABGyYmgBBA8AAAEOAPQiEwAAAQBGAAEAAQMBoKAAABOpd/KZEwAAAQCzAISwQA+FAAEBCAnyCAgAAAEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/07bca17b1c6a.js b/pr/299/docs/search.index/07bca17b1c6a.js new file mode 100644 index 000000000..24640c29e --- /dev/null +++ b/pr/299/docs/search.index/07bca17b1c6a.js @@ -0,0 +1 @@ +rn_("AYMCsCAQqwEFoJAAABMTbXABggKwoBMJAAGwoBMLAAFjcgECAaBAAAAQqmUBbg8AADOAhLAgECgAAbAgDvEAAVAUAPOAAm9y") \ No newline at end of file diff --git a/pr/299/docs/search.index/081380447331.js b/pr/299/docs/search.index/081380447331.js new file mode 100644 index 000000000..f2ec3699e --- /dev/null +++ b/pr/299/docs/search.index/081380447331.js @@ -0,0 +1 @@ +rn_("AQQA9q0MAAABAM8CAQAiAQEAAQoA89oRAAABAJoAAQEA9zAJAAABAHsGAQBrAAEADgEjgAKwYBLeAAFpbwECAPRxDAAAAQCkBQEAAQUA9PQSAAABAEcAAQD3CAAAiLCQEq4AAaAgAAAMc7CQERoAAQTcAzowAAABAAAAAAAIABAAAADcBSAJIQkbDBwMKBApEAkSChI=") \ No newline at end of file diff --git a/pr/299/docs/search.index/0933b5050388.js b/pr/299/docs/search.index/0933b5050388.js new file mode 100644 index 000000000..851a30844 --- /dev/null +++ b/pr/299/docs/search.index/0933b5050388.js @@ -0,0 +1 @@ +rn_("AQUA9KkMAAABANECAQABAAD0GAwAAAEAAQBgAAECADowAAABAAAAAAAJABAAAAAVDxYP/Q+6EPUR9hHgEmATkBORE8cBAACGsDAJLQABoEAAABEZsIARAAABGEAHOjAAAAEAAAAAABcAEAAAAJEHkgcdCDkJaAlpCagMAg8DDyIPww/KD8sPzA/NDwgQCRBAEEEQnxHeEt8SCRMKEw==") \ No newline at end of file diff --git a/pr/299/docs/search.index/096a32f3fb06.js b/pr/299/docs/search.index/096a32f3fb06.js new file mode 100644 index 000000000..06b8b93a7 --- /dev/null +++ b/pr/299/docs/search.index/096a32f3fb06.js @@ -0,0 +1 @@ +rn_("AYACoCAAAA79oDAAABHzZXIBAAKgIAAAEoSwMBEMAAFpdPL5EQAAAQABAwA7MAAAAQAABgABAIwQBgABCAD06xIAAAEAfwAFABMBA7CQE0sAAbBQEjoBT2dscDswAAABAAASAAkAmw8CAA8RAQApEgEAShIBAHsSAQDcEgEADxMBAB4TAQBdEwEAAYACsDARCAABsDARCgABcnYBBAGwABG/AAFkOzAAAAEAAAUAAgAEEQMA6REBALcFAAGHsFARwQD9sGASOAABlAkDOzAAAAEAAAoABQCEDAIACA4BADMQAAC2EAMAMBIAACOAAqAQAAAMg25vAQMA85gPAAABAAEAI4ACoAAAAAvPZHJjAYSgUAAAELOgMAAAD88UARDywQcAAAEAE4ADsGAR3gABsAAO5wABY2dyVwAAAIWgsAAAEwigQAAADgegIAAAENMQwAoB6Q4AAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/09c6ace039aa.js b/pr/299/docs/search.index/09c6ace039aa.js new file mode 100644 index 000000000..0d96cdf22 --- /dev/null +++ b/pr/299/docs/search.index/09c6ace039aa.js @@ -0,0 +1 @@ +rn_("AQIA9CASAAABAIkAAQABAwD0VBIAAAEAjgABAPMCAmJ1OjAAAAEAAAAAABMAEAAAAHMHdAd1BxIPEw+yD7MP0Q/SD2QQZRASERMRHhIfEisSLBI7EqgSqRI=") \ No newline at end of file diff --git a/pr/299/docs/search.index/0aebd591215a.js b/pr/299/docs/search.index/0aebd591215a.js new file mode 100644 index 000000000..24412bcf9 --- /dev/null +++ b/pr/299/docs/search.index/0aebd591215a.js @@ -0,0 +1 @@ +rn_("AQABoCAAAAyQbQHNBQAAAQQBsAASEAABZPS6EQAAAQABAAEAI4SEsKATPgABsEASDgABsHAS7gABFYAAM4ADsNATLAABYWVp") \ No newline at end of file diff --git a/pr/299/docs/search.index/0b9da5e00812.js b/pr/299/docs/search.index/0b9da5e00812.js new file mode 100644 index 000000000..20e0f009e --- /dev/null +++ b/pr/299/docs/search.index/0b9da5e00812.js @@ -0,0 +1 @@ +rn_("AQYA9LYQAAABAAEAAQABAgA7MAAAAQAADAAFAMsLAQC6DwUA2BEBAC4SAQBBEwAA84ACZm4BAQA7MAAAAQAABwACAN8OBQBWEgEA84ACZG4BAQDzWgcAAAEAAQABhgKgAAAAElCgAAAAElFkcvMBAXYBdhIAAEMAA7CAEesAAbCwE24AAWtscjswAAABAAAMAAMAUQcIAKkMAQB7DwEAAQIA9R4IAAABAAEAAQABAPMAhBRBAAFpAAAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/0d24876322d0.js b/pr/299/docs/search.index/0d24876322d0.js new file mode 100644 index 000000000..832460a70 --- /dev/null +++ b/pr/299/docs/search.index/0d24876322d0.js @@ -0,0 +1 @@ +rn_("AQEA90kQAAABAAEAoAIBAH8ABQABAgD03A8AAAEAAQABABOAAqDQAAATW2Nz84ACZW8jgQKwAAd7AAFsdAEAAPT5BQAAAQABAAEAU4ADoFAAABOpZG5y") \ No newline at end of file diff --git a/pr/299/docs/search.index/0d377ac14863.js b/pr/299/docs/search.index/0d377ac14863.js new file mode 100644 index 000000000..cfe321ec0 --- /dev/null +++ b/pr/299/docs/search.index/0d377ac14863.js @@ -0,0 +1 @@ +rn_("AQcA9CMRAAABAAEAAQAjAAKgcAAAESJhcgGhBwAAAQAA9kcQAAABAE0DAQAKAAEAAQAAOzAAAAEAAAYAAgCAEgAA5RIFACOIArBAE1QAAWVzAQ0BsEATTgABZfUUEwAAAQABAAEAAQABBQA7MAAAAQAACAADABwRAgAEEgQAExMAANcDAACGeJvYCPEYFIQG9SAFAAABAFMHAQABAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/0d5508bf0207.js b/pr/299/docs/search.index/0d5508bf0207.js new file mode 100644 index 000000000..df8240998 --- /dev/null +++ b/pr/299/docs/search.index/0d5508bf0207.js @@ -0,0 +1 @@ +rn_("AQACoDAAABBDoJAAABKFMGj1IhEAAG0BAQABAAEAE4ACoDAAAA8GZHIjAYSwEAkIAuiwIAvEAO6gMAAADyoSABj14wUAAC4AAQABAAEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/0dd31cbab4cc.js b/pr/299/docs/search.index/0dd31cbab4cc.js new file mode 100644 index 000000000..dad532e22 --- /dev/null +++ b/pr/299/docs/search.index/0dd31cbab4cc.js @@ -0,0 +1 @@ +rn_("AYkCsCATWQABoDAAABNiaXATAAOgIAAAEiiggAAAEjtlaXX10QsAADIAAQABAAEAAYYCsJATfQABoAAAABHCYmQBAgD01gsAAAEADgMBAAEAADswAAABAAAGAAIAgBIAAOUSBQAjgQKwQBNUAAFlc1OAA7AACUYAAWV4eQGAArAgEhwAAbAACUQAAXJ4pwAAAMWwwBKsAAGwQAyLAAGgMAAAEB8ABIAL87kFAAABANcGcwCEoDAAABJwEAATOjAAAAEAAAAAAAoAEAAAAAwJDQkOCT4JPwnbC7QPuQ9wEHEQ/xE=") \ No newline at end of file diff --git a/pr/299/docs/search.index/0e61017bc68b.js b/pr/299/docs/search.index/0e61017bc68b.js new file mode 100644 index 000000000..75e128870 --- /dev/null +++ b/pr/299/docs/search.index/0e61017bc68b.js @@ -0,0 +1 @@ +rn_("AQIA9QgRAAABAAUBAQBHAQEBAPdJEAAAAQABAKACAQB/AAUAAQIA9NwPAAABAAEAAQATgAKg0AAAE1tjc/OAAmVvI4ACsAAHewABbHTzAAJhcvLnDgAAAQA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/0fa74a34d33c.js b/pr/299/docs/search.index/0fa74a34d33c.js new file mode 100644 index 000000000..e2de71d3a --- /dev/null +++ b/pr/299/docs/search.index/0fa74a34d33c.js @@ -0,0 +1 @@ +rn_("AQADoKAAABNBoCAAABKEsDARDAABZWl08vkRAAABAAEDADswAAABAAAGAAEAjBAGAAEIAPTrEgAAAQB/AAUAEwEDsJATSwABsFASOgFPZ2xwOzAAAAEAABIACQCbDwIADxEBACkSAQBKEgEAexIBANwSAQAPEwEAHhMBAF0TAQABgAKwMBEIAAGwMBEKAAFydgECAbAAEb8AAWQ7MAAAAQAABQACAAQRAwDpEQEAI4ECoJAAABNAaW8BBwD0XBMAAAsAHgAPAHcWAACJsFAS+wABsFARwQD9sGASOAABtQkDOjAAAAEAAAAAAA0AEAAAAIQMhQyGDAgOCQ73D/gPMxBWELYQtxC4ELkQMBI=") \ No newline at end of file diff --git a/pr/299/docs/search.index/103f1ba9c2d5.js b/pr/299/docs/search.index/103f1ba9c2d5.js new file mode 100644 index 000000000..797424c92 --- /dev/null +++ b/pr/299/docs/search.index/103f1ba9c2d5.js @@ -0,0 +1 @@ +rn_("AYICsCAQbgABsDAQ5QABZXABBQD0HA8AAAEA4QABAFOAA7CQERYAAWFpcgGBArCgEwkAAbCgEwsAAWNyxwAAAIWwUAyaAAGgIAAACXmgIAAAC9s8AAE6MAAAAQAAAAAACgAQAAAAfQF+AQoEKgcwCTEJrA+tDxgQGRAnEQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/10e19c9358f8.js b/pr/299/docs/search.index/10e19c9358f8.js new file mode 100644 index 000000000..416058e06 --- /dev/null +++ b/pr/299/docs/search.index/10e19c9358f8.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABJGc/QIDwAArwMBAAIAAQECsGATKgABsEATYwABZHQ7MAAAAQAADQAGAAASAwBjEgEAyhIBAOESAQBZEwEAfRMBAAEBhLBwESAAAbCAEd4AAaBgAAASeoAScwAQ7QAwAzswAAABAAALAAIAAgkFAFgSBQCjgISgsAAAErmxcBOcAAEIQIIBAISgcAAAEamgcAAAEDaxABMqAAGwABI6AU8CIQHywQUAAAEAAQEAOzAAAAEAAAYAAQCiEwYA8wYBZfRXEwAAAQAuAAEAEwEDsEATfwABoGAAABL/YXJ1AaoPAAB3BAAAh6AQAAAF9LAwEi0BNaBAAAAQWRHBDDswAAABAAAGAAIAgBIAAOUSBQA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/1198e3f6eeb3.js b/pr/299/docs/search.index/1198e3f6eeb3.js new file mode 100644 index 000000000..b501b703f --- /dev/null +++ b/pr/299/docs/search.index/1198e3f6eeb3.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAHcAAgCbBwAAfwl2AAGEArBwEyQAAaBAAAAQP2lzAQcA8xgQAAABAA4BI4ACoLAAABHcYXQBAAOwMAkyAAGwMBKNAAGgIAAAB5xhZG8B2wEAADcDAICGoMAAABMasAAS8gABAJgV") \ No newline at end of file diff --git a/pr/299/docs/search.index/125b8dc7d658.js b/pr/299/docs/search.index/125b8dc7d658.js new file mode 100644 index 000000000..c243fb2c3 --- /dev/null +++ b/pr/299/docs/search.index/125b8dc7d658.js @@ -0,0 +1 @@ +rn_("AQABsIAS2gABdAEkCQAAAQkA89MRAAABAAEA84ACY2cBgwKgkAAAEyCgkAAAEyEyMwGAAqBQAAATSqCAAAASdmNl8wACc3TyZwcAAAEAAQABoLAAABK9YvVREgAA0QABAEYAAQAjgAKgAAAAElBkciOAAqBQAAATEmNl84ACaXkBBgD1ZhAAAAEAAQABAAEAAQEBsEATTgABZfUUEwAAAQABAAEAAQD3AACAhaAQAAAL0ClgAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/1344897c571c.js b/pr/299/docs/search.index/1344897c571c.js new file mode 100644 index 000000000..171da9503 --- /dev/null +++ b/pr/299/docs/search.index/1344897c571c.js @@ -0,0 +1 @@ +rn_("AQIA9CASAAABAIkAAQABAgD0JRAAAAEAmwABAAEDAPRUEgAAAQCOAAEAAQIBoEAAABI/cjswAAABAAAHAAIAIxABALsQBQDzAIQDQAg6MAAAAQAAAAAAFQAQAAAAcwd0B3UHEg8TD7IPsw/RD9IPZBBlEBIRExEeEh8SKxIsEjsSZRJmEqgSqRI=") \ No newline at end of file diff --git a/pr/299/docs/search.index/1499361f517d.js b/pr/299/docs/search.index/1499361f517d.js new file mode 100644 index 000000000..57ad35af1 --- /dev/null +++ b/pr/299/docs/search.index/1499361f517d.js @@ -0,0 +1 @@ +rn_("AQgA9KYPAAABAKkAAQABAAGggAAAE5hm8gsJAACBCQEHAPVeEgAAAQABAAEAAQAjgAKgUAAAEIthb/MCAmZzOzAAAAEAAAgABAB2BwAApwcFAKcMAADDEAAAM4ADoEAAAAvOYWVv") \ No newline at end of file diff --git a/pr/299/docs/search.index/1566e3c31611.js b/pr/299/docs/search.index/1566e3c31611.js new file mode 100644 index 000000000..157f4338d --- /dev/null +++ b/pr/299/docs/search.index/1566e3c31611.js @@ -0,0 +1 @@ +rn_("AQwBoKAAABOpd/KZEwAAAQABAQDzLw8AAAEAAQABiYSggAAAE5egQAAAE0mgYAAAE2igkAAAE5sCgAoTgYSgoAAAEuCxoBOnAAGw0BNCAAEEAQnzgANhdHU=") \ No newline at end of file diff --git a/pr/299/docs/search.index/16681f81c6a9.js b/pr/299/docs/search.index/16681f81c6a9.js new file mode 100644 index 000000000..96ab257f7 --- /dev/null +++ b/pr/299/docs/search.index/16681f81c6a9.js @@ -0,0 +1 @@ +rn_("AQIBsEATYwABdDswAAABAAANAAYAABIDAGMSAQDKEgEA4RIBAFkTAQB9EwEAE4ACsDASLQE1aXABBQDzohIAAAEAAQATgAKgMAAAEvhleVMCA7BwEwYAAWJlcwFzDwAAEwACoCAAAAXqZm32bgAAACAHAQC5AQEAIgc=") \ No newline at end of file diff --git a/pr/299/docs/search.index/16f90e0495ca.js b/pr/299/docs/search.index/16f90e0495ca.js new file mode 100644 index 000000000..cf72b1f02 --- /dev/null +++ b/pr/299/docs/search.index/16f90e0495ca.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAUAAQDgEQUAAQACsEASrAABoKAAABMzZXI6MAAAAQAAAAAABwAQAAAA9Ab1BooHiwcjDyQPDBANEFOAA6CgAAATEWdpcw==") \ No newline at end of file diff --git a/pr/299/docs/search.index/17747f0de527.js b/pr/299/docs/search.index/17747f0de527.js new file mode 100644 index 000000000..4ab4e6bbb --- /dev/null +++ b/pr/299/docs/search.index/17747f0de527.js @@ -0,0 +1 @@ +rn_("AYECoBAAABJAoHAAABKBYWMBhwKwABLVAAGwABLXAAE0NlMAhKCQAAASZ7CwEvsAAYEACfVQBwAAMwBBCA4BGQA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/182d04459856.js b/pr/299/docs/search.index/182d04459856.js new file mode 100644 index 000000000..9d910131f --- /dev/null +++ b/pr/299/docs/search.index/182d04459856.js @@ -0,0 +1 @@ +rn_("AQsA9JYSAAABAA8AAQABBAGwQBJSAAFlAa0RAADzAAF0AUkTAAABDQDzYBMAADAAAQABEADziBMAABYAAQDzgQNrbXMBAAA7MAAAAQAABgACAIASAADlEgUAI4YCsEATVAABZXMBBAD07g8AAAEAFwABAPOAA2NydDOAhLBgEAoAAaAQAAAF/gEICQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/18e4b0fb83df.js b/pr/299/docs/search.index/18e4b0fb83df.js new file mode 100644 index 000000000..0d451aeb0 --- /dev/null +++ b/pr/299/docs/search.index/18e4b0fb83df.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgDfEAMAxxECAAEKAbBAE04AAWX1FBMAAAEAAQABAAEA8wACaXP04AsAAAEASgABACOBAqDgAAATNWV5") \ No newline at end of file diff --git a/pr/299/docs/search.index/19c9a628ac91.js b/pr/299/docs/search.index/19c9a628ac91.js new file mode 100644 index 000000000..908fd1d4b --- /dev/null +++ b/pr/299/docs/search.index/19c9a628ac91.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABNNZfJsEwAAAQABgAOgMAAAEauw4BMsAAGwkBNuAAFhY22HAQCAhrAQEFQAAbAQEHQAAaCgAAATW6DwAAATezFJAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/19f55295ffb7.js b/pr/299/docs/search.index/19f55295ffb7.js new file mode 100644 index 000000000..f340452b9 --- /dev/null +++ b/pr/299/docs/search.index/19f55295ffb7.js @@ -0,0 +1 @@ +rn_("AQMA85gPAAABAAEAAYACoLAAABMRsDASagABbnRDAAOgIAAAEEKwUBEUAAFlaGk7MAAAAQAABgADAM8LAAAjEQMAwRIBAPOAAmRy") \ No newline at end of file diff --git a/pr/299/docs/search.index/1a01681e7d77.js b/pr/299/docs/search.index/1a01681e7d77.js new file mode 100644 index 000000000..601b840e9 --- /dev/null +++ b/pr/299/docs/search.index/1a01681e7d77.js @@ -0,0 +1 @@ +rn_("AQkA82UTAAABADUAAQ0BoKAAABOpd/KZEwAAAQABgQKwgBNzAAGgYAAAE5dkczcBAIaFoEAAABNJsGATGwBNApAK") \ No newline at end of file diff --git a/pr/299/docs/search.index/1b6ac9e761aa.js b/pr/299/docs/search.index/1b6ac9e761aa.js new file mode 100644 index 000000000..8487ef942 --- /dev/null +++ b/pr/299/docs/search.index/1b6ac9e761aa.js @@ -0,0 +1 @@ +rn_("AQABoJAAABJuaPI4EgAAAQDzAgFk8kAHAAABAAECADswAAABAAAFAAEAdRMFAPMLAW07MAAAAQAABQABADYTBQAjgAKgIAAADJFhbwECAPMPCQAA+AIBAAGKArAAEtUAAbAAEtcAATQ2I4ECoDAAAAwqZXIBgAKwQBEWAAFXDmQSJYJjdbcGAACHsBAPhQABsFATUgABEVEJ9f0EAAAKAK4AAQD5Bg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/1b82b05116c9.js b/pr/299/docs/search.index/1b82b05116c9.js new file mode 100644 index 000000000..e48f7b2fa --- /dev/null +++ b/pr/299/docs/search.index/1b82b05116c9.js @@ -0,0 +1 @@ +rn_("AQABoLAAABK9YvVREgAA0QABAEYAAQABgAKgIAAADwmgMAAAEslhcvMAAXQBpgcAAAEEAaBAAAATTWXybBMAAAEAAQUAOzAAAAEAAAsABQCLDwUA1hEBAPASAQBAEwAAXxMAAOMBhKAwAAAPB4UAAjswAAABAAALAAUA7AUCAO4GBAAPDAEAsxAAAOYRAABjgAOwAA8pAydkbnI=") \ No newline at end of file diff --git a/pr/299/docs/search.index/1b97b26c3ac1.js b/pr/299/docs/search.index/1b97b26c3ac1.js new file mode 100644 index 000000000..d7219e3ac --- /dev/null +++ b/pr/299/docs/search.index/1b97b26c3ac1.js @@ -0,0 +1 @@ +rn_("AYACsHARogABsCATkgABaW8BAAGw8BOSAAFr9AgTAAAFAAEAJgDzBQFk8t4RAAABAAECAPMCEAAAAQABAPMAAWk6MAAAAQAAAAAABwAQAAAA4A/0Ec0SzhL9Ev4SGRMaEyODArCwE3EAAWFzAYECoKAAABJAoEAAAA+lb3bzgIQBUAE=") \ No newline at end of file diff --git a/pr/299/docs/search.index/1ce4c2454b0a.js b/pr/299/docs/search.index/1ce4c2454b0a.js new file mode 100644 index 000000000..a333bda2d --- /dev/null +++ b/pr/299/docs/search.index/1ce4c2454b0a.js @@ -0,0 +1 @@ +rn_("AQIA9CASAAABAIkAAQABAwD0VBIAAAEAjgABAPMFAmJ1OjAAAAEAAAAAAAcAEAAAALIPsw9kEGUQEhETESsSLBIBAgD0nBAAAAEAWgEBAAEFAPOiEAAAAQABAAEEAPTREQAAAQCaAAEA84CEEQkA") \ No newline at end of file diff --git a/pr/299/docs/search.index/1ea1f2d3747a.js b/pr/299/docs/search.index/1ea1f2d3747a.js new file mode 100644 index 000000000..215722f56 --- /dev/null +++ b/pr/299/docs/search.index/1ea1f2d3747a.js @@ -0,0 +1 @@ +rn_("AQgA9OsSAAABAH8ABQATAAOwkBNLAAGwUBI6AU9nbHA7MAAAAQAAEgAJAJsPAgAPEQEAKRIBAEoSAQB7EgEA3BIBAA8TAQAeEwEAXRMBAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/2098306f4d34.js b/pr/299/docs/search.index/2098306f4d34.js new file mode 100644 index 000000000..cdc87eb43 --- /dev/null +++ b/pr/299/docs/search.index/2098306f4d34.js @@ -0,0 +1 @@ +rn_("AQAAOjAAAAEAAAAAAAoAEAAAABwPHQ/+D/8PixAWERcREhITEo4TjxPzAQF58vsSAAABACMAA6BAAAAQqaBQAAATRmVvc/IADwAAqgI=") \ No newline at end of file diff --git a/pr/299/docs/search.index/21a899f4f99b.js b/pr/299/docs/search.index/21a899f4f99b.js new file mode 100644 index 000000000..b4a853591 --- /dev/null +++ b/pr/299/docs/search.index/21a899f4f99b.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAQB1EwUA8w0BbTswAAABAAAFAAEANhMFACMDhLBAEcoAAaEAAAATe6AAAAAS/zAIAjswAAABAAAUAAUAWhAHAOsRAQBeEgQALhMEAJgTAAA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/25a54c1ab6e0.js b/pr/299/docs/search.index/25a54c1ab6e0.js new file mode 100644 index 000000000..d4ad746ab --- /dev/null +++ b/pr/299/docs/search.index/25a54c1ab6e0.js @@ -0,0 +1 @@ +rn_("AQAA9KYPAAABAKkAAQABhAKwABClAAGwABCnAAE0NvMCAWHyKAwAAAEAAQEA9swRAABmAAEARwD0AAEA84ACZW/zgAJoaw==") \ No newline at end of file diff --git a/pr/299/docs/search.index/266311e2892f.js b/pr/299/docs/search.index/266311e2892f.js new file mode 100644 index 000000000..e589e6467 --- /dev/null +++ b/pr/299/docs/search.index/266311e2892f.js @@ -0,0 +1 @@ +rn_("AQAA9BAJAAABALsJ3QAXAAAAyaBAAAAQPLBgE1IAAaAQAAAMOLBQEvIAAbAwEJ4AAaAAAAAL+rEgE3EAAbBAEDoB9wBOsAk6MAAAAQAAAAAAEQAQAAAAJQkmCToJOwnIC8kLygvSC9MLggyrEdMR1BHVEX0SfhIBEwIT") \ No newline at end of file diff --git a/pr/299/docs/search.index/266827f9a2f0.js b/pr/299/docs/search.index/266827f9a2f0.js new file mode 100644 index 000000000..d9c5f8c7f --- /dev/null +++ b/pr/299/docs/search.index/266827f9a2f0.js @@ -0,0 +1 @@ +rn_("AQAAOjAAAAEAAAAAAAoAEAAAABwPHQ/+D/8PixAWERcREhITEo4TjxMBBADzFw8AANUAAQDzAAFx960MAAABAM8CAQDBAGEAAQDzAAJleTowAAABAAAAAAAMABAAAAAMDA0Mtw+4D0kQShBLEOsS7BL7EvwSaxNwEw==") \ No newline at end of file diff --git a/pr/299/docs/search.index/267d2567696a.js b/pr/299/docs/search.index/267d2567696a.js new file mode 100644 index 000000000..22b695cd2 --- /dev/null +++ b/pr/299/docs/search.index/267d2567696a.js @@ -0,0 +1 @@ +rn_("AYICsEATSwABsGASMgABZXIBBQD0yg8AAAEAAQABAPOAAmZtAQYAOjAAAAEAAAAAAAgAEAAAABUPFg+6EPUR9hHgEmATkBOREwEAAaFAAAATiWf0ogcAAAEAAQABAMMAhKCwAAASybBAD+EAARQgBPUfBwAAWAULAz8C/QBjgAOwMA9OAAFhZWk=") \ No newline at end of file diff --git a/pr/299/docs/search.index/27f8ec34bd1f.js b/pr/299/docs/search.index/27f8ec34bd1f.js new file mode 100644 index 000000000..49f2588be --- /dev/null +++ b/pr/299/docs/search.index/27f8ec34bd1f.js @@ -0,0 +1 @@ +rn_("AYMCsQATngABsNATkAABa20BBAGgoAAAE6l38pkTAAABAAGAAqCAAAATNLCwEw0AAWVvYwQDoGAAABLMZW5wAd8LAAABCgDz8BIAAAEAbgATgAKiEAAAE6lsb3cAAACFoGAAABGfoQAAABNhBIEDOzAAAAEAADsABAABBgUAeAcAAMcHMwCsEAAAQ4ADsIAQqwEFoAAAAAHcZGt0") \ No newline at end of file diff --git a/pr/299/docs/search.index/281cb83843d4.js b/pr/299/docs/search.index/281cb83843d4.js new file mode 100644 index 000000000..1f251ac5d --- /dev/null +++ b/pr/299/docs/search.index/281cb83843d4.js @@ -0,0 +1 @@ +rn_("AQEA9VYJAAABAAEAAQABAPMAAWnzkgwAAAAHAQABBAKgIAAAEP+gMAAAEbJiaAEEDwAApwAAgMWgEAAADJOwEBN/AAGgMAAAD6sEECgB") \ No newline at end of file diff --git a/pr/299/docs/search.index/28827f9488f7.js b/pr/299/docs/search.index/28827f9488f7.js new file mode 100644 index 000000000..218e8e9ed --- /dev/null +++ b/pr/299/docs/search.index/28827f9488f7.js @@ -0,0 +1 @@ +rn_("AQkBoEAAABNNZfJsEwAAAQABgAKwAAyOAAGggAAAErpkcAEDAaAAAAAPbXPy/g4AAAEAAQIDsGASQQABsNATUAABoIAAABLZaXJzAVsTAAD3AACAhaEwAAATa5BJAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/2adb3d0bf2d8.js b/pr/299/docs/search.index/2adb3d0bf2d8.js new file mode 100644 index 000000000..d282d849b --- /dev/null +++ b/pr/299/docs/search.index/2adb3d0bf2d8.js @@ -0,0 +1 @@ +rn_("AQABoDAAAAx3aQFwEgAAAQABsDAQcgABbvKBBwAAAQABAQDz+wcAAO8GAQATAAKgUAAAD9Nlc/XsAwAAAQABAAEAAQABhAKwoBMJAAGwoBMLAAFjcvMBAXTyDhEAAM8AAQMBsGATUgABZjswAAABAAAGAAMAyAsCAH0SAQABEwEA84ACYWXzgIQAKAU=") \ No newline at end of file diff --git a/pr/299/docs/search.index/2b92810a6565.js b/pr/299/docs/search.index/2b92810a6565.js new file mode 100644 index 000000000..017e62f2a --- /dev/null +++ b/pr/299/docs/search.index/2b92810a6565.js @@ -0,0 +1 @@ +rn_("AYQCoCAAABBOoDAAABC1aXQBAQA7MAAAAQAAdwACAJsHAAB/CXYAYwADsAAB3gABZGVs9s4FAAABAAEAnAohAgEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/2bed9cec1d26.js b/pr/299/docs/search.index/2bed9cec1d26.js new file mode 100644 index 000000000..797037952 --- /dev/null +++ b/pr/299/docs/search.index/2bed9cec1d26.js @@ -0,0 +1 @@ +rn_("AQkA82UTAAABADUAAQ0BoKAAABOpd/KZEwAAAQABgQKwgBNzAAGgYAAAE5dkczcBAIWFoEAAABNJsGATGwBNApAK") \ No newline at end of file diff --git a/pr/299/docs/search.index/2e7d78ea0cfe.js b/pr/299/docs/search.index/2e7d78ea0cfe.js new file mode 100644 index 000000000..37920f8f7 --- /dev/null +++ b/pr/299/docs/search.index/2e7d78ea0cfe.js @@ -0,0 +1 @@ +rn_("AQABsPATkgABa/QIEwAABQABACYAEwACsQATfwABZG7y3hEAAAEAE4ACsBAMDAABZW8BBQA7MAAAAQAADQABAMQQDQABAgDzOw8AAAEAAQABAgD0Nw8AAAEAAQABAAEDADswAAABAAAIAAEA4w8IAAGAAqBAAAAQN7DAE0QAAWVzAQEAOzAAAAEAABMAAQBXDxMAI4ACsJAStwABaW03NgAAiqAQAAAL4qBQAAATEbBwEvYAAaAgAAAMlZxVEfV/BwAAkQgBAAEAAQDzgAJsdA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/2e882e6bf78f.js b/pr/299/docs/search.index/2e882e6bf78f.js new file mode 100644 index 000000000..07118cb77 --- /dev/null +++ b/pr/299/docs/search.index/2e882e6bf78f.js @@ -0,0 +1 @@ +rn_("AQoA9JYSAAABAA8AAQABgwKgIAAAD3+gMAAAEA9pdAEEAbAAEhAAAWT0uhEAAAEAAQABACOGhLCgEz4AAbBAEg4AAbBwEu4AARWAAAEAArAwD3EAAbAQEq4AAXN08t4FAAD3BQEMAPQiEwAAAQBGAAEAE4ACoAAAAAd5dHnzgIURwQA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/2f0bd76574d0.js b/pr/299/docs/search.index/2f0bd76574d0.js new file mode 100644 index 000000000..8aaaf3670 --- /dev/null +++ b/pr/299/docs/search.index/2f0bd76574d0.js @@ -0,0 +1 @@ +rn_("AQACoCAAABKEsDARDAABaXTy+REAAAEAAQMAOzAAAAEAAAYAAQCMEAYAAQgA9OsSAAABAH8ABQATAQOwkBNLAAGwUBI6AU9nbHA7MAAAAQAAEgAJAJsPAgAPEQEAKRIBAEoSAQB7EgEA3BIBAA8TAQAeEwEAXRMBAAGAArAwEQgAAbAwEQoAAXJ2AQQBsAARvwABZDswAAABAAAFAAIABBEDAOkRAQC3BQABh7BQEcEA/bBgEjgAAZQJAzswAAABAAAKAAUAhAwCAAgOAQAzEAAAthADADASAAAjgAKgEAAADINubwEDAPOYDwAAAQABACOAAqAAAAALz2RyYwGEoFAAABCzoDAAAA/PFAEQ8sEHAAABABOAArBgEd4AAWNnE4ADoLAAABMIoEAAAA4HZW9z") \ No newline at end of file diff --git a/pr/299/docs/search.index/2f2a8a6c3151.js b/pr/299/docs/search.index/2f2a8a6c3151.js new file mode 100644 index 000000000..fee0361f6 --- /dev/null +++ b/pr/299/docs/search.index/2f2a8a6c3151.js @@ -0,0 +1 @@ +rn_("AQAA9DEQAAABAHUBAQAjgQKgYAAAEjtiaQEAAPQnDwAAAQDmAAMBI4ICsFASTgABZXMBiQKwIBNZAAGgMAAAE2JpcAECAPQgEgAAAQCJAAEAAQMA9FQSAAABAI4AAQDzhQJidQECAPScEAAAAQBaAQEA84ACaWy3AQAAhaAgAAASKBIBDDowAAABAAAAAAAUABAAAACaByUIJggnCCgI0QsDDAQMBQwGDE0PBRChEhsTZRNmE2gTcxN0E5cTmxMBBQDzAxMAAAEAAQBDhgOwkBN9AAGgAAAAEcJiZHcBAwDzJQ8AAAEA+QABAgD21gsAAAEADgMBAJwDAQABAAA7MAAAAQAABgACAIASAADlEgUAI4ECsEATVAABZXNTgAOwAAlGAAFleHkBgAOgQAAAE3uwIBIcAAGwAAlEAAFlcngBgAKgQAAADJKwQA9OAAFnbJcGAADHsMASrAABoDAAABL5sEAMiwABAAagCzowAAABAAAAAAAIABAAAAC5BboFkQylDCEPeQ96DxgSGRIBBgD0HhIAAAEAiQABAPMDAWL0LxAAAAEAdQEBADcMAACJoHAAABGxsBAJKwABsJASTAABsGAPcQABoDAAABJwOIkTOjAAAAEAAAAAACwAEAAAAAoGCwYMBl8HYAcMCQ0JDgk+CT8J2wucDJ0MngyiDKMMpAweDx8Pbw9wD3QPdQ+RD5IPkw+UD5UPlg+XD7QPuQ/TDwAQARBwEHEQ/xGBEqUSyRJcE2cThROUEw==") \ No newline at end of file diff --git a/pr/299/docs/search.index/30399b5c8304.js b/pr/299/docs/search.index/30399b5c8304.js new file mode 100644 index 000000000..70e4d7b8e --- /dev/null +++ b/pr/299/docs/search.index/30399b5c8304.js @@ -0,0 +1 @@ +rn_("AQgBoIAAABOYZgGMEgAAE4ACoOAAABNhYnABAwGwABMbAE1z9R4PAAABAOEAAQDIAgGAArBAEcMAAaAQAAAQP3ByAYEDoAAAABCwsAAPCQEYoBAAAA8BZXN184ADYW9yAQIA9QIQAAABAAEAMgIBAAEEADswAAABAAAJAAEAdhAJAAGBAqCQAAAS4rBQEa4AAWJ3o4CEsFAS/QABsJASLgABQCADVwMAAIagcAAAEtmgQAAAE0YwQQw6MAAAAQAAAAAAOwAQAAAAIwUkBdEF0gXTBdQF1QXWBdcF2AXZBaYHNwk4CT0J5wvoCwkMCgwLDIsMjAwHDvMO9A71DgkPJw8oD2sP4A/uD+8PBhAHEA4QDxAhEEcQSBC1EAMRDBENERER9BEkEiUSzRLOEtQS/RL+EhkTGhMzE5UTlhOgE6ET") \ No newline at end of file diff --git a/pr/299/docs/search.index/30a16458d826.js b/pr/299/docs/search.index/30a16458d826.js new file mode 100644 index 000000000..f35bd4cb5 --- /dev/null +++ b/pr/299/docs/search.index/30a16458d826.js @@ -0,0 +1 @@ +rn_("AQcA9pQSAAABAKAAUwAWAAEA8wEBaQFnCQAAAQAA9WoJAAABAAEAAQABAEcBAIDGe8YhXqUhoKAAABN7sGAS9gABoBAAAAlDAAogig==") \ No newline at end of file diff --git a/pr/299/docs/search.index/30e9b37f1a9e.js b/pr/299/docs/search.index/30e9b37f1a9e.js new file mode 100644 index 000000000..b4840eeba --- /dev/null +++ b/pr/299/docs/search.index/30e9b37f1a9e.js @@ -0,0 +1 @@ +rn_("AYECsDAPcQABsBASrgABc3QBAwDz0xEAAAEAAQAjgAKwABDlAAFoaQEAADswAAABAAATAAEArQcTACOAAqAwAAAMgmFl84ACcnTzAAJhbDswAAABAAAoAAMAgQATAHYBAABXDxMA") \ No newline at end of file diff --git a/pr/299/docs/search.index/311e77def7dc.js b/pr/299/docs/search.index/311e77def7dc.js new file mode 100644 index 000000000..2dd6c2814 --- /dev/null +++ b/pr/299/docs/search.index/311e77def7dc.js @@ -0,0 +1 @@ +rn_("AQADoKAAABNBoCAAABKEsDARDAABZWl08vkRAAABAAEDADswAAABAAAGAAEAjBAGAAEIAPTrEgAAAQB/AAUAEwEDsJATSwABsFASOgFPZ2xwOzAAAAEAABIACQCbDwIADxEBACkSAQBKEgEAexIBANwSAQAPEwEAHhMBAF0TAQABgAKwMBEIAAGwMBEKAAFydgECAbAAEb8AAWQ7MAAAAQAABQACAAQRAwDpEQEAI4ECoJAAABNAaW8BBwD0XBMAAAsAHgAPAHcLAAGIsFARwQD9sGASOAABlQkDOjAAAAEAAAAAAA0AEAAAAIQMhQyGDAgOCQ73D/gPMxBWELYQtxC4ELkQMBIjgAKgEAAADINubw==") \ No newline at end of file diff --git a/pr/299/docs/search.index/3161d4c1c9e3.js b/pr/299/docs/search.index/3161d4c1c9e3.js new file mode 100644 index 000000000..13ab4fc0f --- /dev/null +++ b/pr/299/docs/search.index/3161d4c1c9e3.js @@ -0,0 +1 @@ +rn_("AQwA87QSAAABAAEAAQYA85kQAAABAAEAAYICsGASQQABsNATUAABaXITgAKwEAyOAAFlaZcBAIOHsTATbAABoFAAABAqsJASGAABsEAQKAABlBAH8wABYfRKCQAAAQAjBwEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/325f77b8029e.js b/pr/299/docs/search.index/325f77b8029e.js new file mode 100644 index 000000000..a1ae4d35e --- /dev/null +++ b/pr/299/docs/search.index/325f77b8029e.js @@ -0,0 +1 @@ +rn_("AQUBsEARygABZfLrEQAAAQABggKgkAAAEyCgkAAAEyEyMwEAAqAgAAARqaAgAAAJCWltOzAAAAEAAAwABgD+BAAAMgUFAHMJAADuDgEA+BIAAAsTAQDzAAFzAVkQAAABBQA7MAAAAQAABQABAIYSBQABAAD1LQwAAAEAAQABAAEA84ACZ2tzgISwsBLeAAERSAABBgGgQAAAERll86gMAACYAwEAAYACoJAAABLtsFAQgAABZ3oBgAOwUBMZAAGgEAAAC72gcAAAEnZnbnLHAQAAhaBgAAAS+aBQAAATShUBBDswAAABAAARAAQAxQEBAOMPCADfEAMAxxECAAGFArBwEwYAAbAwEmMAAWVzEwADsEAPDgABoGAAABC0YmVzOzAAAAEAABIABADyBQEA+AYKAIMPAQDUDwMAAQMA9J4PAAABAAQAAQABCwGgQAAAE01l8mwTAAABAPOAAmRy8wACZW87MAAAAQAABwACAMMHAwAECAMAAQIAOzAAAAEAAAgAAQC0CwgAAQAA9NgPAAABAAEAAQABAQA7MAAAAQAA1gACAA4GAAArCNUAYwADsBASIgABZWlzOjAAAAEAAAAAAAwAEAAAABsJUglTCVQJVQnWC9cL5Q7mDg0PJxCCEoMS44CEsCAHgQABEUAEAQGEsDALxgABoEAAAAx6oIAAABCHsEAMewABAAAdAdMBAAABBQD1zxIAAAEAAQBxAAEAE4ACoAAAAAcqYWcBAwD0ohEAAAEAUwEBAPOAAmlvAQEAOzAAAAEAANYAAgCeBwAA3grVAAEIAbBAE04AAWX1FBMAAAEAAQABAAEA84ECZ3QBAQGgcAAAER9pOzAAAAEAAOgAAgCdBwAA9gnnAAEFAPPyDwAAAQABAPOAAmFvE4ACoEAAAAvscnUBgAKgAAAAB4OgEAAACRpkeBMAArAgDKAAAWVy8k4PAAABABOAArCwE5IAAXJ4AQoA9I8SAAABAAEAAQATgAKwEBA7Ag5hZQEEAPTUDwAAAQABAAEAAQQAOjAAAAEAAAAAAAcAEAAAADQSNRJDEkQSvxLAEtIS0xIBBAD0KxAAAAEAAQABAAEDAPPaEQAAAQCaAPOAhAFIAce/AwCPoIAAABHcoGAAAAyRsEAMFAABsTATaQABP6kfOzAAAAEAAEwAIgBkAAAAaAAAAB4FAQAnBQAAywUBAGcHAQBxBwEAeQcAAH0HAQBMCQAAXwkHAHUJAQDFCwAA2QsBAOALAQDxCwcAFAwBACsMAQBxDAEAHA8BAIgPAQD+DwEAChABACIQAABEEAIAVBABAGYQBACLEAAA2RADABYRAQASEgEAFhIBAD8SAACOEwEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/32c27beceb5f.js b/pr/299/docs/search.index/32c27beceb5f.js new file mode 100644 index 000000000..b8d235cf5 --- /dev/null +++ b/pr/299/docs/search.index/32c27beceb5f.js @@ -0,0 +1 @@ +rn_("AQUBsEARygABZfLrEQAAAQABAAA7MAAAAQAAEQAEAMUBAQDjDwgA3xADAMcRAgABCgGwQBNOAAFl9RQTAAABAAEAAQABAEcGAACHsCAQOwIOsMATkgABoCAAAAcqsDAHgQABGIkKOjAAAAEAAAAAAA4AEAAAAHEHcgd9B34HTAnZC9oL4AvhCysMLAxxDHIMFhIXEg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/3407cc8b99a3.js b/pr/299/docs/search.index/3407cc8b99a3.js new file mode 100644 index 000000000..20c8dcbd5 --- /dev/null +++ b/pr/299/docs/search.index/3407cc8b99a3.js @@ -0,0 +1 @@ +rn_("AYECoCAAABN8sEAPMgABYnABCQD0QxIAAAEAjgABAAEMAPSBEwAAAQABAAEA84ACbnYBgAKwMAwPAAGgMAAADBFpbwECAaAAAAATSnMBGBEAACOAAqBAAAAS+mFl5wEAgIWwAAW9AAGQSAE=") \ No newline at end of file diff --git a/pr/299/docs/search.index/34726ae306d9.js b/pr/299/docs/search.index/34726ae306d9.js new file mode 100644 index 000000000..fe5e6a2ab --- /dev/null +++ b/pr/299/docs/search.index/34726ae306d9.js @@ -0,0 +1 @@ +rn_("AQMA9qUMAAB8AlgAAQCeAgEAAQQAOzAAAAEAAAoABAC6DwUA2BEBAC4SAQBBEwAAAYYCoAAAABJQoAAAABJRZHLzAgF2AXYSAADzgAJlbycBAADHoDAAAA8goHAAABGsoIAAABMSoNAAABK5oLAAABMSQEUICTswAAABAAAJAAMABwYCAEsHBAASDAEAI4ACcpo+9Xx6ZHIBAAA6MAAAAQAAAAAACgAQAAAAXQleCVQPaBJpEpQSlRI1E4gTnhOfExOAAqAwAAAP0Gx0w4AEoAAAAA/AoAAAAA/BNDZhZQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/34bb2b1a0ab9.js b/pr/299/docs/search.index/34bb2b1a0ab9.js new file mode 100644 index 000000000..d155cc168 --- /dev/null +++ b/pr/299/docs/search.index/34bb2b1a0ab9.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwDsEAIAcRIBAAMTAgABBwD2lBIAAAEAoABTABYAAQABBADz7xAAAAEAAQAjAAKgYAAAEf9hcvMWEAAAAQCrAQGAAqEQAAAThbCAE30AAWl1dwgAAIixUBOgAAGACScAFs6wcBLNAAGhMAAAE5QKQRvyZwkAAGkGAYECoFAAABA7oKAAABJJa3ABAwA7MAAAAQAA1gACAIkMAQAKDtQA84ACbHTzgAJhZQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/35cd5d54f947.js b/pr/299/docs/search.index/35cd5d54f947.js new file mode 100644 index 000000000..6d6766600 --- /dev/null +++ b/pr/299/docs/search.index/35cd5d54f947.js @@ -0,0 +1 @@ +rn_("AQgA9NoSAAABAFEAAQABAgGgYAAAE3xz8v0QAAABAAGAArAgD1UAAaCQAAATM2FlAQcBsEATTgABZfUUEwAAAQABAAEAAQABAwGwQBNjAAF08soSAAABACOFAqBAAAAS+GJzAQIA9PkQAAABAAEAAQA3dAAAi7BAEqwAAaBQAAAQmKCQAAASoLBQEgsAAaAwAAAMIrOYBzswAAABAABXABEAzAEBAPgDAwANBgAA9AYBAIoHAQAYCQEAJAkAAC8JAABcCQAAOQw3AIMMAAAjDwEA8A8BAAwQAQBSEAEA1BAEAMMRAQA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/36de07894256.js b/pr/299/docs/search.index/36de07894256.js new file mode 100644 index 000000000..0bf508625 --- /dev/null +++ b/pr/299/docs/search.index/36de07894256.js @@ -0,0 +1 @@ +rn_("AQMBgBJzABDtczswAAABAAAFAAEAWBIFACOCAqCQAAATSmlzAQEA9CASAAABAIkAAQAjgAKwoBL7AAFjbgGEArCgEwkAAbCgEwsAAWNyAQIAOzAAAAEAAAYAAQCRDwYAE4ICoCAAABLtZXUBAgDz7xAAAAEAAQABgAKwIBMqAAGgYAAAEoFvdmOBA6AQAAASQGFjcwEDAPR+DAAAAQABAAEAAQEA9CUQAAABAJsAAQATgAOgwAAAE1awEAktAAFucHUBgAKwMA9OAAGhYAAAE4lhZQGBArBAEc0AAbBAEc8AATQ2AQUA9KYPAAABAKkAAQBTgISgsAAAExGwMBJqAAEBoAQBCADzZxIAAAcAFwATgAKwQBEUAAFhZQEAAaAgAAAPwnIBiBAAAAEAAPS7EgAAAQBKAAEApwAAgIWgMAAAECKgEAAAEEKwoBMcAAEJICEBCQGgQAAAE01l8mwTAAABAEOAhKAgAAATW6CQAAASuqEwAAATaxBJAAECAPRUEgAAAQCOAAEAE4ACsEASIgABaXkBAQKgMAAAEf+gQAAAEj9ncjswAAABAAAHAAIAIxABALsQBQABgAKwABLVAAGwABLXAAE0NgEGAPSVEwAAAQAKAAEA8wICcnYBABMAAPMCAWX0rg8AAAEAqAABAPOAAmR0560/ABKgMAAAEEOgQAAADJKw8BOSAAGwcBKCAAEwYWJjZWdoaWtsbm9wcnN0dXY7MAAAAQAA8QBFACIFAADvBQIA+QUDAPMGAABQBwAAcwcCAIMHAACVBwQAmwcAAPsHAAAjCAEAEAkBABYJAQAiCQAAewkBAH8JdgDCCwEAzwsAANwLAAARDAAAHQwAAOoOAQASDwEAKQ8AAIoPAACyDwEAxA8AANEPAQAWEAEAGhADACsQAwBkEAEAhhAAAKIQAgDSEAAA6xAAAA4RAAASEQEAIhEEALERAAC/EQEAwhEAAMwRAADdEQAAEBIBAB4SAQArEgEAMhIBADgSAQA7EgAAPhIAAFASAABlEgEAehIAAH8SAACPEgQAqBIBALkSAAC7EgEAwRIBAMwSAAD6EgAABhMCAA0TAQAoEwEANBMAAEcTAQBuEwEAqRMAAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/3748eeff0c66.js b/pr/299/docs/search.index/3748eeff0c66.js new file mode 100644 index 000000000..cac70343e --- /dev/null +++ b/pr/299/docs/search.index/3748eeff0c66.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAANYAAgCJDAEACg7UAAEBAaAAAAASRnP1JQUAAOMJrwMBAAIAAYACoCAAAA79oDAAABHzZXITAQKgIAAAENNwdQHpDgAAAQECoGAAABK9oOAAABMTZXQ7MAAAAQAACAABACkFCAABgAOwIBGgAAGwsBHWAAGwsBHYAAFiY3PzgMUAAQoK") \ No newline at end of file diff --git a/pr/299/docs/search.index/378531389b4a.js b/pr/299/docs/search.index/378531389b4a.js new file mode 100644 index 000000000..8e2994e31 --- /dev/null +++ b/pr/299/docs/search.index/378531389b4a.js @@ -0,0 +1 @@ +rn_("AQUA9XYPAAABAAEANgIBAAEEAaAAAAAQBXP1ogwAAAEAAQDQAgEAAQIA9dQQAAABAAEAAQABAAGAArBgEhYAAaAgAAAQJ295AQABoMAAABMnafRnEgAABwAXABsAAQYA9IoTAAABAAEAAQABAgD0JRAAAAEAmwABAAECAaBAAAASP3I7MAAAAQAABwACACMQAQC7EAUA84ACYW9nAwAAhrCAEj4AVbCgEq4AAQwwBjswAAABAAAIAAMA2AsAAOcQAwC2EQMA84ICZWlHAQAAhbBwErsAAaCAAAATmKBAAAAPLbBABPQLCQAADQYBAHMDAYECoCAAABAhsFARwwABYW8BBAGgAAAAEtRzASAQAABjBAOwUBCTAAFjZXP2EwkAAAEAAQAsAAEAaggBAAA7MAAAAQAAJAAIAGkHBwB2DwIAxBANAOwQAgCuEQEAJhIBAHESAQADEwIAAQACsBALwgABsDATPgABZXD0Gg8AAAEA4AABAAEAA7BAEwsAAaFAAAATcKBQAAARDmJjZgE4BwAA44CEsUATlQABAjAQAQEDoLAAABLJsEAP4QABoGAAABC6Y2V09B8HAABjCD8C/QABAwA7MAAAAQAABQABAOARBQABAAGgoAAAEzNy9CMPAAABAOgAAQDzgQJncwEHAPVeEgAAAQABAAEAAQABgAKgQAAAEIuwUBByAAFjafOAAmFvAQABoCAAABCqcvPtCwAAAwNcAROAAoAFuwAb6HJ4AQMCsEASHAABoDAAABJwZXfyuQ8AALgAE4MCoHAAABKlZ28BAgD1AhIAAAEA3wB3AAEAE4ACsDASLQE1aXABBgDzohIAAAEAAQDzBQJicwFzDwAAAQIA9FAPAAABAAEAAQAjgAKgYAAAEapiY/e/AACMoJAAABGrO8kaOjAAAAEAAAAAABcAEAAAANAB3gHfARMEFAQVBD8HdgenB6gHqQeqB6sHrAe9CycMpwxuD4APgQ9CEMMQfxOAEw==") \ No newline at end of file diff --git a/pr/299/docs/search.index/37cfa41c8509.js b/pr/299/docs/search.index/37cfa41c8509.js new file mode 100644 index 000000000..7d06027e9 --- /dev/null +++ b/pr/299/docs/search.index/37cfa41c8509.js @@ -0,0 +1 @@ +rn_("AYACoJAAABJ6oBAAAAX0aW8BCQDzZRMAAAEANQABDAGgoAAAE6l38pkTAAABAAEBAPMvDwAAAQABAPOAAnR1AYECsIATcwABoGAAABOXZHM3AgCDhqBAAAATSbBgExsATbAwEwkAAQKQDgESAPSBEwAAAQABAAEAAQDGoFAAABHmoAAAAA/EsCAQdAABoFAAABEYoQAAABN8oEAAABMnAAMCCzswAAABAAAUAAcA9QUAAOwLAACNDAAAPg8MAJgQAAD7EQEAIBMBAAEBAbCwE24AAWw7MAAAAQAADAADAFEHCACpDAEAew8BAAEBAaAwAAAQT2jzEgkAAC4AwgcBAgD0+RAAAAEAAQABABOBArAgEP0AAWFz84ICbm/3AwCAh6BAAAAL1RUBBw==") \ No newline at end of file diff --git a/pr/299/docs/search.index/3872754cddc0.js b/pr/299/docs/search.index/3872754cddc0.js new file mode 100644 index 000000000..b881b0e6b --- /dev/null +++ b/pr/299/docs/search.index/3872754cddc0.js @@ -0,0 +1 @@ +rn_("AQABsIASRwABdvR3CQAAAQB2CQEAE4ACsDAQNAABbG4BgAKgAAAAD8CgAAAAD8E0NvMDAXYBpgwAABMAAqBAAAAQsGFz9+4LAAA/A1oBLAEBAAEAkQEBAgD0IBIAAAEAiQABAAEDAPRUEgAAAQCOAAEA8wACYnU6MAAAAQAAAAAAEwAQAAAAcwd0B3UHEg8TD7IPsw/RD9IPZBBlEBIRExEeEh8SKxIsEjsSqBKpEvOAAmR0E4ACoCAAAAyvZXIBBwGgQAAAEyZl8lATAAABAAEHAPQrEAAAAQABAAEAAQAA9WoJAAABAAEAAQABACOAAqCgAAATe2ZwAQMBoJAAABL6ZfLsDgAAAQDXAAAAhaBQAAAPqLFAE6IAAYEpAPK/BQAAAQDzgAJsdJcBAICFsTATKAABsRATUgABEUUA") \ No newline at end of file diff --git a/pr/299/docs/search.index/38ae726b5e74.js b/pr/299/docs/search.index/38ae726b5e74.js new file mode 100644 index 000000000..7c0c9015a --- /dev/null +++ b/pr/299/docs/search.index/38ae726b5e74.js @@ -0,0 +1 @@ +rn_("AQAA9kcQAAABAE0DAQAKAAEAAQAAOzAAAAEAAAYAAgCAEgAA5RIFACOIArBAE1QAAWVzAQ0BsEATTgABZfUUEwAAAQABAAEAAQABBQA7MAAAAQAACAADABwRAgAEEgQAExMAANcBAACFeJvYCPEYFIQC83QMAAABAAEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/39c20340df78.js b/pr/299/docs/search.index/39c20340df78.js new file mode 100644 index 000000000..89402d23a --- /dev/null +++ b/pr/299/docs/search.index/39c20340df78.js @@ -0,0 +1 @@ +rn_("ARMA9aQTAAABAAEAAQABABOAAqDAAAASRWFvAQICoLAAABNAoLAAABNBY3Pz9w8AAAEAXgABgQKwUBEUAAGgwAAAExFoafOAAmVpI4ACoLAAABJ/aXQBBgGwgBNHAAF2OzAAAAEAAAcAAgDyEAQAtBICAHcAAACGoKAAABKhoJAAABJvsFAPtQABBeAC9EIHAAABAAEANww=") \ No newline at end of file diff --git a/pr/299/docs/search.index/3a74000719ff.js b/pr/299/docs/search.index/3a74000719ff.js new file mode 100644 index 000000000..91353690e --- /dev/null +++ b/pr/299/docs/search.index/3a74000719ff.js @@ -0,0 +1 @@ +rn_("AYYCoAAAABJQoAAAABJRZHIBgAKgcAAAEtmgMAAAED9mdAELAaBAAAATJmXyUBMAAAEAI4ECsFAQFgABY3YBAAGwgBMoAAFw8iQTAAABAEMGA6AgAAAQiKBAAAARvmRlc/MuDwAAEwMBAPMABGlyc3Y7MAAAAQAADwAFACQHBQDgEQUACxIBAHYSAABbEwAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/3b786721c45c.js b/pr/299/docs/search.index/3b786721c45c.js new file mode 100644 index 000000000..5c6452fe8 --- /dev/null +++ b/pr/299/docs/search.index/3b786721c45c.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAYAAQCiEwYA8wYBZfRXEwAAAQAuAAEAEwMDsEATfwABoGAAABL/YXJ1AaoPAAAjgAKAEKIAEAFkcg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/3bd565fd40d0.js b/pr/299/docs/search.index/3bd565fd40d0.js new file mode 100644 index 000000000..6e7dd8f45 --- /dev/null +++ b/pr/299/docs/search.index/3bd565fd40d0.js @@ -0,0 +1 @@ +rn_("AQoBoIAAABOYZgGMEgAAAQgA9S4TAAABAAEAAQABAPMDAWb1xQ8AAAEAAQABAAEA84MCYXMBAgKgIAAAEP+gMAAAEbJiaAEEDwAAI4ACoIAAABJFYXDzAAJobQEGBQAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/3d41efe337a8.js b/pr/299/docs/search.index/3d41efe337a8.js new file mode 100644 index 000000000..093b649b3 --- /dev/null +++ b/pr/299/docs/search.index/3d41efe337a8.js @@ -0,0 +1 @@ +rn_("AQUA9NERAAABAJoAAQABCAD0fRIAAAEAgwABADMBA7BAElIAAWFiZfWYDAAAAQDTAkECwgABAAD15wsAAAEACwMBAAEA84ACc3UjgQKwgBJ3AAFydAGAArBwEgsAAaAQAAAMfW56AQYA9LIPAAABALEAAQBzAISgoAAAE6kCART1wwUAAAEAhQ1QAAEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/3d4983faf394.js b/pr/299/docs/search.index/3d4983faf394.js new file mode 100644 index 000000000..d9c656ecc --- /dev/null +++ b/pr/299/docs/search.index/3d4983faf394.js @@ -0,0 +1 @@ +rn_("AYACsHAR3gABoLAAABMIZW8jAYSwcBEgAAGgYAAAEnqAEnMAEO0AMAM7MAAAAQAAGgAGAJMHAQACCQUAqwwBAHYQCQC0EAAAWBIFAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/3eb044d68081.js b/pr/299/docs/search.index/3eb044d68081.js new file mode 100644 index 000000000..82e4776ea --- /dev/null +++ b/pr/299/docs/search.index/3eb044d68081.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAYAAQCMEAYAAQAA9zAJAAABAHsGAQBrAAEADgEBggKgIAAAEE6gMAAAELVpdPOAA2FncwEBAaBAAAATJmXyUBMAAAEAI4ACsFAS3gABZnPzgAJpbw==") \ No newline at end of file diff --git a/pr/299/docs/search.index/3f1d867bdbec.js b/pr/299/docs/search.index/3f1d867bdbec.js new file mode 100644 index 000000000..8be475bac --- /dev/null +++ b/pr/299/docs/search.index/3f1d867bdbec.js @@ -0,0 +1 @@ +rn_("AQ8A9A8TAAABAE0AAQABBAD0sA8AAAEAsQABAAECADowAAABAAAAAAAJABAAAAAAEQER/RH+EdoS2xIsEy0TnBOdE/MAAW/yfQkAAAEAU4ADsQATbgABaG9w84ACc3Q=") \ No newline at end of file diff --git a/pr/299/docs/search.index/3f2790b84249.js b/pr/299/docs/search.index/3f2790b84249.js new file mode 100644 index 000000000..d943965b9 --- /dev/null +++ b/pr/299/docs/search.index/3f2790b84249.js @@ -0,0 +1 @@ +rn_("AYACsHATfQABoAAAAAUGZmcBAQA7MAAAAQAABwABAPsLBwABAQGgcAAAEaphAT8HAAAjgAKhAAAAE4VkbnOAhKBgAAARqQBBSA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/3f76acc9c4c0.js b/pr/299/docs/search.index/3f76acc9c4c0.js new file mode 100644 index 000000000..964c42a52 --- /dev/null +++ b/pr/299/docs/search.index/3f76acc9c4c0.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAYAAQCRDwYAAQMA9H4MAAABAAEAAQBjAAOgkAAAEm5ocHM6MAAAAQAAAAAAEAAQAAAA7wXwBfEF8wYiCREMHQwrECwQLRAuEL8RwBEQEhESOBI5Eg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/3fe667d21e90.js b/pr/299/docs/search.index/3fe667d21e90.js new file mode 100644 index 000000000..08b7f9620 --- /dev/null +++ b/pr/299/docs/search.index/3fe667d21e90.js @@ -0,0 +1 @@ +rn_("AQABoDAAABCwdPTuDwAAAQAXAAEAAYACoAAAAA/AoAAAAA/BNDbzAwF2AaYMAAAXAQAAh7CgEhoAAbAgC+AAAbBgEhYAAbCAEf0AAaAgAAAQJwlERjowAAABAAAAAAAJABAAAAA1BzYHNwfuCy0PhxCzEbQRtRFGEw==") \ No newline at end of file diff --git a/pr/299/docs/search.index/402127170d10.js b/pr/299/docs/search.index/402127170d10.js new file mode 100644 index 000000000..9fcb5452d --- /dev/null +++ b/pr/299/docs/search.index/402127170d10.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAABAABQD1BQAA7AsAAI0MAAA+DwwAmBAAAAGEArAAEKUAAbAAEKcAATQ2FwgAAIigIAAAEP+gMAAAEGyg4AAAEqCgMAAAEbKwcA8YAAGgIAAABf7LwAg7MAAAAQAA+QAVAIABAADqBQAA/gUAAAkJAAAPCQAALQkBAM0LAAAHDAEAKAwBAJAMAAC1DNUABA8AABUPAQD9DwAAuhAAAPURAQDDEgUA4BIAAGATAAB1EwUAkBMBAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/40672655bcf1.js b/pr/299/docs/search.index/40672655bcf1.js new file mode 100644 index 000000000..d9d614b54 --- /dev/null +++ b/pr/299/docs/search.index/40672655bcf1.js @@ -0,0 +1 @@ +rn_("AYkCsCATWQABoDAAABNiaXATAAOgIAAAEiiggAAAEjtlaXX10QsAADIAAQABAAEAAYYCsJATfQABoAAAABHCYmQBgAKwMBIcAAGgMAAAEB9ydnMChKAwAAAScBAAEzowAAABAAAAAAAJABAAAAAMCQ0JDgk+CT8JtA+5D3AQcRD/EQEDAbAwEjYAAXPz8g8AAAEAAQABAAA7MAAAAQAACgADAJkQAgCwEgMAihMDANMAhLAgCCkAARAYAfUtDAAAAQABAAEAAQA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/4083ed670548.js b/pr/299/docs/search.index/4083ed670548.js new file mode 100644 index 000000000..408697c82 --- /dev/null +++ b/pr/299/docs/search.index/4083ed670548.js @@ -0,0 +1 @@ +rn_("AQMBoAAAAA9rc/PkCwAAAQABAAECAPPjCwAApgQBACMAAqBAAAAMlGVy8hcFAAABAAGAAqBAAAASzLAwEfsAAWFzAQIBoEAAABNNZfJsEwAAAQDzgARsbnJ2") \ No newline at end of file diff --git a/pr/299/docs/search.index/40d41dda421f.js b/pr/299/docs/search.index/40d41dda421f.js new file mode 100644 index 000000000..c0f7cfdc8 --- /dev/null +++ b/pr/299/docs/search.index/40d41dda421f.js @@ -0,0 +1 @@ +rn_("AQQA9q0MAAABAM8CAQAiAQEAAQoA89oRAAABAJoAAQEA9zAJAAABAHsGAQBrAAEADgEjgAKwYBLeAAFpbwECAPRxDAAAAQCkBQEAAQUA9PQSAAABAEcAAQD3BAAAh6AgAAAMc7CQERoAAQScAzowAAABAAAAAAAIABAAAADcBSAJIQkbDBwMKBApEAkSChI=") \ No newline at end of file diff --git a/pr/299/docs/search.index/40e5df003d92.js b/pr/299/docs/search.index/40e5df003d92.js new file mode 100644 index 000000000..38228ec94 --- /dev/null +++ b/pr/299/docs/search.index/40e5df003d92.js @@ -0,0 +1 @@ +rn_("AQIBoKAAABJnZPIiBwAAAQABgQKgMAAAE1ugUAAAEu1jcwEEA7AgEHQAAaBQAAARGKBAAAATJ2xzdvIgEwAAAQDzAANhZW/yGgQAAAEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/40fc47699d53.js b/pr/299/docs/search.index/40fc47699d53.js new file mode 100644 index 000000000..cb5e24bae --- /dev/null +++ b/pr/299/docs/search.index/40fc47699d53.js @@ -0,0 +1 @@ +rn_("AQABsIATKAABcPIkEwAAAQBDBQOgIAAAEIigQAAAEb5kZXPzLg8AABMDAQABAgA7MAAAAQAAEwABAFcPEwATgYSgUAAAEDegEAAAC+KgIAAADJUcQAAzgAOwUBJ3AAFhZm4=") \ No newline at end of file diff --git a/pr/299/docs/search.index/4154473306f6.js b/pr/299/docs/search.index/4154473306f6.js new file mode 100644 index 000000000..b6fd0a831 --- /dev/null +++ b/pr/299/docs/search.index/4154473306f6.js @@ -0,0 +1 @@ +rn_("AYUCsHATBgABsDASYwABZXMTAAOwQA8OAAGgYAAAELRiZXM7MAAAAQAAEgAEAPIFAQD4BgoAgw8BANQPAwABAgDz/QUAADUJAQABAwD1Bg8AAJgAAQAEAAEAAQoBoEAAABNNZfJsEwAAAQAjAAOgMAAAEEOgkAAAEoUwY2j1IhEAAG0BAQABAAEA84ACZHLnAAAAh7AQCQgC6LAgC8QA7qAwAAAPKrAAB2QAARJBODswAAABAAAQAAYAeAACAOMFAAARBgMAwwcDAAQIAwDOCwAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/4365fc23c2e2.js b/pr/299/docs/search.index/4365fc23c2e2.js new file mode 100644 index 000000000..d6cd055ab --- /dev/null +++ b/pr/299/docs/search.index/4365fc23c2e2.js @@ -0,0 +1 @@ +rn_("AQQA9FwTAAALAB4ADwABAgGwQBHFAAFlAQUPAAABgAKgkAAAEyCgkAAAEyEyM5cCAADGoCAAAAwnoGAAABBssGAMqwABAWQAAzswAAABAAAGAAIARQcFAPkLAAA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/442799c14c6f.js b/pr/299/docs/search.index/442799c14c6f.js new file mode 100644 index 000000000..c46a3f073 --- /dev/null +++ b/pr/299/docs/search.index/442799c14c6f.js @@ -0,0 +1 @@ +rn_("AQkA82UTAAABADUAAYACsCATCQABoCAAAA8NaXkBCQGggAAAE5hmAYwSAAABAwGwABMbAE1zAckSAABDgAOwIA8JARiwUBHDAAFhb3IBAgD1AhAAAAEAAQAyAgEA8wADaXR1OjAAAAEAAAAAAA4AEAAAAKYHPQlrD+APAxH0ESQSJRLNEs4S1BL9Ev4SGRMaEwEGAPQSEgAAAQB7AQEAEwICsGASFAABZm3yhwwAAAEAI4ICsEASPAABZWwBgAKwQBAGAAGgIAAAE0ljdBOAArCwEqYAAWFsAQYA8xcPAADVAAEAAQUA9PQSAAABAEcAAQDzAgFj8gkSAAABAAGBArBQEFIAAbAgDucAAWNuAQUAOzAAAAEAAA0AAgCNDAAAPg8MAAECADswAAABAAAHAAEAXwkHAOcFAAAJoDAAAAwOoLAAABMZoIAAABDSsNATKgABMGFmaWttcnV2OzAAAAEAACQCBQDEAQAAFgPVAJ4HAADeCtUAiw12AAEMAaCgAAATqXfymRMAAAEAAQEA8y8PAAABAAEA84ACdHUBAwDzxxEAAAEAAQABAQD1ZhAAAAEAAQABAAEAAQEA9pEHAAABAIsABQehANwBY4ADsJAS9gABYW10AQMBoEAAABNNZfJsEwAAAQATgAKwYBOcAAFlcgEDAaAAAAAS1HMBIBAAAAECADowAAABAAAAAAAKABAAAABdCV4JVA9oEmkSlBKVEjUTiBOeE58TgwAEoIAAABGksHAQlgABsCAQNgDpZm5ydgEcCAAAAYECoAAAAAk9oDAAAA8Nc3THAQCAxrAAEygAAbCQEaIAAaAQAAAHdwASiCgBAgA7MAAAAQAADQAGAIsPBQCTEAEA1hEBAPASAQBAEwAAXxMAABOCArDwE5wAAWZnAYACsIATcwABoGAAABOXZHMTgAKwwBK7AAFpbwEIAPUuEwAAAQABAAEAAQDzAgFm9cUPAAABAAEAAQABACOAA6AgAAAPB7CgE3EAAWRydPf7BwCPoDAAAA78l9cPOzAAAAEAAB8BHQAWBAEAFAUCAMUFAgDsBQIADwYBAO4GBABmBwAAgAcAABYIAwATCQIAQQkBAOQLAgAPDAEAMgwFAIkMAQAKDtQAJQ8BAKAPAgDhDwEA8g8CAB8QAQCAEAEAsxAAAAoRAQCsEQAA5hEAAHcSAQCYEgcAgRMDAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/44a79dbac07c.js b/pr/299/docs/search.index/44a79dbac07c.js new file mode 100644 index 000000000..ffe468f70 --- /dev/null +++ b/pr/299/docs/search.index/44a79dbac07c.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAADgAAgAvCQAAOQw3AAGAArBQD+4AAbCwEpYAAWFsYwAEsEAO7gABoBAAAA+oYnBydjswAAABAAAGAAEAjBAGAAGAArCAEDoB97DgEvsAAWlyAQMBsEAQiQABZQECDgAAAQUAOzAAAAEAAAYAAQCRDwYAAQMA9H4MAAABAAEAAQDzAAJwczowAAABAAAAAAAOABAAAADvBfAF8QXzBiIJEQwdDCsQLBAtEC4QvxHAERASERJXAAAAhbBQD+ACFLBQDwoAxKBAAAAQtEiAAzswAAABAAAPAAYAAgUAALAFAwD2DgAABBEDALoRAwDpEQEA0wCEoGAAAAyQEAgG83cBAAABAPsK") \ No newline at end of file diff --git a/pr/299/docs/search.index/45caa4397354.js b/pr/299/docs/search.index/45caa4397354.js new file mode 100644 index 000000000..d2b0f7993 --- /dev/null +++ b/pr/299/docs/search.index/45caa4397354.js @@ -0,0 +1 @@ +rn_("AQEAOjAAAAEAAAAAAAkAEAAAABUPFg/9D7oQ9RH2EeASYBOQE5ETAYACsOAS2gABsDAS8gABZHDDgISxQBOgAAGgkAAAEqECgAk=") \ No newline at end of file diff --git a/pr/299/docs/search.index/45e872f24307.js b/pr/299/docs/search.index/45e872f24307.js new file mode 100644 index 000000000..e8bfeb65f --- /dev/null +++ b/pr/299/docs/search.index/45e872f24307.js @@ -0,0 +1 @@ +rn_("AQEA9JAHAACHB9UAAQABBQD0NBIAAAEAigABAPMCAWP1iA8AAAEAuwABAAEAYwAEoDAAAAvvoHAAABEDZW5zdvOlDwAAlQD3AQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/45f4fbda75f7.js b/pr/299/docs/search.index/45f4fbda75f7.js new file mode 100644 index 000000000..d1f2e2e5e --- /dev/null +++ b/pr/299/docs/search.index/45f4fbda75f7.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABCwc/XuCwAAmQQsAQEAAQATggKgIAAADK9lciOAA6BAAAAL7bAwCTcAAWVvdAECAPSWEgAAAQAPAAEAE4ACsEATRAABYnYTgAKwAAdiAAFveAEKAPXPEgAAAQABAHEAAQABgQKgIAAAECGwUBHDAAFhbwEEAaAAAAAS1HMBIBAAAGMDA7BQEJMAAWNlc/YTCQAAAQABACwAAQBqCAEBAPR+DAAAAQABAAEAI4ACsDASOgFPcnT3AQAAhrAQB2QAAQHAB/K4AQAAGw8=") \ No newline at end of file diff --git a/pr/299/docs/search.index/47fa58ac1f7e.js b/pr/299/docs/search.index/47fa58ac1f7e.js new file mode 100644 index 000000000..2b3339697 --- /dev/null +++ b/pr/299/docs/search.index/47fa58ac1f7e.js @@ -0,0 +1 @@ +rn_("AYQCsAAQpQABsAAQpwABNDbzAAFh8igMAAABACOAArCQEKsBBWx0AYACsHASjQABsIASwQABY3MBAAA6MAAAAQAAAAAACQAQAAAAABEBEf0R/hHaEtsSLBMtE5wTnRPzgAJkZScBAICFoNAAABMAsBAFIAABoDAAAA+oAggL") \ No newline at end of file diff --git a/pr/299/docs/search.index/481e68f578df.js b/pr/299/docs/search.index/481e68f578df.js new file mode 100644 index 000000000..766462807 --- /dev/null +++ b/pr/299/docs/search.index/481e68f578df.js @@ -0,0 +1 @@ +rn_("AQEA9egFAADpCwEAmgABAAEAADswAAABAAAHAAMA1wEBACAMAQBQDwMAAYMCsHASjQABsIASwQABY3MBBQD0NBIAAAEAigABAPMIAWP1iA8AAAEAuwABAAEAAQABoDAAABCwdPTuDwAAAQAXAAEAAYACoAAAAA/AoAAAAA/BNDbzAwF2AaYMAACXAAAAhbCgEhoAAbAgC+AAAbCAEf0AAQkEBjowAAABAAAAAAAJABAAAAA1BzYHNwfuCy0PhxCzEbQRtRFGEwECAPQgEgAAAQCJAAEAAQMA9FQSAAABAI4AAQDzAAJidTowAAABAAAAAAAVABAAAABzB3QHdQcSDxMPsg+zD9EP0g9kEGUQEhETER4SHxIrEiwSOxJlEmYSqBKpEgEBAPTkBQAAAQCxBgEAtwAAgIWxABMsAAGwAAd9AAEMCEQBCAA7MAAAAQAABQABAOARBQDXDQAAiLBAEFQAAaAgAAAMr7kACzswAAABAAAPAAUAZgABAC0HBwCMBwEApg8BAFAQAQA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/4821942f57b5.js b/pr/299/docs/search.index/4821942f57b5.js new file mode 100644 index 000000000..e35afddbe --- /dev/null +++ b/pr/299/docs/search.index/4821942f57b5.js @@ -0,0 +1 @@ +rn_("AQIA9dQQAAABAAEAAQABAAGAArBgEhYAAaAgAAAQJ295AQABoMAAABMnafRnEgAABwAXABsAAQYA9IoTAAABAAEAAQABAgD0JRAAAAEAmwABAAECAaBAAAASP3I7MAAAAQAABwACACMQAQC7EAUA84ACYW9nAwAAhrCAEj4AVbCgEq4AAQwwBjswAAABAAAIAAMA2AsAAOcQAwC2EQMA84ACZWk=") \ No newline at end of file diff --git a/pr/299/docs/search.index/48b9c2df65c3.js b/pr/299/docs/search.index/48b9c2df65c3.js new file mode 100644 index 000000000..1ae5bf062 --- /dev/null +++ b/pr/299/docs/search.index/48b9c2df65c3.js @@ -0,0 +1 @@ +rn_("AQcAOzAAAAEAAHcAAgAeEAAAKBF2AAEDADswAAABAAB2AAEAiw12AAECADswAAABAAAHAAEA+wsHAPMAA2JpdDswAAABAAB3AAIAAQUAADkFdgA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/49d47034aeca.js b/pr/299/docs/search.index/49d47034aeca.js new file mode 100644 index 000000000..67d5b7651 --- /dev/null +++ b/pr/299/docs/search.index/49d47034aeca.js @@ -0,0 +1 @@ +rn_("AQkBoIAAABOYZgGMEgAAAQIA9QIQAAABAAEAMgIBAPMAAml1OjAAAAEAAAAAAAcAEAAAAOAP9BHNEs4S/RL+EhkTGhMBCAD1LhMAAAEAAQABAAEA8wIBZvXFDwAAAQABAAEAAQATgAKwoBNxAAFydPMCAmFz8/IPAAABAAEAAQQBsEATTgABZfUUEwAAAQABAAEAAQABAgA7MAAAAQAACwACAMMSBQB1EwUA8wMBbTswAAABAAANAAQAeQwAAJUQAADtEQUANhMFAPOBAmNl84ACYWU=") \ No newline at end of file diff --git a/pr/299/docs/search.index/49f51c99ba82.js b/pr/299/docs/search.index/49f51c99ba82.js new file mode 100644 index 000000000..c6f3a5307 --- /dev/null +++ b/pr/299/docs/search.index/49f51c99ba82.js @@ -0,0 +1 @@ +rn_("AQUA9PQSAAABAEcAAQDzAwFj8gkSAAABACOAArBwE5wAAWhvAYICsAAR5wABsAARDAABZXNDAAOgoAAAE0GgIAAAEoRlaXTy+REAAAEAAQIAOzAAAAEAAAYAAQCMEAYAAQIBoEAAABMmZfJQEwAAAQDzgAJpbwEIAPTrEgAAAQB/AAUAEwEDsJATSwABsFASOgFPZ2xwOzAAAAEAABIACQCbDwIADxEBACkSAQBKEgEAexIBANwSAQAPEwEAHhMBAF0TAQABgAKwQBHBAP2gIAAAEE9haQGBArAwEvsAAaAwAAAR3WNpAQMBsAASJAABczswAAABAAANAAMAChEBAJgSBwCBEwMAAQMA9QgRAAABAAUBAQBHAfOAAnJ2AQAA9L8RAAABAFAAAQDzAgFkOzAAAAEAAAkAAwAEEQMAuhEDAOkRAQAjgAKgkAAAE0BpbxOAArDwE44AAWxvAQUA9FwTAAALAB4ADwAjgAKgYAAAEnlncyOAArCQEz4AAWJs98cAAI2wYBNxAAGwcBLuAAGwYBI4AAGwUBImAAG1yRc6MAAAAQAAAAAAHwAQAAAA/QUSCUAJFgwXDIQMhQyGDAMOBA4FDgYOCA4JDjIPMw80DzUPNg/3D/gPMxBWELYQtxC4ELkQAhEwEkcSSBISEw==") \ No newline at end of file diff --git a/pr/299/docs/search.index/4a662a061b37.js b/pr/299/docs/search.index/4a662a061b37.js new file mode 100644 index 000000000..ddcd0f74d --- /dev/null +++ b/pr/299/docs/search.index/4a662a061b37.js @@ -0,0 +1 @@ +rn_("AYACsEASaAABoMAAABMZZW8BAgD0+RAAAAEAAQABABMAA7AgEP0AAbCAEywAAWFzdAGDDAAAAQQA8zQPAAABAAEAs4CEsEASdwABQQAG") \ No newline at end of file diff --git a/pr/299/docs/search.index/4a809c839d91.js b/pr/299/docs/search.index/4a809c839d91.js new file mode 100644 index 000000000..49cf24e15 --- /dev/null +++ b/pr/299/docs/search.index/4a809c839d91.js @@ -0,0 +1 @@ +rn_("AQAA9NgPAAABAAEAAQDzAgFz9VIJAAABAAEAAQC4BQEGAPQjEQAAAQABAAEAAQEAOzAAAAEAAAYAAQCiEwYA8wYBZfRXEwAAAQAuAAEAEwYDsEATfwABoGAAABL/YXJ1AaoPAADzgAJhZQEDAPTfEAAAAQABAAEAAQIA9AIPAAABAAYEAQABgAOwAAefAAGgAAAAC9SwcA+3AAFucnOnBwAAh6BwAAARIrBwEf0AAZEFQTowAAABAAAAAAAMABAAAACuAa8BsAGxAaEHCggLCFsJDgw7DzwPPQ9DEA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/4ac32d6f5fe0.js b/pr/299/docs/search.index/4ac32d6f5fe0.js new file mode 100644 index 000000000..6740040be --- /dev/null +++ b/pr/299/docs/search.index/4ac32d6f5fe0.js @@ -0,0 +1 @@ +rn_("AQcA9CsQAAABAAEAAQABAAD1agkAAAEAAQABAAEAI4ACoKAAABN7ZnABAwGgkAAAEvpl8uwOAAABANcAAACFoFAAAA+osUATogABgSkA8r8FAAABAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/4b168eb35e34.js b/pr/299/docs/search.index/4b168eb35e34.js new file mode 100644 index 000000000..1ca565898 --- /dev/null +++ b/pr/299/docs/search.index/4b168eb35e34.js @@ -0,0 +1 @@ +rn_("AQEA9WYQAAABAAEAAQABACOBArCQEvYAAWF0Q4ADsRATbAABoIAAABK5Y290EwACsAATRAABbnL20QEAAAEARgIBAEgDIws=") \ No newline at end of file diff --git a/pr/299/docs/search.index/4b2753b82fcd.js b/pr/299/docs/search.index/4b2753b82fcd.js new file mode 100644 index 000000000..f0bffb349 --- /dev/null +++ b/pr/299/docs/search.index/4b2753b82fcd.js @@ -0,0 +1 @@ +rn_("AQQA8/kOAAABAAEAAYACoFAAAA/9oGAAABBtaXIBgAKgIAAADCegYAAAEGxlaAEBAaAgAAAQa2LzIwwAAAEAAQATgQKgEAAADCZhYwEGAPQSEgAAAQB7AQEAEwACsGASFAABZm3yhwwAAAEAI4ACoMAAABLUbnKnBwAAh6BAAAAO9rBADvcAATQAFzswAAABAAAXAAMAzgEAAPwDDQC0CwgAAYADsHASjQABsDASPAABsIASwQABY3JzAQABsCASjQABbzowAAABAAAAAAAJABAAAAAAEQER/RH+EdoS2xIsEy0TnBOdEwGAArAgCTIAAbAwEkcAAXJ09wAAAMegIAAAB5ygYAAADyKwgBJHAAFABpII9tsBAABEAlgFAQB2CQEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/4b3a70d5ace1.js b/pr/299/docs/search.index/4b3a70d5ace1.js new file mode 100644 index 000000000..bc3233a52 --- /dev/null +++ b/pr/299/docs/search.index/4b3a70d5ace1.js @@ -0,0 +1 @@ +rn_("AQgA9S4TAAABAAEAAQABAPMAAWY7MAAAAQAABQACALkBAADFDwQAAQIAOzAAAAEAAAsAAgDDEgUAdRMFAPMGAW07MAAAAQAADQAEAHkMAACVEAAA7REFADYTBQBjgAOwAAkyAAFlbXM=") \ No newline at end of file diff --git a/pr/299/docs/search.index/4bc2f7dbadf5.js b/pr/299/docs/search.index/4bc2f7dbadf5.js new file mode 100644 index 000000000..828bf9fa5 --- /dev/null +++ b/pr/299/docs/search.index/4bc2f7dbadf5.js @@ -0,0 +1 @@ +rn_("AQQBsEATTgABZfUUEwAAAQABAAEAAQABAgA7MAAAAQAACwACAMMSBQB1EwUA8wEBbTswAAABAAANAAQAeQwAAJUQAADtEQUANhMFAPMBAWXyIBEAAAEAM4CEsBAPtwABoCAAAAvUFEAE") \ No newline at end of file diff --git a/pr/299/docs/search.index/4bf63ac4398b.js b/pr/299/docs/search.index/4bf63ac4398b.js new file mode 100644 index 000000000..f231b74ea --- /dev/null +++ b/pr/299/docs/search.index/4bf63ac4398b.js @@ -0,0 +1 @@ +rn_("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") \ No newline at end of file diff --git a/pr/299/docs/search.index/4c291219c272.js b/pr/299/docs/search.index/4c291219c272.js new file mode 100644 index 000000000..d7e7d8520 --- /dev/null +++ b/pr/299/docs/search.index/4c291219c272.js @@ -0,0 +1 @@ +rn_("AYQCsAAQpQABsAAQpwABNDbzAgFh8igMAAABAAEAAPbMEQAAZgABAEcA9AABAPMAAWTz2hEAAAEAmgBjgAOgIAAACQphZW8=") \ No newline at end of file diff --git a/pr/299/docs/search.index/4c96afa0c0be.js b/pr/299/docs/search.index/4c96afa0c0be.js new file mode 100644 index 000000000..8faf3123f --- /dev/null +++ b/pr/299/docs/search.index/4c96afa0c0be.js @@ -0,0 +1 @@ +rn_("AQIBoQAAABNnZPKJBwAA6wEBBgGgMAAAERF0AaESAAABAwGwUBH7AAFl9CMJAAD7AgEAigMBAQD0+QUAAAEAAQABAAEBAaEAAAATXGTy+AUAACIClwcAAIewoBH5AAGg4AAAEzMegArz8gMAAAEAAQA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/4d63d12f11e5.js b/pr/299/docs/search.index/4d63d12f11e5.js new file mode 100644 index 000000000..f5b10d8de --- /dev/null +++ b/pr/299/docs/search.index/4d63d12f11e5.js @@ -0,0 +1 @@ +rn_("AQEBsIASZQABdfJQCQAAAQAjgAKwEAlOAAEzNgEGAPMYEAAAAQAOAROAArAADvEAAW91AQMA89MRAAABAAEAI4ACsAAQ5QABaGkBAAA7MAAAAQAAEwABAK0HEwAjgAKgMAAADIJhZfOAAnJ0E4ACsDASrgABYWwBAAGggAAAE5hm8gsJAACBCQEHAPVeEgAAAQABAAEAAQAjgAKgUAAAEIthb/MCAmZzOzAAAAEAAAgABAB2BwAApwcFAKcMAADDEAAAE4ACoEAAAAvOYW8BAwGwQBNjAAF08soSAAABACOFAqBAAAAS+GJzEwACoFAAABCYYmb3GAkAAAEAQwCUBgEAYQABABOBAqBQAAARH2d0AYACoEAAABEOoCAAABLZYWkBAgD0SgkAAAEAIwcBAAECAPMJDAAAAQABABOAA7AQCUgAAbAwEwsAAWl1eQECADswAAABAAALAAIAwxIFAHUTBQDzCAFtOzAAAAEAAAwAAwCVEAAA7REFADYTBQABgQKggAAAEdygcAAAExlhc/OAAm5yt7UBAI2hQAAAE3CwMBCxAAGgIAAACUywIAd6AdO3lQ47MAAAAQAABQIFAMIBAADmARoBOAcAAJ0HAAD2CecA") \ No newline at end of file diff --git a/pr/299/docs/search.index/4e0c6b87a738.js b/pr/299/docs/search.index/4e0c6b87a738.js new file mode 100644 index 000000000..93f1aafc7 --- /dev/null +++ b/pr/299/docs/search.index/4e0c6b87a738.js @@ -0,0 +1 @@ +rn_("AQQA9mUTAAABAA0AAQAjAAQAAQMA88cRAAABAAEAI4ECsSATnAABY2sTgAKgMAAAEyZuclMAA6BAAAAL22VvcvRPEAAAYwGeAQEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/4e720ea46bc3.js b/pr/299/docs/search.index/4e720ea46bc3.js new file mode 100644 index 000000000..0c21c02f2 --- /dev/null +++ b/pr/299/docs/search.index/4e720ea46bc3.js @@ -0,0 +1 @@ +rn_("AQcAOzAAAAEAAAUAAQCGEgUA8wUBdDswAAABAAAIAAIAxQsAAPELBwABBQDzohIAAAEAAQA3AACAhbAwD4AAAaCgAAASvaAwAAAS+BBBSA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/4f554ad18979.js b/pr/299/docs/search.index/4f554ad18979.js new file mode 100644 index 000000000..85d10002c --- /dev/null +++ b/pr/299/docs/search.index/4f554ad18979.js @@ -0,0 +1 @@ +rn_("AQIAOjAAAAEAAAAAAA4AEAAAALAMsQw8EBkRGhEbEb4RxRHGEcoRyxEaEhsSThJPEgEJAPOiEAAAAQABAAEKAPPTEQAAAQABAAEGAPVmEAAAAQABAAEAAQDzgAJkbnMAhLEgEyoAAQARA/IiEgAAAQABDQD2lBIAAAEAoABTABYAAQABgAKwoBKsAAGgEAAAC+9jdQEBAPS7EgAAAQBKAAEA8wABZfJ7CQAAAQD3AACAxbAwD+EAAQACqAg=") \ No newline at end of file diff --git a/pr/299/docs/search.index/5037f3c0b285.js b/pr/299/docs/search.index/5037f3c0b285.js new file mode 100644 index 000000000..d7d38787f --- /dev/null +++ b/pr/299/docs/search.index/5037f3c0b285.js @@ -0,0 +1 @@ +rn_("AQIA9dQQAAABAAEAAQABAAGAArBgEhYAAaAgAAAQJ295AQABoMAAABMnafRnEgAABwAXABsAAQYA9IoTAAABAAEAAQABAgD0JRAAAAEAmwABAAECAaBAAAASP3I7MAAAAQAABwACACMQAQC7EAUA84ACYW9nAwAAhrCAEj4AVbCgEq4AAQwwBjswAAABAAAIAAMA2AsAAOcQAwC2EQMA84ICZWkjAAKggAAAE5hmaPILCQAAgQkBAAKwEAvCAAGwMBM+AAFlcPQaDwAAAQDgAAEAAQADsEATCwABoUAAABNwoFAAABEOYmNmATgHAADzgAJtbgEBA6CwAAASybBAD+EAAaBgAAAQumNldPQfBwAAYwg/Av0AAQUAOzAAAAEAAAUAAQDgEQUAAQcA9V4SAAABAAEAAQABACOAAqBQAAAQi2Fv8wCFIEkCOzAAAAEAAAgABAB2BwAApwcFAKcMAADDEAAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/50c4c9dd70c1.js b/pr/299/docs/search.index/50c4c9dd70c1.js new file mode 100644 index 000000000..e13d94126 --- /dev/null +++ b/pr/299/docs/search.index/50c4c9dd70c1.js @@ -0,0 +1 @@ +rn_("AQEA90kQAAABAAEAoAIBAH8ABQABAQKgQAAAE1agMAAAEE9laPUSCQAALgDWAgEA6wQBAgD0+RAAAAEAAQABABOBArAgEP0AAWFz84ACbm8BAgD03A8AAAEAAQABABMAAqDQAAATW2Nz8lUPAAABAPOAA2Vpbw==") \ No newline at end of file diff --git a/pr/299/docs/search.index/512cfed374c5.js b/pr/299/docs/search.index/512cfed374c5.js new file mode 100644 index 000000000..a34b60b9d --- /dev/null +++ b/pr/299/docs/search.index/512cfed374c5.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABJGc/QIDwAArwMBAAIAAQECsGATKgABsEATYwABZHQ7MAAAAQAADQAGAAASAwBjEgEAyhIBAOESAQBZEwEAfRMBAAEBA7BwESAAAbCAEd4AAYAScwAQ7W1uczswAAABAAALAAIAAgkFAFgSBQCjgISgsAAAErmxcBOcAAEIQIIBAQA7MAAAAQAABgABAKITBgDzBgFl9FcTAAABAC4AAQATAQOwQBN/AAGgYAAAEv9hcnUBqg8AAFcBAACFsRATKgABsDASLQE1EYEIOzAAAAEAAAYAAgCAEgAA5RIFAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/5161b276f515.js b/pr/299/docs/search.index/5161b276f515.js new file mode 100644 index 000000000..a1f74460d --- /dev/null +++ b/pr/299/docs/search.index/5161b276f515.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAkABABoCQEAyg8DAAgQAQDeEgEAAQIBsEATYwABdDswAAABAAANAAYAABIDAGMSAQDKEgEA4RIBAFkTAQB9EwEAE4ACsDASLQE1aXABiAKwABLVAAGwABLXAAE0NhOBAqAwAAAQrGRzAYACoCAAAAwqsGATBgABbngBBQDzohIAAAEAAQATgAKgMAAAEvhlefMAhBIAA/JzDwAA2gDzgAJydA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/520fc5a0c8db.js b/pr/299/docs/search.index/520fc5a0c8db.js new file mode 100644 index 000000000..1521bbef6 --- /dev/null +++ b/pr/299/docs/search.index/520fc5a0c8db.js @@ -0,0 +1 @@ +rn_("AYECoCAAABAhsFARwwABYW8BAwGgAAAAEtRzASAQAAAjgAKgAAAACBxydmMAA7BQEJMAAWNlczswAAABAAAIAAQAEwkCAEEJAQCgDwIArBEAAAcBAICFsFAQUgABoKAAABKgsPAStwABsCAO5wABRCEE") \ No newline at end of file diff --git a/pr/299/docs/search.index/52a47b654b95.js b/pr/299/docs/search.index/52a47b654b95.js new file mode 100644 index 000000000..24cb236ea --- /dev/null +++ b/pr/299/docs/search.index/52a47b654b95.js @@ -0,0 +1 @@ +rn_("AQIA9QISAAABAN8AdwABABOAArAwEi0BNWlwAQYA86ISAAABAAEA8wICYnMBcw8AAPMBAWbyjwcAANwIAQIA9FQSAAABAI4AAQAzgAOwQBIiAAFpdXk=") \ No newline at end of file diff --git a/pr/299/docs/search.index/52a660a476b8.js b/pr/299/docs/search.index/52a660a476b8.js new file mode 100644 index 000000000..9570f116b --- /dev/null +++ b/pr/299/docs/search.index/52a660a476b8.js @@ -0,0 +1 @@ +rn_("AQIBsEATYwABdDswAAABAAANAAYAABIDAGMSAQDKEgEA4RIBAFkTAQB9EwEAE4ACsDASLQE1aXABBQDzohIAAAEAAQATgAKgMAAAEvhleVMBA7BwEwYAAWJlcwFzDwAAE4ADsDATcQABsCAHKwABZWxz") \ No newline at end of file diff --git a/pr/299/docs/search.index/53206e8cf266.js b/pr/299/docs/search.index/53206e8cf266.js new file mode 100644 index 000000000..126f236eb --- /dev/null +++ b/pr/299/docs/search.index/53206e8cf266.js @@ -0,0 +1 @@ +rn_("AQkA82UTAAABADUAAQwBoKAAABOpd/KZEwAAAQABAQDzLw8AAAEAAQDzgAJ0dQGBArCAE3MAAaBgAAATl2RzNwIAgIagQAAAE0mwYBMbAE2wMBMJAAECkA4BAAGwUBA0AAFj9IYHAAABAAEA6gHDgISgEAAADCqgUAAAEawMQAQ=") \ No newline at end of file diff --git a/pr/299/docs/search.index/53c1218b1076.js b/pr/299/docs/search.index/53c1218b1076.js new file mode 100644 index 000000000..f8e0d3024 --- /dev/null +++ b/pr/299/docs/search.index/53c1218b1076.js @@ -0,0 +1 @@ +rn_("AQABsKAR2AABZTswAAABAAAJAAIADAgHABQSAQABAAGgIAAADJBtAc0FAAABgAKwoBHWAAGwQA8QAAFsbwEEAbAAEhAAAWT0uhEAAAEAAQABACMChLCgEz4AAbBAEg4AAbBwEu4AARWAAAESEwAAAQABsIATKAABcPIkEwAAAQBDAAOgIAAAEIigQAAAEb5kZXPzLg8AABMDAQDzgQJladcCAACGsCARoAABsNATLAABFwEC9AMFAAABAAEArwA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/53d3e59dd54f.js b/pr/299/docs/search.index/53d3e59dd54f.js new file mode 100644 index 000000000..7377bb9ae --- /dev/null +++ b/pr/299/docs/search.index/53d3e59dd54f.js @@ -0,0 +1 @@ +rn_("AQEA90kQAAABAAEAoAIBAH8ABQABAgD03A8AAAEAAQABABOAAqDQAAATW2NzU4ADoHAAABNWZWlvQ4ADoCAAAA8gsAAHewABZGx0") \ No newline at end of file diff --git a/pr/299/docs/search.index/5452a8cd2f09.js b/pr/299/docs/search.index/5452a8cd2f09.js new file mode 100644 index 000000000..9eccb7022 --- /dev/null +++ b/pr/299/docs/search.index/5452a8cd2f09.js @@ -0,0 +1 @@ +rn_("AQMA88cRAAABAAEAI4ECsSATnAABY2sBBAGgQAAAERll86gMAACYAwEAAQAA9RoQAAABAAEAAQBiAnOAhKAwAAATJggwAQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/5456fb44b43c.js b/pr/299/docs/search.index/5456fb44b43c.js new file mode 100644 index 000000000..d41ee4f71 --- /dev/null +++ b/pr/299/docs/search.index/5456fb44b43c.js @@ -0,0 +1 @@ +rn_("AQABsGAS/QABZQEzEwAAAQYA9DISAAABADsBAQABBAD0TBIAAAEA/gABAPMAA2VyczowAAABAAAAAAAJABAAAAA0CTUJNgltEKsQohGjEbAR9hL3Eg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/54c99bfc02b4.js b/pr/299/docs/search.index/54c99bfc02b4.js new file mode 100644 index 000000000..ff4e8885b --- /dev/null +++ b/pr/299/docs/search.index/54c99bfc02b4.js @@ -0,0 +1 @@ +rn_("AQAEoAAAAA/EsCAQdAABoFAAABEYoEAAABMnZGxzdjswAAABAAASAAYA9QUAAOwLAACNDAAAPg8MAJgQAAAgEwEAAQIA9PkQAAABAAEAAQAThAKwIBD9AAFhc/OAAmFl") \ No newline at end of file diff --git a/pr/299/docs/search.index/550f9f8ba786.js b/pr/299/docs/search.index/550f9f8ba786.js new file mode 100644 index 000000000..cb06883b7 --- /dev/null +++ b/pr/299/docs/search.index/550f9f8ba786.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwDsEAIAcRIBAAMTAgABBADz7xAAAAEAAQAjAAKgYAAAEf9hcvMWEAAAAQCrAQGAAqEQAAAThbCAE30AAWl1NwQAgYexUBOgAAGACScAFs6wcBLNAAGhMAAAE5QKQBsBAAA7MAAAAQAAdgABAIsNdgABAgD2lBIAAAEAoABTABYAAQAjgQKgIAAAEDZydvOAAmVvI4ACoCAAABGkZHQBAAA7MAAAAQAA1wADAA4GAAArCNUAOAwAAPOAA2Rudg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/5535895d7e1a.js b/pr/299/docs/search.index/5535895d7e1a.js new file mode 100644 index 000000000..1a82c04fa --- /dev/null +++ b/pr/299/docs/search.index/5535895d7e1a.js @@ -0,0 +1 @@ +rn_("AQMBgBJzABDtczswAAABAAAFAAEAWBIFACOCAqCQAAATSmlzBwEAhMWwUBIiAAGgoAAAErqwcBKCAAGg0AAAE1aAARgI") \ No newline at end of file diff --git a/pr/299/docs/search.index/55eb9c789f72.js b/pr/299/docs/search.index/55eb9c789f72.js new file mode 100644 index 000000000..69233f091 --- /dev/null +++ b/pr/299/docs/search.index/55eb9c789f72.js @@ -0,0 +1 @@ +rn_("AYACoIAAABHcsHATGQABYXMBAAGggAAAE5hm8gsJAACBCQEHAPVeEgAAAQABAAEAAQAjgAKgUAAAEIthb/MAAmZzOzAAAAEAAAgABAB2BwAApwcFAKcMAADDEAAA84ACZHk=") \ No newline at end of file diff --git a/pr/299/docs/search.index/570e64122582.js b/pr/299/docs/search.index/570e64122582.js new file mode 100644 index 000000000..c0eb9cee0 --- /dev/null +++ b/pr/299/docs/search.index/570e64122582.js @@ -0,0 +1 @@ +rn_("AYMCsQATngABsNATkAABa20BCgDz8BIAAAEAbgATgAKiEAAAE6lsbzMChKBgAAARn6EAAAATYQSAAzswAAABAAA0AAIAeAcAAMcHMwA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/5726b451fa74.js b/pr/299/docs/search.index/5726b451fa74.js new file mode 100644 index 000000000..d3630cac5 --- /dev/null +++ b/pr/299/docs/search.index/5726b451fa74.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAQB1EwUA8w0BbTswAAABAAAFAAEANhMFAEcAAACFsAATRwABsEARygABoQAAABN7oAAAABL/OAgCOzAAAAEAABgABgC+CwMAWhAHAOsRAQBeEgQALhMEAJgTAAA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/587c8d82fabf.js b/pr/299/docs/search.index/587c8d82fabf.js new file mode 100644 index 000000000..e7a8f25af --- /dev/null +++ b/pr/299/docs/search.index/587c8d82fabf.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAUAAQCGEgUAAQAA9S0MAAABAAEAAQABAPOAAmdrE4ACsLAS3gABYW8BgAKgIAAAC72wYBCAAAFlaQGFArBwEwYAAbAwEmMAAWVzEwADsEAPDgABoGAAABC0YmVzOzAAAAEAABIABADyBQEA+AYKAIMPAQDUDwMAAQMA9J4PAAABAAQAAQABCwGgQAAAE01l8mwTAAABAPOAAmRy8wACZW87MAAAAQAABwACAMMHAwAECAMAAQIAOzAAAAEAAAgAAQC0CwgA84ACb3QBAwA7MAAAAQAA1gACAJ4HAADeCtUAAQIBoHAAABEfaTswAAABAADoAAIAnQcAAPYJ5wATgAKgQAAAC+xydQEEAPRDEgAAAQCOAAEAAQMA89oRAAABAJoA84ACYW/XBwAAiLBADKAAAbEwE2kAATSBFjswAAABAAAdAAwAHgUBAMsFAQDFCwAA8QsHABQMAQAcDwEA/g8BAIsQAADZEAMAFhEBABISAQCOEwEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/59c419e706d0.js b/pr/299/docs/search.index/59c419e706d0.js new file mode 100644 index 000000000..99e5ed616 --- /dev/null +++ b/pr/299/docs/search.index/59c419e706d0.js @@ -0,0 +1 @@ +rn_("AYACsAAPVQABsCAMFgABZWkBiQKwIBNZAAGgMAAAE2JpcBMAA6AgAAASKKCAAAASO2VpdfXRCwAAMgABAAEAAQABhgKwkBN9AAGgAAAAEcJiZAGAArAwEhwAAaAwAAAQH3J2cwCEoDAAABJwEAATOjAAAAEAAAAAAAoAEAAAAAwJDQkOCT4JPwnbC7QPuQ9wEHEQ/xFjgAOgEAAACQFkbXQ=") \ No newline at end of file diff --git a/pr/299/docs/search.index/5a5100ed4a84.js b/pr/299/docs/search.index/5a5100ed4a84.js new file mode 100644 index 000000000..0180853c8 --- /dev/null +++ b/pr/299/docs/search.index/5a5100ed4a84.js @@ -0,0 +1 @@ +rn_("AQsBoEAAABMmZfJQEwAAAQABBQD0IxEAAAEAAQABAAGDArAAEBYAAbBQEnEAAWR3E4ACoKAAABLhZXYBAgD0MRAAAAEAdQEBAAEAAPQnDwAAAQDmAAMBI4ICsFASTgABZXMBAgD0IBIAAAEAiQABAAEDAPRUEgAAAQCOAAEA84UCYnUBAgD0nBAAAAEAWgEBAPOAAmls8wADYnR1OjAAAAEAAAAAAA8AEAAAAJoHJQgmCCcIKAhNDwUQoRIbE2UTZhNoE3MTdBOXE5sTAQwA8wMTAAABAAEAAQYA9B4SAAABAIkAAQDzAwFi9C8QAAABAHUBAQAXBgAAh7BADyUAAaBwAAARsbAQCSsAAbCQEkwAATgJAzowAAABAAAAAAAhABAAAAAKBgsGDAZfB2AHnAydDJ4MogyjDKQMHg8fD28PcA90D3UPkQ+SD5MPlA+VD5YPlw/TDwAQARCBEqUSyRJcE2cThROUEwEDAPQyEgAAAQA7AQEA84ACY213AQCAxbGAE6QAAUCBBAg=") \ No newline at end of file diff --git a/pr/299/docs/search.index/5a77bebba663.js b/pr/299/docs/search.index/5a77bebba663.js new file mode 100644 index 000000000..15f4e2199 --- /dev/null +++ b/pr/299/docs/search.index/5a77bebba663.js @@ -0,0 +1 @@ +rn_("AQYA9BISAAABAHsBAQATAgKwYBIUAAFmbfKHDAAAAQAjggKwQBI8AAFlbAEFAPT0EgAAAQBHAAEA8wMBY/IJEgAAAQABgQKgAAAACT2gMAAADw1zdBOAAqAQAAAHd25404CEsHATnAABkEAB") \ No newline at end of file diff --git a/pr/299/docs/search.index/5c2e611e8037.js b/pr/299/docs/search.index/5c2e611e8037.js new file mode 100644 index 000000000..8d36974f8 --- /dev/null +++ b/pr/299/docs/search.index/5c2e611e8037.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAkAAQB2EAkAAQIAOzAAAAEAABMAAQBXDxMAE4GEoFAAABA3oBAAAAvioCAAAAyVHEAAE4ACsFASdwABYW4zgAOwMA8CAAFpc3Q=") \ No newline at end of file diff --git a/pr/299/docs/search.index/5cc356801927.js b/pr/299/docs/search.index/5cc356801927.js new file mode 100644 index 000000000..1b7993fd6 --- /dev/null +++ b/pr/299/docs/search.index/5cc356801927.js @@ -0,0 +1 @@ +rn_("AQEBsIASZQABdfJQCQAAAQAjgAKwEAlOAAEzNgECAPRKCQAAAQAjBwEAAQIA8wkMAAABAAEAE4ACsBAJSAABaXUBAgA7MAAAAQAACwACAMMSBQB1EwUA8wkBbTswAAABAAAMAAMAlRAAAO0RBQA2EwUAdyAAAIqgMAAAEtmwMBCxAAGgcAAAER+gIAAACUywMBDlAAGwIAd6AdOzhQo7MAAAAQAABAIEAMIBAADmARoBnQcAAPYJ5wA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/5d03104f5b36.js b/pr/299/docs/search.index/5d03104f5b36.js new file mode 100644 index 000000000..f523a7ad1 --- /dev/null +++ b/pr/299/docs/search.index/5d03104f5b36.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABCwc/XuCwAAmQQsAQEAAQATggKgIAAADK9lcgECAPQjDwAAAQDoAAEAI4ECoBAAAAvtZWkTgAKg8AAAExJucjMAA7AwCTcAAWVvdAH/EAAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/5dcae36ab7a9.js b/pr/299/docs/search.index/5dcae36ab7a9.js new file mode 100644 index 000000000..a8af7241f --- /dev/null +++ b/pr/299/docs/search.index/5dcae36ab7a9.js @@ -0,0 +1 @@ +rn_("AQACoCAAABKEsDARDAABaXTy+REAAAEAAQMAOzAAAAEAAAYAAQCMEAYAAQgA9OsSAAABAH8ABQATAQOwkBNLAAGwUBI6AU9nbHA7MAAAAQAAEgAJAJsPAgAPEQEAKRIBAEoSAQB7EgEA3BIBAA8TAQAeEwEAXRMBAAGAArAwEQgAAbAwEQoAAXJ2AQQBsAARvwABZDswAAABAAAFAAIABBEDAOkRAQC3BQABh7BQEcEA/bBgEjgAAZQJAzswAAABAAAKAAUAhAwCAAgOAQAzEAAAthADADASAAAjgAKgEAAADINubwEDAPOYDwAAAQABACOAAqAAAAALz2RyYwCEoFAAABCzoDAAAA/PFAEQ8sEHAAABAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/5e27fec6e119.js b/pr/299/docs/search.index/5e27fec6e119.js new file mode 100644 index 000000000..b2b9bb58f --- /dev/null +++ b/pr/299/docs/search.index/5e27fec6e119.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAkAAQB2EAkAI4ADsGASdwABsDAPAgABaXN0AQgA9S4TAAABAAEAAQABAPMBAWb1xQ8AAAEAAQABAAEAY4ADoDAAABI/ZWd0") \ No newline at end of file diff --git a/pr/299/docs/search.index/5e990d199296.js b/pr/299/docs/search.index/5e990d199296.js new file mode 100644 index 000000000..56eec354f --- /dev/null +++ b/pr/299/docs/search.index/5e990d199296.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAUAAQBYEgUAg4YEsFASIgABsHASggABoNAAABNWYm5vdhOAArAQEfsAAXByAQABoDAAABERdAGhEgAA8wIBc/LTDwAA0gITgAKgYAAAD8RldQECAPatDAAAAQDPAgEAIgEBAAGAArAAEecAAYAOBzBQAWVzAQEA84AHAAD3CgEAY4ADsIAS8gABYm5084ADYW9yARQA9IETAAABAAEAAQABAAA7MAAAAQAABgACAIASAADlEgUAI4gCsEATVAABZXMBBAGwMBJWAAFj860QAAABAAEAAQUAOzAAAAEAAAgAAwAcEQIABBIEABMTAADzAgNjZXDzdAwAAAEAAQBjgAOgYAAADyllb3UBAAGwYBL9AAFlATMTAADzAgFz9jQJAAABAAEANwc+AAUBAQgA88cRAAABAAEAQ4ADoBAAABKEoOAAABMIZG9zAQIBoFAAABCscgFNEAAAAYACoEAAAA/QoHAAABNBYWUBAAA7MAAAAQAACwACACQHBQDgEQUA84ACdHYBAQD0FggAAAEAAQABAPMAAm5y9ioJAABQADYGAQCxAAEA8wCHMJEM9fkRAAABAHkAAQDtAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/5f3aaa0e45e4.js b/pr/299/docs/search.index/5f3aaa0e45e4.js new file mode 100644 index 000000000..1f6e97293 --- /dev/null +++ b/pr/299/docs/search.index/5f3aaa0e45e4.js @@ -0,0 +1 @@ +rn_("AYACoFAAABBtoFAAABGfYXUBgwKxABOeAAGw0BOQAAFrbQEEAaCgAAATqXfymRMAAAEAAYACoIAAABM0sLATDQABZW9jAwOgYAAAEsxlbnAB3wsAACOAAqBAAAAP/W1wAQoA8/ASAAABAG4AE4ACohAAABOpbG/3AAAAhaEAAAATYQSBAzswAAABAAA7AAQAAQYFAHgHAADHBzMArBAAAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/5f5fc12a19de.js b/pr/299/docs/search.index/5f5fc12a19de.js new file mode 100644 index 000000000..93c9c759d --- /dev/null +++ b/pr/299/docs/search.index/5f5fc12a19de.js @@ -0,0 +1 @@ +rn_("AYICoFAAABHmoQAAABN8Y3QjAAKwQA8LAAFldTswAAABAAA3ABYA9gYBAMsLAQCaDAEAEA8BAIsPBQC6DwUANBABAJMQAQAcEQIA1hEDAAQSBAAuEgEANBIBAEMSAQC/EgEA0hIBAPASAQD0EgEAExMAADwTAQBAEwEAXxMAAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/602d50ba42fc.js b/pr/299/docs/search.index/602d50ba42fc.js new file mode 100644 index 000000000..2dac2e2f0 --- /dev/null +++ b/pr/299/docs/search.index/602d50ba42fc.js @@ -0,0 +1 @@ +rn_("AYACoCAAAA8JoDAAABLJYXLzAAF0AaYHAAABBAGgQAAAE01l8mwTAAABAAEFADswAAABAAALAAUAiw8FANYRAQDwEgEAQBMAAF8TAADjAISgMAAADweFAAI7MAAAAQAACwAFAOwFAgDuBgQADwwBALMQAADmEQAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/6043b7b1f86b.js b/pr/299/docs/search.index/6043b7b1f86b.js new file mode 100644 index 000000000..4a591b007 --- /dev/null +++ b/pr/299/docs/search.index/6043b7b1f86b.js @@ -0,0 +1 @@ +rn_("AQAA9GsPAAC5AgEArwDzAAFzOzAAAAEAABkACAAUBQIA5AsCACUPAQDhDwEAHxABAAoRAQCYEgcAgRMDACOAArEQE4YAAWF0") \ No newline at end of file diff --git a/pr/299/docs/search.index/60bbd1af8668.js b/pr/299/docs/search.index/60bbd1af8668.js new file mode 100644 index 000000000..b28b234e3 --- /dev/null +++ b/pr/299/docs/search.index/60bbd1af8668.js @@ -0,0 +1 @@ +rn_("AQUA9CsQAAABAAEAAQATgAKgsAAAEu1jcwEEAPP5DgAAAQABAAGAAqBQAAAP/aBgAAAQbWlyAYACoCAAAAwnoGAAABBsZWgBAQGgIAAAEGti8yMMAAABAAEAE4ECoBAAAAwmYWMBgAKgEAAAErmwMAyzAAFlcyMAAqBAAAATTWV38mwTAAABAAEGAPQSEgAAAQB7AQEAEwACsGASFAABZm3yhwwAAAEAAQEA9DgQAAABAKoAAQAjgAKgsAAAEtRkZwGCAqCgAAASQKBAAAAPpW9284ADZ25yAYACoEAAABINoDAAAA72aGy3HwAAibBADvcAATRAVzswAAABAAAYAAQAzgEAAPwDDQC0CwgAeAwAAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/6133a24a21bd.js b/pr/299/docs/search.index/6133a24a21bd.js new file mode 100644 index 000000000..19db52b63 --- /dev/null +++ b/pr/299/docs/search.index/6133a24a21bd.js @@ -0,0 +1 @@ +rn_("AYACsRATfwABsBAMDAABZW8BBQA7MAAAAQAADQABAMQQDQABAgDzOw8AAAEAAQABAgD0Nw8AAAEAAQABAAEDADswAAABAAAIAAEA4w8IAIcHAACHsKAStwABsNATRAABoFAAABMRjBQR9X8HAACRCAEAAQABAPOBAmx08wABaTswAAABAAB4AAMAKwQAAB4QAAAoEXYAAQUA82UTAAABADUAAQABoEAAABAiZfMWCQAAAQCaCBOAAqAwAAAPFGRy8wABZfMcBwAAAQABAPOAAmh0AQEAOzAAAAEAAHcAAgAeEAAAKBF2ABOAAqBQAAAPVGl0AQgA9OsSAAABAH8ABQATAISw8BNnAC2wkBNLAAGwUBI6AU9AiQA7MAAAAQAAEgAJAJsPAgAPEQEAKRIBAEoSAQB7EgEA3BIBAA8TAQAeEwEAXRMBANcBAACGsIAQ3QABoEAAAAk5CDAmOjAAAAEAAAAAAA0AEAAAAH0AfgCJB3QJxAuyDDEQMhCnEagRIBIhEqoSqxI=") \ No newline at end of file diff --git a/pr/299/docs/search.index/61b0c88b9449.js b/pr/299/docs/search.index/61b0c88b9449.js new file mode 100644 index 000000000..d1f228cb1 --- /dev/null +++ b/pr/299/docs/search.index/61b0c88b9449.js @@ -0,0 +1 @@ +rn_("AQIBsEASrAABZfL0BgAAAQABBADzoA8AAAEAAQBDAAOwQBIaAAGxABLwAAFlZnA7MAAAAQAAIQAGAN0BAAADBwsAEAcLAN8OBQA3EAAAVhIBAGOAA7AwC9IAAWFlaQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/61d6cf934531.js b/pr/299/docs/search.index/61d6cf934531.js new file mode 100644 index 000000000..4a2f7cbd4 --- /dev/null +++ b/pr/299/docs/search.index/61d6cf934531.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgCAEgAA5RIFACOIArBAE1QAAWVzAQQBsDASVgABY/OtEAAAAQABAAEFADswAAABAAAIAAMAHBECAAQSBAATEwAA8wMDY2Vw83QMAAABAAEAAQABsGAS/QABZQEzEwAA8wIBc/Y0CQAAAQABADcHPgAFAQECAaBQAAAQrHIBTRAAAPOAA2ZtcA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/61e64371195c.js b/pr/299/docs/search.index/61e64371195c.js new file mode 100644 index 000000000..316f1059f --- /dev/null +++ b/pr/299/docs/search.index/61e64371195c.js @@ -0,0 +1 @@ +rn_("AQYA9D0QAAABALkAAQATgQOgAAAAEIaxIBOGAAFhZG4BFAD1pBMAAAEAAQABAAEAAQYBsIATRwABdjswAAABAAAHAAIA8hAEALQSAgBXAAABhqDAAAASf6CgAAASoaCQAAASb7BQD7UAAQXgAvRCBwAAAQABADcMAQYA89oRAAABAJoAAQABoGAAAA8icgEfBAAA1wEAgMWgMAAAC84AAIoK") \ No newline at end of file diff --git a/pr/299/docs/search.index/61e80bc02385.js b/pr/299/docs/search.index/61e80bc02385.js new file mode 100644 index 000000000..dbf6e7649 --- /dev/null +++ b/pr/299/docs/search.index/61e80bc02385.js @@ -0,0 +1 @@ +rn_("AQEA9R4IAAABAAEAAQABAAEAAqAwAAAQQ6CQAAAShTBo9SIRAABtAQEAAQABABOAAqAwAAAPBmRyIwKEsBAJCALosCALxADuoDAAAA8qEgAY9eMFAAAuAAEAAQABADOAhLCgEdYAAbBADxAAARBJAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/6373129d4310.js b/pr/299/docs/search.index/6373129d4310.js new file mode 100644 index 000000000..9d01fc7d1 --- /dev/null +++ b/pr/299/docs/search.index/6373129d4310.js @@ -0,0 +1 @@ +rn_("AYMCsQATngABsNATkAABa20BCgDz8BIAAAEAbgATgAKiEAAAE6lsbzMBhKBgAAARn6EAAAATYQSAAzswAAABAAA0AAIAeAcAAMcHMwA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/6383b6d7c4f5.js b/pr/299/docs/search.index/6383b6d7c4f5.js new file mode 100644 index 000000000..47b7c25f2 --- /dev/null +++ b/pr/299/docs/search.index/6383b6d7c4f5.js @@ -0,0 +1 @@ +rn_("AQUA9DQSAAABAIoAAQDzAAFj9YgPAAABALsAAQABAAEJAPNlEwAAAQA1AAENAaCgAAATqXfymRMAAAEAAYECsIATcwABoGAAABOXZHM3AQCEhaBAAAATSbBgExsATQKQCiOAAqBwAAAQh2RyAQsBoEAAABNNZfJsEwAAAQATgAKwMA+jAAFkcvMCAWX0BAgAAAEAAQABACOAArAgC8YAAWV5AQoA88cRAAABAAEAAQsAOzAAAAEAAAcAAQCYEgcAAQIA9PQSAAABAEcAAQC3AACAhbBwEfkAAbDQE44AAWAgBgEGAPPTEQAAAQABAAEDAbBAE2MAAXTyyhIAAAEAI4UCoEAAABL4YnMTAgKgUAAAEJhiZvcYCQAAAQBDAJQGAQBhAAEA84ACaXABBADzoA8AAAEAAQDzAAFwOzAAAAEAAAsAAQAQBwsAI4ADoDAAAAvTsHASrAABYWVp84ADZG1uAQAA9iIJAAD7AqIFAQBQAAEA8wIBZDswAAABAAAJAAMABBEDALoRAwDpEQEAI4ACoJAAABNAaW8BgAOwMBJlAAGAEqIAEAGwUBBHAAFjc3jzAAFlASgFAADnFQAAiaAAAAAMKqBAAAAMerBADHsAAZBIXQHTAQAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/63a6d2637da3.js b/pr/299/docs/search.index/63a6d2637da3.js new file mode 100644 index 000000000..7b5cdec15 --- /dev/null +++ b/pr/299/docs/search.index/63a6d2637da3.js @@ -0,0 +1 @@ +rn_("AQQA9NgPAAABAAEAAQABAAA6MAAAAQAAAAAACgAQAAAAHA8dD/4P/w+LEBYRFxESEhMSjhOPE/MBAXny+xIAAAEAAYABD6dKNNM8bkOAA6AgAAAMeqAQAAAMg21ubwEDAPSYDwAAAQABAA8BAYACoLAAABMRsDASagABbnRDAAOgIAAAEEKwUBEUAAFlaGk7MAAAAQAABgADAM8LAAAjEQMAwRIBAPOAAmRyhwsAAAqAAbwAEAGAAb8AEAGgUAAAELOgUAAAE0agMAAAD8SgMAAAD88wMWNlaW9zdHV39cEHAAABAD4HqgJDAQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/64129e8e0e98.js b/pr/299/docs/search.index/64129e8e0e98.js new file mode 100644 index 000000000..0cdd79ff2 --- /dev/null +++ b/pr/299/docs/search.index/64129e8e0e98.js @@ -0,0 +1 @@ +rn_("AQQBsAASJAABczswAAABAAALAAIAmBIHAIETAwBHAAABhqCAAAASebEAE44AAbBAEecAAbCAE5wAAbBQEiYAARUAFvQDDgAAAQABAAEAAQUA8zQPAAABAAEAY4CEsEAPtwABoAAAAAcPAPAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/657321230ec5.js b/pr/299/docs/search.index/657321230ec5.js new file mode 100644 index 000000000..bd1f1a945 --- /dev/null +++ b/pr/299/docs/search.index/657321230ec5.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAHcAAgAeEAAAKBF2AAEAAbBwErsAAWXyGA8AAAEA8wABc/SADwAAAQD+AwEAM4ADoGAAABDSZGlv") \ No newline at end of file diff --git a/pr/299/docs/search.index/659cd1b8f458.js b/pr/299/docs/search.index/659cd1b8f458.js new file mode 100644 index 000000000..3b343bf58 --- /dev/null +++ b/pr/299/docs/search.index/659cd1b8f458.js @@ -0,0 +1 @@ +rn_("AQYA9LASAAABAAEAAQABDwD0ihMAAAEAAQABAPOHAmhtAQQBsAASEAABZPS6EQAAAQABAAEAI4eEsKATPgABsEASDgABsHAS7gABFYAAAQABoEAAAA/CZTswAAABAAAGAAIAlQcEACMIAQATgAKwUA+AAAFkcgEJAPXPEgAAAQABAHEAAQC3AgCAhrAgEcMAAVU1iV1+GhFJAQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/65b95eb2cbb1.js b/pr/299/docs/search.index/65b95eb2cbb1.js new file mode 100644 index 000000000..0b2736f43 --- /dev/null +++ b/pr/299/docs/search.index/65b95eb2cbb1.js @@ -0,0 +1 @@ +rn_("AYIBfODbmy34dAEAAbBgEv0AAWUBMxMAAPMCAXP2NAkAAAEAAQA3Bz4ABQEBAgGgUAAAEKxyAU0QAADzgANmbXA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/66080551a4d4.js b/pr/299/docs/search.index/66080551a4d4.js new file mode 100644 index 000000000..e86576487 --- /dev/null +++ b/pr/299/docs/search.index/66080551a4d4.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABLUcwEgEAAAAYACsDAPtQABsFAPtwABYWkBgAKgsAAAE1ugMAAAEExsbhcBAACFoCAAABGpoCAAAAkJsIATKAABEJEEOjAAAAEAAAAAABAAEAAAAP4EMgUzBTQFNQU2BTcFcwnuDu8ORhL4Ev8SCxMMEyQTJRMBAgA6MAAAAQAAAAAACgAQAAAAXQleCVQPaBJpEpQSlRI1E4gTnhOfEwEAADswAAABAAAGAAIAgBIAAOUSBQABAgGwMBJWAAFjOjAAAAEAAAAAABMAEAAAAJ8MBg+YD5kPmg+eD58Pow+kDzYQqRCtEK4QrxAfEUwSTRImE0sTTBMBggKwMBMcAAGwQBCWAAFhZQEBAbBAE04AAWX1FBMAAAEAAQABAAEAE4ACoBAAAAvQYW/HBwAAx7BAE1QAAaCAAAARpABMiAnyHAgAALgDAYACoIAAABJvoJAAABJJYWsBAwDzxxEAAAEAAQAjgISxIBOcAAGgAAAACT2gMAAADw0EBAYBCAD1pBMAAAEAAQABAAEAE4oCsEATfwABYXKTAISgwAAAEkWhAAAAEwgBQAbynwcAAAEAAQMBoEAAABEZZfOoDAAAmAMBAAEFAPSwEgAAAQABAAEAU4ADsAASagABZXN0AQYA9IoTAAABAAEAAQDzCAFt9LYRAAABAAEAAQABAgA7MAAAAQAABQABAHUTBQDzDQFtOzAAAAEAAAUAAQA2EwUAAQQA97AMAAABAA0FDAABAIMAAQABAwGAEnMAEO1zOzAAAAEAAAUAAQBYEgUAI4ICoJAAABNKaXMBgAKgsAAAExGwMBJqAAFudAGAAqCAAAAShbBAERQAAWFlAYACoBAAABBCoAAAABCIbnIBCQGgQAAAE01l8mwTAAABACOAAqCQAAASumlsxyMAAAqgMAAAEEOwUBIiAAGw8BOSAAGwcBKCAAGg0AAAE1YwYmNlaGlrbm92OzAAAAEAABgACwDPCwAAGhADACIRBAB/EgAAjxIDAMESAQAIEwAADRMBACgTAQA0EwAARxMBAAECAqCwAAATQKCwAAATQWNz8/cPAAABAF4AAQECsFARFAABoMAAABMRaGnywRIAAAEA84ACZWkBAQGgkAAAEn9z9NwPAAABAAEAAQABAwGwABAUAAFzAYcPAADzgANoaXQBAQGgUAAAEg1jAXgMAAABBQGwgBNHAAF2OzAAAAEAAAcAAgDyEAQAtBICAAEBAPSmDwAAAQCpAAEA84ADY2Rs92UPANGwkBGiAAGgUAAAEDuhAAAAE3ugoAAAEqGgEAAAB3dAP78pOzAAAAEAADcAFgAlBQEA4AUCAEIHAgC+CwMAewwBAPkOAgAIDwAALg8AAFUPAQBZEAgAsBAAAN4RAQDnEQEA6xEBAEESAQBeEgQArhIBALcSAQC6EgAALhMEAHsTAACYEwAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/660ac0c7b41d.js b/pr/299/docs/search.index/660ac0c7b41d.js new file mode 100644 index 000000000..1aca980a1 --- /dev/null +++ b/pr/299/docs/search.index/660ac0c7b41d.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABMmZfJQEwAAAQABBAGwABIQAAFk9LoRAAABAAEAAQCnAAAChbCgEz4AAbBAEg4AAbBwEu4AARWIAAESEwAAAQABsIATKAABcPIkEwAAAQBDAAOgIAAAEIigQAAAEb5kZXPzLg8AABMDAQDzgAJlaQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/663d3ccd45e8.js b/pr/299/docs/search.index/663d3ccd45e8.js new file mode 100644 index 000000000..95003c14d --- /dev/null +++ b/pr/299/docs/search.index/663d3ccd45e8.js @@ -0,0 +1 @@ +rn_("AYADsAAS1QABsAAS1wABsHATRwABNDZhAQQA9JUTAAABAAoAAQAjgQKgQAAAEIZtcAEBADswAAABAAAIAAMA3BEAAEUSAACiEwYAxwEAAMWgQAAAD4qgYAAAEvpABYAIOzAAAAEAABYACAArDwEAPRABAIIQAwDyEAYAtBICAAATAABXEwEAhhMBAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/672d94eeb5ad.js b/pr/299/docs/search.index/672d94eeb5ad.js new file mode 100644 index 000000000..468c9dd2f --- /dev/null +++ b/pr/299/docs/search.index/672d94eeb5ad.js @@ -0,0 +1 @@ +rn_("AQcA9CMRAAABAAEAAQAjAQKgcAAAESJhcgGhBwAAAQMAOzAAAAEAAAcAAgDfDgUAVhIBAPMAAmN0OzAAAAEAAFcAJgAIBQsAtwUBAB4JAQDjCwAA6QsCAJQMAACfDAIAsAwBAAYPAAALDwEADg8BABQPAACYDwIAng8BAKMPAQDCDwAANhAAADwQAACJEAEAlhABAKkQAQCtEAIACBEBABkRAgAfEQAAvhEAAMURAQDKEQEADhIBABoSAwA8EgEATBIDAFISAQCsEgEAvRIAACYTAABLEwQAVBMCAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/679f4284e04c.js b/pr/299/docs/search.index/679f4284e04c.js new file mode 100644 index 000000000..c43c138f9 --- /dev/null +++ b/pr/299/docs/search.index/679f4284e04c.js @@ -0,0 +1 @@ +rn_("AYMCoJAAABMgoJAAABMhMjMBgAKgUAAAE0qggAAAEnZjZfMAAnN08mcHAAABAAEAAaCwAAASvWL1URIAANEAAQBGAAEAI4ACsAAPKQMnZHIBAAGwgBMoAAFw8iQTAAABAEMAA6AgAAAQiKBAAAARvmRlc/MuDwAAEwMBABOCAqAgAAATEmVp84ACY2UBBAA7MAAAAQAACQAEAGgJAQDKDwMACBABAN4SAQCzgISgkAAAE3sQAUI=") \ No newline at end of file diff --git a/pr/299/docs/search.index/67d96877f75a.js b/pr/299/docs/search.index/67d96877f75a.js new file mode 100644 index 000000000..bd192d081 --- /dev/null +++ b/pr/299/docs/search.index/67d96877f75a.js @@ -0,0 +1 @@ +rn_("AQkA82UTAAABADUAAYACsCATCQABoCAAAA8NaXkBCQGggAAAE5hmAYwSAAABAwGwABMbAE1zAckSAAAjgAKgIAAADwlhcgECAPUCEAAAAQABADICAQDzAANpdHU6MAAAAQAAAAAADQAQAAAApgc9CWsP4A/0ESQSJRLNEs4S1BL9Ev4SGRMaEwGAArBAEAYAAaAgAAATSWN0E4ACsLASpgABYWwBDAGgoAAAE6l38pkTAAABAAEBAPMvDwAAAQABAPOAAnR1AQMA88cRAAABAAEAAQEA9WYQAAABAAEAAQABACOAArCQEvYAAWF0AQQBoEAAABNNZfJsEwAAAQABAgA7MAAAAQAACwAFAIsPBQDWEQEA8BIBAEATAABfEwAAE4ICsPATnAABZmcBgQKwgBNzAAGgYAAAE5dkcwEIAPUuEwAAAQABAAEAAQDzAgFm9cUPAAABAAEAAQABACOAA6AgAAAPB7CgE3EAAWRydHfvAQCNsBATKAABsOATKgABl9UOOzAAAAEAAAUBEgAWBAEAFAUCAOwFAgDuBgQAFggDAOQLAgAPDAEAiQwBAAoO1AAlDwEA4Q8BAPIPAgAfEAEAsxAAAAoRAQDmEQAAmBIHAIETAwA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/67f7a312869a.js b/pr/299/docs/search.index/67f7a312869a.js new file mode 100644 index 000000000..d03d1ec18 --- /dev/null +++ b/pr/299/docs/search.index/67f7a312869a.js @@ -0,0 +1 @@ +rn_("AQMA9qUMAAB8AlgAAQCeAgEAAQQAOzAAAAEAAAoABAC6DwUA2BEBAC4SAQBBEwAAAYYCoAAAABJQoAAAABJRZHLzAgF2AXYSAADzgAJlbycBAADHoDAAAA8goHAAABGsoIAAABMSoNAAABK5oLAAABMSQEUICTswAAABAAAJAAMABwYCAEsHBAASDAEAI4ACcpo+9Xx6ZHIBAAA6MAAAAQAAAAAACgAQAAAAXQleCVQPaBJpEpQSlRI1E4gTnhOfExOAAqAwAAAP0Gx084ACYWU=") \ No newline at end of file diff --git a/pr/299/docs/search.index/681c1ab8da48.js b/pr/299/docs/search.index/681c1ab8da48.js new file mode 100644 index 000000000..f41ebd266 --- /dev/null +++ b/pr/299/docs/search.index/681c1ab8da48.js @@ -0,0 +1 @@ +rn_("AQEBoGAAABN8c/L9EAAAAQABCAD1LhMAAAEAAQABAAEAowCEsCAJewABoIAAABH0MEAIOzAAAAEAAAUAAgC5AQAAxQ8EAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/682a70b084b3.js b/pr/299/docs/search.index/682a70b084b3.js new file mode 100644 index 000000000..bce9127e5 --- /dev/null +++ b/pr/299/docs/search.index/682a70b084b3.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAcAAQBfCQcAAQIA938PAADPAIECAQABAHEAAQATgAKwMBAPAKZpdPMAAXPycRMAAAEAM4ADsRAS7gABZG5w") \ No newline at end of file diff --git a/pr/299/docs/search.index/69247c51b33f.js b/pr/299/docs/search.index/69247c51b33f.js new file mode 100644 index 000000000..d5c7832b8 --- /dev/null +++ b/pr/299/docs/search.index/69247c51b33f.js @@ -0,0 +1 @@ +rn_("AQ0A9c8SAAABAAEAcQABAAGAAqEQAAATCLBwExwAAW5yAQcA9N8QAAABAAEAAQAzAASgIAAACTmw4BL9AAExZWly8/EDAACGA6MBAQIA9DEQAAABAHUBAQABAwD0nBAAAAEAWgEBAPMAAmJ1AU0PAAABAwD0LxAAAAEAdQEBAPOEAmRzAQIA8xwJAAABAFQA84ACaXQBGADzphMAAAEAAQABgAKwsBLNAAGgwAAAEnpldfOBAmNtAQ0BsEATTgABZfUUEwAAAQABAAEAAQATggKwABBHAAFrcwEEAPTYDwAAAQABAAEAAYECoCAAABBCsEASagABZWnzggJldAEEAaBgAAARzHL03QsAAAEAbQMBAPOAA2FlbwEJAPb1EQAAAQDqAIAAMAABABOAArBgD/AAAWRuAQYA9RoQAAABAAEAAQBiAgEDAaBwAAASKGf0hAcAAAEA6gEBAAEJAPNlEwAAAQA1AAGBArCAE3MAAaBgAAATl2RzFwEAiIWw0BOZAAGgQAAAE0mwYBMbAE0CkAoBBAD0uxIAAAEASgABAFOAA7CQE5IAAWVobwEAAaBAAAAQqWUBAA8AACOBArBQEFQAAWt0d/wBAI2hkAAAE4mwsBI2AAGgoAAAEfT9gQ87MAAAAQAAGQAIAG0AAABzAAMAuwUBAGIHAwBECQMA+wsHAIUPAQCkEQAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/6978b4ea0875.js b/pr/299/docs/search.index/6978b4ea0875.js new file mode 100644 index 000000000..49727be56 --- /dev/null +++ b/pr/299/docs/search.index/6978b4ea0875.js @@ -0,0 +1 @@ +rn_("AQYA9LASAAABAAEAAQABDwD0ihMAAAEAAQABAAEAAbDwE5IAAWv0CBMAAAUAAQAmAAECAPSmDwAAAQCpAAEA9wAAAIWxABN/AAGJMADy3hEAAAEAE4ACsBAMDAABZW8BBQA7MAAAAQAADQABAMQQDQABAgDzOw8AAAEAAQABAgD0Nw8AAAEAAQABAAEDADswAAABAAAIAAEA4w8IAAGAAqAAAAAL4rAwE04AAXFyAYACoEAAABA3sMATRAABZXMBAQA7MAAAAQAAEwABAFcPEwAjgAKwkBK3AAFpbXc2AACKoFAAABMRsHAS9gABoCAAAAyVnFUROzAAAAEAAAkAAwB/BwAAEBADABQTBAABAgGwMBCeAAFwAasRAACzgISgIAAAEQMCKAQ=") \ No newline at end of file diff --git a/pr/299/docs/search.index/6a74a6193ac1.js b/pr/299/docs/search.index/6a74a6193ac1.js new file mode 100644 index 000000000..cf54e1ad9 --- /dev/null +++ b/pr/299/docs/search.index/6a74a6193ac1.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAcAAgAyDAUAgBABAAEAAbBQEyoAAXIBIgUAAFMBhLBAEcUAAbBAE2MAAZgABDowAAABAAAAAAAoABAAAAAcBB0EHgTfBRwJHQlDCXEJfQwFD3EPcg9/D6sPAhADEAQQThCpEQASARICEgMSKBI2EjcSYxJkEsoSyxLPEtAS0RLhEuISQhNDE1kTWhN9E34T") \ No newline at end of file diff --git a/pr/299/docs/search.index/6bc821d036fc.js b/pr/299/docs/search.index/6bc821d036fc.js new file mode 100644 index 000000000..79600e540 --- /dev/null +++ b/pr/299/docs/search.index/6bc821d036fc.js @@ -0,0 +1 @@ +rn_("AQUA9PQSAAABAEcAAQDzAwFj8gkSAAABACOAArBwE5wAAWhvAYICsAAR5wABsAARDAABZXNDAAOgoAAAE0GgIAAAEoRlaXT1+REAAAEAeQABAO0AAYECoEAAABJ6sEASOAABYWYBgAKwcBHeAAGgsAAAEwhlbwECADswAAABAAAGAAEAjBAGAAECAaBAAAATJmXyUBMAAAEA84ACaW8BCAD06xIAAAEAfwAFABMBA7CQE0sAAbBQEjoBT2dscDswAAABAAASAAkAmw8CAA8RAQApEgEAShIBAHsSAQDcEgEADxMBAB4TAQBdEwEAAYACsEARwQD9oCAAABBPYWkBgQKwMBL7AAGgMAAAEd1jaQEDAbAAEiQAAXM7MAAAAQAADQADAAoRAQCYEgcAgRMDAAEDAPUIEQAAAQAFAQEARwHzgAJydgEAAPS/EQAAAQBQAAEA8wIBZDswAAABAAAJAAMABBEDALoRAwDpEQEAI4ACoJAAABNAaW8TgAKw8BOOAAFsbwEFAPRcEwAACwAeAA8AI4ACoGAAABJ5Z3MjgAKwkBM+AAFibPeXAwCPsHARIAABsGATcQABsHAS7gABsFASJgABtfkXOzAAAAEAADsAFwD9BQAAkwcBAAIJBQASCQAAQAkAANALAAAWDAEAhAwCAKsMAQADDgMACA4BADIPBAD3DwEAMxAAAFYQAAB2EAkAtBAAALYQAwACEQAAMBIAAEcSAQBYEgUAEhMAAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/6bcf985f9cc0.js b/pr/299/docs/search.index/6bcf985f9cc0.js new file mode 100644 index 000000000..93e502813 --- /dev/null +++ b/pr/299/docs/search.index/6bcf985f9cc0.js @@ -0,0 +1 @@ +rn_("AQIA9NkQAAABAAEAAQABAQDz7xAAAAEAAQATgAJmPTzNRehlcwEKAPO0EgAAAQABAAGAArAgEyoAAaBgAAASgW92AYACoDAAABHMoAAAABJAY2TzgIUFEAY=") \ No newline at end of file diff --git a/pr/299/docs/search.index/6c19dc66c62e.js b/pr/299/docs/search.index/6c19dc66c62e.js new file mode 100644 index 000000000..5a275e3d8 --- /dev/null +++ b/pr/299/docs/search.index/6c19dc66c62e.js @@ -0,0 +1 @@ +rn_("AQIA9DEQAAABAHUBAQABAAD0Jw8AAAEA5gADASOCArBQEk4AAWVzAQIA9CASAAABAIkAAQABAwD0VBIAAAEAjgABAPOFAmJ1AQIA9JwQAAABAFoBAQDzgAJpbPMAA2J0dTowAAABAAAAAAAPABAAAACaByUIJggnCCgITQ8FEKESGxNlE2YTaBNzE3QTlxObEwEMAPMDEwAAAQABAAEGAPQeEgAAAQCJAAEA8wMBYvQvEAAAAQB1AQEAFwYAAIewQA8lAAGgcAAAEbGwEAkrAAGwkBJMAAE4CQM6MAAAAQAAAAAAIQAQAAAACgYLBgwGXwdgB5wMnQyeDKIMowykDB4PHw9vD3APdA91D5EPkg+TD5QPlQ+WD5cP0w8AEAEQgRKlEskSXBNnE4UTlBM=") \ No newline at end of file diff --git a/pr/299/docs/search.index/6c5b31781505.js b/pr/299/docs/search.index/6c5b31781505.js new file mode 100644 index 000000000..f22950253 --- /dev/null +++ b/pr/299/docs/search.index/6c5b31781505.js @@ -0,0 +1 @@ +rn_("AYACsHAQngABsCATRAABYWQBBgD0QxIAAAEAjgABACOAArCgEyoAAXN5AQYA9D0QAAABALkAAQCTgISgAAAAEIaxIBOGAAEJIAHzgAJlaQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/6c87bbe255e2.js b/pr/299/docs/search.index/6c87bbe255e2.js new file mode 100644 index 000000000..e60b3dfda --- /dev/null +++ b/pr/299/docs/search.index/6c87bbe255e2.js @@ -0,0 +1 @@ +rn_("AYkCsCATWQABoDAAABNiaXATAAOgIAAAEiiggAAAEjtlaXX10QsAADIAAQABAAEAAYYCsJATfQABoAAAABHCYmQBgAKwMBIcAAGgMAAAEB9ydnMAhKAwAAAScBAAEzowAAABAAAAAAAJABAAAAAMCQ0JDgk+CT8JtA+5D3AQcRD/EQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/6cc490e2f6c0.js b/pr/299/docs/search.index/6cc490e2f6c0.js new file mode 100644 index 000000000..83832c930 --- /dev/null +++ b/pr/299/docs/search.index/6cc490e2f6c0.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgCAEgAA5RIFACOIArBAE1QAAWVzAQQBsDASVgABY/OtEAAAAQABAAEFADswAAABAAAIAAMAHBECAAQSBAATEwAA8wEDY2Vw83QMAAABAAEAAQIA9ioJAABQADYGAQCxAAEA84ACbnI=") \ No newline at end of file diff --git a/pr/299/docs/search.index/6e48f0d21453.js b/pr/299/docs/search.index/6e48f0d21453.js new file mode 100644 index 000000000..ff1ece725 --- /dev/null +++ b/pr/299/docs/search.index/6e48f0d21453.js @@ -0,0 +1 @@ +rn_("AQAA9DEQAAABAHUBAQAjgQKgYAAAEjtiaQEAAPQnDwAAAQDmAAMBI4ECsFASTgABZXMTgAKwAAd6AdNhcgGJArAgE1kAAaAwAAATYmlwAQIA9CASAAABAIkAAQABAwD0VBIAAAEAjgABAPOFAmJ1AQIA9JwQAAABAFoBAQDzgAJpbLcBAACFoCAAABIoEgEMOjAAAAEAAAAAABYAEAAAAJoHJQgmCCcIKAjRCwMMBAwFDAYMTQ8FEGoSaxKhEhsTZRNmE2gTcxN0E5cTmxM=") \ No newline at end of file diff --git a/pr/299/docs/search.index/70212e5b30bb.js b/pr/299/docs/search.index/70212e5b30bb.js new file mode 100644 index 000000000..13a10b901 --- /dev/null +++ b/pr/299/docs/search.index/70212e5b30bb.js @@ -0,0 +1 @@ +rn_("AYQCoCAAABBOoDAAABC1aXQBAQA7MAAAAQAAdwACAJsHAAB/CXYA8wACZWzzbBAAACECAQABAgA7MAAAAQAA1gACAM0LAAC1DNUAxwAAAIWwsBI8AAGgEAAAErmwMAyzAAEUMAI7MAAAAQAAAgELAKcBBgDpBQAAFQbVAGkHBwB2DwIAxBANAOwQAgCuEQEAJhIBAHESAQADEwIA") \ No newline at end of file diff --git a/pr/299/docs/search.index/71646ec45998.js b/pr/299/docs/search.index/71646ec45998.js new file mode 100644 index 000000000..ec9ad8c92 --- /dev/null +++ b/pr/299/docs/search.index/71646ec45998.js @@ -0,0 +1 @@ +rn_("AQUA9PQSAAABAEcAAQDzBAFj8gkSAAABACOCAqBQAAAR3WZ0AQABsAATRwABZPS+CwAAAQABAAEA84ACZWkTgAKw4BNSAAF0dg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/71e93f057075.js b/pr/299/docs/search.index/71e93f057075.js new file mode 100644 index 000000000..6aa692a0e --- /dev/null +++ b/pr/299/docs/search.index/71e93f057075.js @@ -0,0 +1 @@ +rn_("AQQA9KIRAAABAFMBAQDzAAFsOzAAAAEAAAgAAwB1CQEAChABAGYQBAABAAGwYBL9AAFl9AkMAAABAAEAKAcBBQD0MhIAAAEAOwEBAAEHADswAAABAAAFAAEAhhIFAPMCAXQ7MAAAAQAACAACAMULAADxCwcA84ACYWUBgAKxEBN/AAGwEAwMAAFlbwEFADswAAABAAANAAEAxBANAAECAPM7DwAAAQABAAECAPQ3DwAAAQABAAEAAQMAOzAAAAEAAAgAAQDjDwgAhwcAAIewoBK3AAGw0BNEAAGgUAAAExGMFBH1fwcAAJEIAQABAAEA84ACbHQTgAOgIAAADHewoBMsAAFhZG4BgAKwgBILAAGwoBJ3AAFpdAEOAPQiEwAAAQBGAAEAE4ACsEAPhQABbHIBBgD0EBAAAAEAAQABAEcFAACHoNAAABNGsFAPgwABoAAAABJ5sPATJAABBZED8ioPAAClAAECAPRMEgAAAQD+AAEAI4ACoCAAAAvQaXIBCAD0thEAAAEAAQABAAEEAbAAEBQAAXMBhw8AAAECAaBQAAASDWMBeAwAALcCAACHsEAMsAABsBASagABoAAAABJGlRADOjAAAAEAAAAAAA0AEAAAACUFJgXgBeEF4gV7DHwM+Q76DvsOCA+3ErgSuhIBgAKwIAk6AAGg4AAAEu1ibPcWAACJsBAF2gABsCAHnwABkQlHOzAAAAEAAIUACgArBAAANAkCAB4QAABtEAAAqxAAACgRdgCgEQMAsBEAAHASAAD2EgEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/729a3ef57c7a.js b/pr/299/docs/search.index/729a3ef57c7a.js new file mode 100644 index 000000000..9714162c4 --- /dev/null +++ b/pr/299/docs/search.index/729a3ef57c7a.js @@ -0,0 +1 @@ +rn_("AYICsEARzQABsEARzwABNDYBBgD0lRMAAAEACgABAPMCAXIBABMAAPMDAWX0rg8AAAEAqAABAFOAA7CwExwAAWFlaQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/729ca138be4c.js b/pr/299/docs/search.index/729ca138be4c.js new file mode 100644 index 000000000..8aabef055 --- /dev/null +++ b/pr/299/docs/search.index/729ca138be4c.js @@ -0,0 +1 @@ +rn_("AQUA9NERAAABAJoAAQABCAD0fRIAAAEAgwABADMBA7BAElIAAWFiZfWYDAAAAQDTAkECwgABAAD15wsAAAEACwMBAAEA84ACc3UjgQKwgBJ3AAFydAEGAPSyDwAAAQCxAAEAUwCEsIASCwABoKAAABOpAgEU9cMFAAABAIUNUAABAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/74c41d060fa6.js b/pr/299/docs/search.index/74c41d060fa6.js new file mode 100644 index 000000000..d4070dcbb --- /dev/null +++ b/pr/299/docs/search.index/74c41d060fa6.js @@ -0,0 +1 @@ +rn_("AQUA9NERAAABAJoAAQABCAD0fRIAAAEAgwABADMBA7BAElIAAWFiZTowAAABAAAAAAAJABAAAABdB14HmAyZDAEPbA+1D7YPrRFvEgEAADswAAABAAAeAA4AIwUBANEFCAA3CQEA5wsBAIsMAQAHDgAA8w4CAAkPAAAnDwEADhABACEQAAC1EAAADBEBABERAAABgQKwABClAAGwABCnAAE0NiOAArCAE3EAAW9wAQABsEASTgABZfOwEAAANwEBALcBAICFoGAAABBNEAEL") \ No newline at end of file diff --git a/pr/299/docs/search.index/7601f1395e7f.js b/pr/299/docs/search.index/7601f1395e7f.js new file mode 100644 index 000000000..4330d0c33 --- /dev/null +++ b/pr/299/docs/search.index/7601f1395e7f.js @@ -0,0 +1 @@ +rn_("AQQA8+8QAAABAAEAAYICsEARzQABsEARzwABNDYBBgD0lRMAAAEACgABAPMCAXIBABMAAPMCAWX0rg8AAAEAqAABABOAAqBQAAAR/2R00wCEsLATHAABEQEB9xYQAAABAG8AHAABAAEAHgE=") \ No newline at end of file diff --git a/pr/299/docs/search.index/76f46ceef4bd.js b/pr/299/docs/search.index/76f46ceef4bd.js new file mode 100644 index 000000000..51026b5c1 --- /dev/null +++ b/pr/299/docs/search.index/76f46ceef4bd.js @@ -0,0 +1 @@ +rn_("AYACsAAQpQABsAAQpwABNDYBBAGgoAAAE6l38pkTAAABAAGAAqCAAAATNLCwEw0AAWVvYwIDoGAAABLMZW5wAd8LAADzAAJhdgEPBwAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/77281c5ced0c.js b/pr/299/docs/search.index/77281c5ced0c.js new file mode 100644 index 000000000..2c53bd057 --- /dev/null +++ b/pr/299/docs/search.index/77281c5ced0c.js @@ -0,0 +1 @@ +rn_("AQEBsFAPEAABY/QDBQAAAQABAK8AAQABoIAAABOYZgGMEgAA8wYBc/KnDAAAHAQzAAOgYAAAE3xjaXPy/RAAAAEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/77607e6e8760.js b/pr/299/docs/search.index/77607e6e8760.js new file mode 100644 index 000000000..c47bc9852 --- /dev/null +++ b/pr/299/docs/search.index/77607e6e8760.js @@ -0,0 +1 @@ +rn_("AQAA9EoJAAABACMHAQABCQGggAAAE5hmAYwSAAABAgD1AhAAAAEAAQAyAgEA8wACaXU6MAAAAQAAAAAABwAQAAAA4A/0Ec0SzhL9Ev4SGRMaEwEIAPUuEwAAAQABAAEAAQDzAgFm9cUPAAABAAEAAQABABOAArCgE3EAAXJ08wACYXPz8g8AAAEAAQDzgAJ0dg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/789bd808f118.js b/pr/299/docs/search.index/789bd808f118.js new file mode 100644 index 000000000..5b77e1277 --- /dev/null +++ b/pr/299/docs/search.index/789bd808f118.js @@ -0,0 +1 @@ +rn_("AQMBsDASVgABY/OtEAAAAQABAAEDAYAScwAQ7XM7MAAAAQAABQABAFgSBQAjggKgkAAAE0ppcwcBAIDFsFASIgABoKAAABK6sHASggABoNAAABNWgAEYCPOAAmRy") \ No newline at end of file diff --git a/pr/299/docs/search.index/78e2e859c206.js b/pr/299/docs/search.index/78e2e859c206.js new file mode 100644 index 000000000..1705dcf4f --- /dev/null +++ b/pr/299/docs/search.index/78e2e859c206.js @@ -0,0 +1 @@ +rn_("AYACoAAAAA/AoAAAAA/BNDYBgAOwABLVAAGwABLXAAGwcBNHAAE0NmEBBAD0lRMAAAEACgABACOBAqBAAAAQhm1wAQEAOzAAAAEAAAgAAwDcEQAARRIAAKITBgDHAQACxaBAAAAPiqBgAAAS+kAFgAg7MAAAAQAAFgAIACsPAQA9EAEAghADAPIQBgC0EgIAABMAAFcTAQCGEwEA8wACZXY6MAAAAQAAAAAAEQAQAAAA2QHaAfcFpgzsDu0OBw8gD64Prw9XEFgQzRHOEc8R0BEcEx0TAYECsHAQggABsHAQhAABNDYjgAKwEAeKAAFucAEDArBAEhwAAaAwAAAScGV39LQPAAAFALcAAQATgQKgcAAAEqVnbyOAArBQDysAAWR0VwEAAIWgEAAAD4qxIBNXAAERIQH0+RAAAAEAAQABAA==") \ No newline at end of file diff --git a/pr/299/docs/search.index/7a4fbb02a71b.js b/pr/299/docs/search.index/7a4fbb02a71b.js new file mode 100644 index 000000000..0d1a7de32 --- /dev/null +++ b/pr/299/docs/search.index/7a4fbb02a71b.js @@ -0,0 +1 @@ +rn_("AQMA9CAJAAABAPoCAQABAwDzGAwAAAEAAQDzgAJpbwEBAbBQEDQAAWP0hgcAAAEAAQDqASOBAqBQAAAP9mNuAQQAOzAAAAEAAAcAAwDsEAIAcRIBAAMTAgABAQA6MAAAAQAAAAAABwAQAAAADxEQESkSKhJ7EnwSHhMfE/MJAWkBMxAAANcCAACHsMASagABoFAAAA7woRAAABNGFAEXOzAAAAEAACMAAQCBASMA") \ No newline at end of file diff --git a/pr/299/docs/search.index/7b1c227ec211.js b/pr/299/docs/search.index/7b1c227ec211.js new file mode 100644 index 000000000..ca25740c8 --- /dev/null +++ b/pr/299/docs/search.index/7b1c227ec211.js @@ -0,0 +1 @@ +rn_("AQIA99YLAAABAA4DAQBBAVsCAQABAwD0Hg8AAAEA4QABAAGBAqAAAAAQsKAQAAAPAWV184ACYXIBgQKgkAAAEuKwUBGuAAFidxOAArCQEi4AAW5y8wACZXT07g8AAAEAFwABAAEBAPUtDAAAAQABAAEAAQATgAKwsBLeAAFhbyMAArBADKAAAWV0OzAAAAEAAAkABAAeBQEAywUBABQMAQDZEAMAAQIAOzAAAAEAAAkAAgAMCAcAFBIBAAGAArCQEnEAAaCgAAAS4WV2E4ACsYATpAABY20BggKwQBMLAAGgUAAAEQ5iZhOAAqBAAAATM2RzI4ACoNAAABN8Y27zgIQRAUABBQGwQBJSAAFlAa0RAABDggOhAAAAE4ig0AAAE2BrbXMTgAKwYBAKAAFhcgEFADswAAABAAB3AAIAHhAAACgRdgABggOwIBIWAAGwIA+sAAGwQBCgAAFrbHNTgAOwIBNjAAFrdHUBiYSggAAAE5egQAAAE0mgYAAAE2igkAAAE5sCgAoTgoSgoAAAEuCxoBOnAAGw0BNCAAEEAQkBgAKxcBOiAAGg0AAAExpjbyOAArBgEAgAAWlvAQAA9OALAAABAEoAAQAjgQKg4AAAEzVleQENADowAAABAAAAAAAHABAAAABKEksS3BLdEg8TEBNdE14TAYECsEASaAABoMAAABMZZW8BAgA6MAAAAQAAAAAACAAQAAAAFQ8WD7oQ9RH2EeASYBOQE5ET8wEBbzswAAABAAAJAAQAaAkBAMoPAwAIEAEA3hIBAPOAA21ucgEAADswAAABAAAGAAIAgBIAAOUSBQBDgAOwQBNUAAGgIAAADJ9lcnMjAAKwMBLBAAFjcvKuEgAAAQABgQKwgBIaAAGhAAAAE19jZgGAAqAAAAAPwKAAAAAPwTQ28wIBdgGmDAAA8wABZAEpCQAA1/4hAJKwoBMJAAGwcBEAAAGwgBH9AAGgsAAAEjCgcAAAD/WwAAlGAAGd3X87MAAAAQAAFgALAHQBAAAaBQMAyAUCACsHAQA1BwIA7gsAAC0PAACHEAAA7xACALMRAgBGEwAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/7b6c1a81028e.js b/pr/299/docs/search.index/7b6c1a81028e.js new file mode 100644 index 000000000..e6be82355 --- /dev/null +++ b/pr/299/docs/search.index/7b6c1a81028e.js @@ -0,0 +1 @@ +rn_("AQgAOjAAAAEAAAAAAAcAEAAAAEoSSxLcEt0SDxMQE10TXhMjgAKwUBI+AFVmbgEAAbBAEhoAAWU7MAAAAQAADAACAAMHCwA3EAAAI4ACoDAAAAvSYWUzgISwUBKNAAGwEBMqAAEIYAE=") \ No newline at end of file diff --git a/pr/299/docs/search.index/7bb9d7afc6de.js b/pr/299/docs/search.index/7bb9d7afc6de.js new file mode 100644 index 000000000..7f5c14c95 --- /dev/null +++ b/pr/299/docs/search.index/7bb9d7afc6de.js @@ -0,0 +1 @@ +rn_("AQYA9NoSAAABAFEAAQAjgAKhEAAAE4ljchOAAqAgAAAJG2h5AQEBoGAAABN8c/L9EAAAAQAjgAKwIAl7AAFldQGAArAgD1UAAaCQAAATM2FlAQcBsEATTgABZfUUEwAAAQABAAEAAQABBAGwABIkAAFzOzAAAAEAAAsAAgCYEgcAgRMDAEcAAASGoIAAABJ5sQATjgABsEAR5wABsIATnAABsFASJgABFQAW9AMOAAABAAEAAQCDAISgAAAAEvqwQBOSAAGwMBKsAAFIAAP3GQUAAB8LAQCkAAEABQABAAEDAbBAE2MAAXTyyhIAAAEAI4UCoEAAABL4YnMBAQD0+RAAAAEAAQABABOAAqAQAAAJPGRst+gAAIyg0AAAEoSgUAAAEJigkAAAEqCwUBILAAGgMAAADCK3mAc7MAAAAQAAWgATAMcBAQDMAQEA+AMDAA0GAAD0BgEAigcBAJwHAAAYCQEAJAkAAC8JAABcCQAAOQw3AIMMAAAjDwEA8A8BAAwQAQBSEAEA1BAEAMMRAQA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/7bc6215ea521.js b/pr/299/docs/search.index/7bc6215ea521.js new file mode 100644 index 000000000..5033277d3 --- /dev/null +++ b/pr/299/docs/search.index/7bc6215ea521.js @@ -0,0 +1 @@ +rn_("AQIA9NkQAAABAAEAAQABCgDztBIAAAEAAQDzgQJtdAEGAPOZEAAAAQABAAGCArBgEkEAAbDQE1AAAWlyE4ACsBAMjgABZWkBBQD09BIAAAEARwABAPMEAWPyCRIAAAEAI4ICoFAAABHdZnQBAAGwABNHAAFk9L4LAAABAAEAAQDzgQJlaTcDAACIsTATbAABoFAAABAqsJASGAABsEAQKAABlRAHAScTAAA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/7c19fbc10374.js b/pr/299/docs/search.index/7c19fbc10374.js new file mode 100644 index 000000000..2767d335f --- /dev/null +++ b/pr/299/docs/search.index/7c19fbc10374.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAcAAgAyDAUAgBABAAECAPOiEAAAAQABAAEGAPSKEwAAAQABAAEA8wYBbfS2EQAAAQABAAEA84ACbnbzgAJhaQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/7c3ea2f4edb6.js b/pr/299/docs/search.index/7c3ea2f4edb6.js new file mode 100644 index 000000000..1ece895c5 --- /dev/null +++ b/pr/299/docs/search.index/7c3ea2f4edb6.js @@ -0,0 +1 @@ +rn_("AQQBsAASJAABczswAAABAAALAAIAmBIHAIETAwBHAAAEhqCAAAASebEAE44AAbBAEecAAbCAE5wAAbBQEiYAARUAFvQDDgAAAQABAAEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/7c6d85f865c7.js b/pr/299/docs/search.index/7c6d85f865c7.js new file mode 100644 index 000000000..b695cae28 --- /dev/null +++ b/pr/299/docs/search.index/7c6d85f865c7.js @@ -0,0 +1 @@ +rn_("AQQBoEAAABCqZQFuDwAAAQgA9OcQAAABAAEAAQABAwGgQAAAEKllAQAPAAABAgDz6QsAAAEAAQAjAAKwIAvZAAFrcvYgBwAAAQAJCeoAAQD8AfOAAnJ4AQMBsIATRwABdjswAAABAAALAAMAPRABAPIQBgC0EgIAI4ACsGAPtAC8YXIBAwGgAAAAD21z8v4OAAABACOAAqCwAAAS2WVoAQMBoEAAABA8ZfIlCQAAAQDXJQAAjLAgB48I3LBwELEAAbAgB3EAAbCwEjIAAaCAAAAQ66AAAAASP78MTDowAAABAAAAAAAJABAAAADPAQsEDAQNBMYLxwsjDCQMJQxAEg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/7c7b9eca657f.js b/pr/299/docs/search.index/7c7b9eca657f.js new file mode 100644 index 000000000..126a52665 --- /dev/null +++ b/pr/299/docs/search.index/7c7b9eca657f.js @@ -0,0 +1 @@ +rn_("AQQAOjAAAAEAAAAAAAcAEAAAABIPEw/RD9IPHhIfEqgSqRIBAAD0EAkAAAEAuwndABcAAACHoEAAABA8sGATUgABoBAAAAw4sFAS8gABsDAQngABsSATcQABOMECOjAAAAEAAAAAAA4AEAAAACUJJgk6CTsJyAvJC8oL0gvTC4IMqxF9En4SARMCEzOAA6CAAAAS+WVveQ==") \ No newline at end of file diff --git a/pr/299/docs/search.index/7ca0ef420c00.js b/pr/299/docs/search.index/7ca0ef420c00.js new file mode 100644 index 000000000..f4f66f1f6 --- /dev/null +++ b/pr/299/docs/search.index/7ca0ef420c00.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwDsEAIAcRIBAAMTAgABBADz7xAAAAEAAQAjAAKgYAAAEf9hcvMWEAAAAQCrAQGAAqEQAAAThbCAE30AAWl1NwQAgoexUBOgAAGACScAFs6wcBLNAAGhMAAAE5QKQBsTgAKwEBILAAFpcg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/7cada5462b17.js b/pr/299/docs/search.index/7cada5462b17.js new file mode 100644 index 000000000..b4cb60f11 --- /dev/null +++ b/pr/299/docs/search.index/7cada5462b17.js @@ -0,0 +1 @@ +rn_("AQIBsEATYwABdDswAAABAAANAAYAABIDAGMSAQDKEgEA4RIBAFkTAQB9EwEAE4ACsDASLQE1aXABBQDzohIAAAEAAQATgAKgMAAAEvhleVMAA7BwEwYAAWJlcwFzDwAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/7ce0db9b2df8.js b/pr/299/docs/search.index/7ce0db9b2df8.js new file mode 100644 index 000000000..793a0fe6c --- /dev/null +++ b/pr/299/docs/search.index/7ce0db9b2df8.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgCAEgAA5RIFACOIArBAE1QAAWVzAQQBsDASVgABY/OtEAAAAQABAAEFADswAAABAAAIAAMAHBECAAQSBAATEwAA8wADY2Vw83QMAAABAAEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/7da0e5fc56e2.js b/pr/299/docs/search.index/7da0e5fc56e2.js new file mode 100644 index 000000000..a85ebc5a3 --- /dev/null +++ b/pr/299/docs/search.index/7da0e5fc56e2.js @@ -0,0 +1 @@ +rn_("AYMCoFAAABA7oKAAABJJa3ABAAGwQBGiAAFs9HUJAAABAJQGAQABCAD0thEAAAEAAQABAAEEAbAAEBQAAXMBhw8AAAECAaBQAAASDWMBeAwAALcAAACFsEAMsAABoDAAAAvQlQABOzAAAAEAAAgABAAmBQAA4AUCAHsMAQD5DgIAJwEAgIWwMAk6AAGgEAAAEnmwIAefAAGRAEQBEAD0gRMAAAEAAQABAAEDAPR1CQAAAQCUBgEAY4ADoAAAAAvQbnJ1AQACsNASuwABoDAAAA78Ymr2xQUAAAEAAQBIAAEAVgEBAAGwgBLaAAF0OzAAAAEAADoABAANBgAAJAkAAC8JAAA5DDcAAQkA89MRAAABAAEA84ADY2d0AYMCoJAAABMgoJAAABMhMjMBgAKgUAAAE0qggAAAEnZjZfMAAnN08mcHAAABAAEAAaCwAAASvWL1URIAANEAAQBGAAEAI4ACoAAAABJQZHIjgAKgUAAAExJjZfOAAml5AQQA9WYQAAABAAEAAQABAPMBAW4BYQcAAAEBADswAAABAAAJAAIADAgHABQSAQABBgD0sBIAAAEAAQABAAEPAPSKEwAAAQABAAEA84ECaG0BAAGwQBNOAAFl9RQTAAABAAEAAQABAFOAA6AgAAARA2xudNdtAIDLoCAAABN8sGAP+QABsEAPMgABwBs4Cg==") \ No newline at end of file diff --git a/pr/299/docs/search.index/7de8caeba04a.js b/pr/299/docs/search.index/7de8caeba04a.js new file mode 100644 index 000000000..b7d8e6be9 --- /dev/null +++ b/pr/299/docs/search.index/7de8caeba04a.js @@ -0,0 +1 @@ +rn_("AYACsFATBgABoAAAAAkaY3QBAQA7MAAAAQAABQABAIYSBQABAwD0lAwAACkGlwABAAGAAqAQAAAMKqBQAAARrGNkAQIA8w8JAAD4AgEA8wABbfMHBQAArgABAAGBAqAQAAASQKBwAAASgWFj8wABcvODBwAATwkZAGcPAICIsDAPCgDEoDAAABN7KTAj") \ No newline at end of file diff --git a/pr/299/docs/search.index/7fbcc8b0cc40.js b/pr/299/docs/search.index/7fbcc8b0cc40.js new file mode 100644 index 000000000..cb59bc09e --- /dev/null +++ b/pr/299/docs/search.index/7fbcc8b0cc40.js @@ -0,0 +1 @@ +rn_("AYACoGAAABL5oKAAABLtYWkBCwD0jxIAAAEAAQABAAEEAPTUDwAAAQABAAEAAQQA9CsQAAABAAEAAQDzgAJscvMAA2NkcwEiEAAA") \ No newline at end of file diff --git a/pr/299/docs/search.index/7fc1150b9f0e.js b/pr/299/docs/search.index/7fc1150b9f0e.js new file mode 100644 index 000000000..a3f60e45c --- /dev/null +++ b/pr/299/docs/search.index/7fc1150b9f0e.js @@ -0,0 +1 @@ +rn_("AQcA9c8SAAABAAEAcQABAAEHAPPyDwAAAQABAAGAAqAAAAAHg6AQAAAJGmR48wEBZfJODwAAAQDnAAAAhaBgAAAMkbBADBQAATIIAfQnBQAAUgLbCAEA") \ No newline at end of file diff --git a/pr/299/docs/search.index/alias/a762150c532c.js b/pr/299/docs/search.index/alias/a762150c532c.js new file mode 100644 index 000000000..f829e4636 --- /dev/null +++ b/pr/299/docs/search.index/alias/a762150c532c.js @@ -0,0 +1 @@ +rd_("") \ No newline at end of file diff --git a/pr/299/docs/search.index/crateNames/8a381fcf2bd4.js b/pr/299/docs/search.index/crateNames/8a381fcf2bd4.js new file mode 100644 index 000000000..59242fbfb --- /dev/null +++ b/pr/299/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/299/docs/search.index/desc/ee2830ff879e.js b/pr/299/docs/search.index/desc/ee2830ff879e.js new file mode 100644 index 000000000..06a657920 --- /dev/null +++ b/pr/299/docs/search.index/desc/ee2830ff879e.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 handshakeAcAn 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.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000BaCalls U::from(self).000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dNextCeMethod 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\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 bufferCgStatistics about UDP datagrams received on a connection0CjStatistics about UDP datagrams transmitted on a connection0AnMethod 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 limitClIndicates 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 controller0BkThe 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\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.BgThe amount of packets sent on this path0CbSet 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 ConnectingCeThe 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\xa60CiReason 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 peerBnHeader 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\xa6CnCreate a client configuration that trusts the platform\xe2\x80\x99s \xe2\x80\xa60CmCreate 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/299/docs/search.index/entry/510fc07218fc.js b/pr/299/docs/search.index/entry/510fc07218fc.js new file mode 100644 index 000000000..d508de07d --- /dev/null +++ b/pr/299/docs/search.index/entry/510fc07218fc.js @@ -0,0 +1 @@ +rd_("Ah[3817,15,3818,0,113,0,0]Ak[4595,15,4596,4596,114,0,0]Ai[3817,15,3818,0,4001,0,0]Al[4595,15,4596,1873,4002,0,0]Al[4307,15,4308,4308,4003,0,0]Al[4595,15,1781,1781,1543,0,0]Ao[1320,13,1321,1321,3849,3043,0]Ao[3817,13,3818,3593,4637,3043,0]Ao[3817,13,3818,3952,4267,3043,0]Ao[3817,13,3818,3952,3051,3043,0]Ao[3817,13,3818,3952,3232,3043,0]Ao[3817,13,3818,3952,4266,3043,0]Ao[3817,13,3818,3957,3249,3043,0]Ao[3817,13,3818,3957,4035,3043,0]Al[3817,13,3818,0,1552,3043,0]Al[3817,13,3818,0,4796,3043,0]Al[3817,13,3818,0,1280,3043,0]Al[3817,13,3818,0,1536,3043,0]Al[3817,13,3818,0,3048,3043,0]Al[3817,13,3818,0,1952,3043,0]Al[3817,13,3818,0,3988,3043,0]Al[3817,13,3818,0,4361,3043,0]Al[3817,13,3818,0,3862,3043,0]Al[3817,13,3818,0,3852,3043,0]Al[3817,13,3818,0,3031,3043,0]Al[3817,13,3818,0,4358,3043,0]Al[3817,13,3818,0,4540,3043,0]Al[3817,13,3818,0,3994,3043,0]Al[3817,13,3818,0,3997,3043,0]Al[3817,13,3818,0,4001,3043,0]Al[3817,13,3818,0,4695,3043,0]Ak[3817,13,3818,0,466,3043,0]Ak[3817,13,3818,0,113,3043,0]Al[3817,13,3818,0,2348,3043,0]Al[3817,13,1785,0,4943,3043,0]Al[3817,13,1862,0,1297,3043,0]Ao[4595,13,4596,1895,1553,3043,0]Ao[4595,13,4596,1895,4797,3043,0]Ao[4595,13,4596,3067,1281,3043,0]Ao[4595,13,4596,3076,4638,3043,0]Ao[4595,13,4596,1476,1537,3043,0]Ao[4595,13,4596,1476,3226,3043,0]Ao[4595,13,4596,1476,3047,3043,0]Ao[4595,13,4596,1476,4692,3043,0]Ao[4595,13,4596,1476,3202,3043,0]Ao[4595,13,4596,1491,3049,3043,0]Ao[4595,13,4596,1039,3052,3043,0]Ao[4595,13,4596,1039,4157,3043,0]Ao[4595,13,4596,1044,1953,3043,0]Ao[4595,13,4596,1044,3250,3043,0]Ao[4595,13,4596,1044,3861,3043,0]Ao[4595,13,4596,2086,3879,3043,0]Ao[4595,13,4596,2086,4268,3043,0]Ao[4595,13,4596,2086,3992,3043,0]Ao[4595,13,4596,3594,4362,3043,0]Ao[4595,13,4596,3594,3044,3043,0]Ao[4595,13,4596,3594,4235,3043,0]Ao[4595,13,4596,3189,3863,3043,0]Ao[4595,13,4596,1783,3853,3043,0]Ao[4595,13,4596,1466,3032,3043,0]Ao[4595,13,4596,1466,4360,3043,0]Ao[4595,13,4596,1466,4542,3043,0]Ao[4595,13,4596,2371,4546,3043,0]Ao[4595,13,4596,2371,3995,3043,0]Ao[4595,13,4596,1846,2332,3043,0]Ao[4595,13,4596,1846,4636,3043,0]Ao[4595,13,4596,1873,3998,3043,0]Ao[4595,13,4596,1873,4002,3043,0]Al[4595,13,4596,0,4272,3043,0]Al[4595,13,4596,0,4696,3043,0]An[4595,13,4596,4596,467,3043,0]An[4595,13,4596,4596,114,3043,0]Ao[4595,13,4596,4596,2349,3043,0]Ao[4595,13,4596,4596,3030,3043,0]Ao[4595,13,1781,1781,4163,3043,0]Ao[4595,13,4838,4838,4737,3043,0]Ao[4595,13,4838,4838,1299,3043,0]Ao[4595,13,1786,1786,4944,3043,0]Al[4595,13,1863,0,1298,3043,0]Ao[4307,13,4308,4308,4003,3043,0]Ak[3817,13,3818,1014,459,0,0]Al[3817,13,3818,3952,3230,0,0]Al[3817,13,3818,3952,4738,0,0]Al[3817,13,3818,3957,3236,0,0]Ai[4595,14,3045,0,1530,0,0]Ai[4595,14,3045,0,1520,0,0]Ai[4595,14,3045,0,3011,0,0]Ai[4595,14,3045,0,4140,0,0]Ai[4595,14,3045,0,4022,0,0]Ai[4595,14,3045,0,4015,0,0]Ai[4595,14,3878,0,2342,0,0]Ai[4595,14,3878,0,2363,0,0]Ai[4595,14,3878,0,2327,0,0]Ai[4595,14,3878,0,1942,0,0]Ae[1320,7,1321,0,0,0,0]Ah[4595,14,4373,0,456,0,0]Ai[3817,15,3818,0,3031,0,0]Al[4595,15,4596,1466,3032,0,0]Ag[3817,5,3588,129,0,0,0]Ag[4595,5,3589,129,0,0,0]Al[1015,15,1016,1016,4730,0,0]Ae[3837,5,3838,0,0,0,0]Ah[3817,6,3818,4596,0,0,0]Ae[4595,6,4596,0,0,0,0]Al[4595,15,1781,1781,1543,0,0]Ai[3817,19,3818,0,1552,0,0]Ai[3817,19,3818,0,1536,0,0]Al[4595,19,4596,1895,1553,0,0]Al[4595,19,4596,1476,1537,0,0]Ae[1320,5,1321,0,0,0,0]Ae[1015,5,1775,0,0,0,0]Ae[1015,5,1868,0,0,0,0]Ah[3817,15,3818,0,113,0,0]Ak[4595,15,4596,4596,114,0,0]Ak[3817,13,3818,0,1552,124,0]Ak[3817,13,3818,0,1536,124,0]Ak[3817,13,3818,0,3862,124,0]Ak[3817,13,3818,0,3997,124,0]Aj[3817,13,3818,0,466,124,0]Aj[3817,13,3818,0,113,124,0]Ak[3817,13,3818,0,2348,124,0]An[4595,13,4596,1895,1553,124,0]An[4595,13,4596,3076,4638,124,0]An[4595,13,4596,1476,1537,124,0]An[4595,13,4596,1039,3052,124,0]An[4595,13,4596,1039,4157,124,0]An[4595,13,4596,1044,3250,124,0]An[4595,13,4596,3189,3863,124,0]An[4595,13,4596,2371,4546,124,0]An[4595,13,4596,1846,4636,124,0]An[4595,13,4596,1873,3998,124,0]Am[4595,13,4596,4596,467,124,0]Am[4595,13,4596,4596,114,124,0]An[4595,13,4596,4596,2349,124,0]Ai[3817,13,3818,0,2348,0,0]Al[4595,13,4596,4596,2349,0,0]Ai[4595,14,3878,0,1531,0,0]Ai[4595,14,3878,0,3017,0,0]Ai[3817,14,3818,0,2357,0,0]Al[4595,14,4596,4596,2358,0,0]Al[4307,14,4308,4308,2346,0,0]Al[4307,14,4308,4308,2359,0,0]An[1320,13,1321,1321,121,1287,0]Ao[1320,13,1321,1321,3849,1287,0]Ao[1320,13,1490,1490,4269,1287,0]Ao[3886,13,3887,3887,4883,1287,0]Ao[3817,13,3818,3593,3204,1287,0]Ao[3817,13,3818,3593,3206,1287,0]Ao[3817,13,3818,3593,4799,1287,0]Db[3817,13,3818,3593,4637,1287,0,\"impl-Debug-for-SendDatagramError\"]Dd[3817,13,3818,3593,4637,1914,0,\"impl-Display-for-SendDatagramError\"]Ao[3817,13,3818,2370,2325,1287,0]Ao[3817,13,3818,2370,4130,1287,0]Ao[3817,13,3818,2397,2329,1287,0]Ck[3817,13,3818,2397,3233,1287,0,\"impl-Debug-for-RetryError\"]Cm[3817,13,3818,2397,3233,1914,0,\"impl-Display-for-RetryError\"]Ao[3817,13,3818,2397,4249,1287,0]An[3817,13,3818,1014,459,1287,0]Ao[3817,13,3818,3952,3230,1287,0]Ao[3817,13,3818,3952,4738,1287,0]Da[3817,13,3818,3952,4267,1914,0,\"impl-Display-for-ReadToEndError\"]Co[3817,13,3818,3952,4267,1287,0,\"impl-Debug-for-ReadToEndError\"]Cl[3817,13,3818,3952,3051,1914,0,\"impl-Display-for-ReadError\"]Cj[3817,13,3818,3952,3051,1287,0,\"impl-Debug-for-ReadError\"]Ck[3817,13,3818,3952,3232,1287,0,\"impl-Debug-for-ResetError\"]Cm[3817,13,3818,3952,3232,1914,0,\"impl-Display-for-ResetError\"]Co[3817,13,3818,3952,4266,1287,0,\"impl-Debug-for-ReadExactError\"]Da[3817,13,3818,3952,4266,1914,0,\"impl-Display-for-ReadExactError\"]Ao[3817,13,3818,1502,4036,1287,0]Ao[3817,13,3818,1056,3875,1287,0]Ao[3817,13,3818,3957,3236,1287,0]Cm[3817,13,3818,3957,3249,1914,0,\"impl-Display-for-WriteError\"]Ck[3817,13,3818,3957,3249,1287,0,\"impl-Debug-for-WriteError\"]Cm[3817,13,3818,3957,4035,1287,0,\"impl-Debug-for-StoppedError\"]Co[3817,13,3818,3957,4035,1914,0,\"impl-Display-for-StoppedError\"]Cf[3817,13,3818,0,1552,1914,0,\"impl-Display-for-VarInt\"]Cd[3817,13,3818,0,1552,1287,0,\"impl-Debug-for-VarInt\"]Db[3817,13,3818,0,4796,1287,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Dd[3817,13,3818,0,4796,1914,0,\"impl-Display-for-VarIntBoundsExceeded\"]Al[3817,13,3818,0,1280,1287,0]Cd[3817,13,3818,0,1536,1287,0,\"impl-Debug-for-PathId\"]Cf[3817,13,3818,0,1536,1914,0,\"impl-Display-for-PathId\"]Al[3817,13,3818,0,2360,1287,0]Al[3817,13,3818,0,3212,1287,0]Al[3817,13,3818,0,3048,1287,0]Al[3817,13,3818,0,4365,1287,0]Al[3817,13,3818,0,1952,1287,0]Cl[3817,13,3818,0,3988,1914,0,\"impl-Display-for-ClosedStream\"]Cj[3817,13,3818,0,3988,1287,0,\"impl-Debug-for-ClosedStream\"]Cm[3817,13,3818,0,4361,1287,0,\"impl-Debug-for-ConnectionError\"]Co[3817,13,3818,0,4361,1914,0,\"impl-Display-for-ConnectionError\"]Al[3817,13,3818,0,4375,1287,0]Al[3817,13,3818,0,4382,1287,0]Al[3817,13,3818,0,4661,1287,0]Al[3817,13,3818,0,4676,1287,0]Al[3817,13,3818,0,3862,1287,0]Al[3817,13,3818,0,4244,1287,0]Al[3817,13,3818,0,4029,1287,0]Al[3817,13,3818,0,4853,1287,0]Al[3817,13,3818,0,3982,1287,0]Ck[3817,13,3818,0,3852,1914,0,\"impl-Display-for-ConfigError\"]Ci[3817,13,3818,0,3852,1287,0,\"impl-Debug-for-ConfigError\"]Ci[3817,13,3818,0,3031,1914,0,\"impl-Display-for-FrameType\"]Cg[3817,13,3818,0,3031,1287,0,\"impl-Debug-for-FrameType\"]Cm[3817,13,3818,0,4358,1287,0,\"impl-Debug-for-ConnectionClose\"]Co[3817,13,3818,0,4358,1914,0,\"impl-Display-for-ConnectionClose\"]D`[3817,13,3818,0,4540,1914,0,\"impl-Display-for-ApplicationClose\"]Cn[3817,13,3818,0,4540,1287,0,\"impl-Debug-for-ApplicationClose\"]Cj[3817,13,3818,0,3994,1287,0,\"impl-Debug-for-ConnectError\"]Cl[3817,13,3818,0,3994,1914,0,\"impl-Display-for-ConnectError\"]Cl[3817,13,3818,0,3997,1914,0,\"impl-Display-for-ConnectionId\"]Cj[3817,13,3818,0,3997,1287,0,\"impl-Debug-for-ConnectionId\"]Al[3817,13,3818,0,4001,1287,0]Db[3817,13,3818,0,4695,1914,0,\"impl-Display-for-TransportErrorCode\"]D`[3817,13,3818,0,4695,1287,0,\"impl-Debug-for-TransportErrorCode\"]Al[3817,13,3818,0,4571,1287,0]Ak[3817,13,3818,0,466,1287,0]C`[3817,13,3818,0,113,1287,0,\"impl-Debug-for-Dir\"]Cb[3817,13,3818,0,113,1914,0,\"impl-Display-for-Dir\"]Ch[3817,13,3818,0,2348,1914,0,\"impl-Display-for-StreamId\"]Cf[3817,13,3818,0,2348,1287,0,\"impl-Debug-for-StreamId\"]Al[3817,13,3818,0,2357,1287,0]Ak[3817,13,3588,0,107,1287,0]Al[3817,13,3588,0,3020,1287,0]Al[3817,13,3588,0,1284,1287,0]Al[3817,13,3588,0,3857,1287,0]Al[3817,13,3588,0,1927,1287,0]Al[3817,13,3588,0,4149,1287,0]Al[3817,13,3588,0,4629,1287,0]Al[3817,13,1785,0,4943,1287,0]Al[3817,13,1785,0,3855,1287,0]Al[3817,13,1785,0,4699,1287,0]Db[3817,13,1862,0,4792,1287,0,\"impl-Debug-for-NoInitialCipherSuite\"]Dd[3817,13,1862,0,4792,1914,0,\"impl-Display-for-NoInitialCipherSuite\"]Ce[3817,13,1862,0,1297,1914,0,\"impl-Display-for-Error\"]Cc[3817,13,1862,0,1297,1287,0,\"impl-Debug-for-Error\"]Ao[4595,13,4596,3594,3207,1287,0]Ao[4595,13,4596,2371,2326,1287,0]Cg[4595,13,4596,1895,1553,1287,0,\"impl-Debug-for-VarInt\"]Ci[4595,13,4596,1895,1553,1914,0,\"impl-Display-for-VarInt\"]De[4595,13,4596,1895,4797,1287,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Dg[4595,13,4596,1895,4797,1914,0,\"impl-Display-for-VarIntBoundsExceeded\"]Ao[4595,13,4596,3067,1281,1287,0]Db[4595,13,4596,3076,4638,1287,0,\"impl-Debug-for-SendDatagramError\"]Dd[4595,13,4596,3076,4638,1914,0,\"impl-Display-for-SendDatagramError\"]Ci[4595,13,4596,1476,1537,1914,0,\"impl-Display-for-PathId\"]Cg[4595,13,4596,1476,1537,1287,0,\"impl-Debug-for-PathId\"]Ao[4595,13,4596,1476,4025,1287,0]Ao[4595,13,4596,1476,3226,1287,0]Ao[4595,13,4596,1476,3047,1287,0]De[4595,13,4596,1476,4692,1914,0,\"impl-Display-for-SetPathStatusError\"]Dc[4595,13,4596,1476,4692,1287,0,\"impl-Debug-for-SetPathStatusError\"]Cm[4595,13,4596,1476,3202,1914,0,\"impl-Display-for-ClosedPath\"]Ck[4595,13,4596,1476,3202,1287,0,\"impl-Debug-for-ClosedPath\"]Ao[4595,13,4596,1491,2361,1287,0]Ao[4595,13,4596,1491,3213,1287,0]Ao[4595,13,4596,1491,3049,1287,0]Ao[4595,13,4596,1491,4366,1287,0]Cl[4595,13,4596,1039,3052,1914,0,\"impl-Display-for-ReadError\"]Cj[4595,13,4596,1039,3052,1287,0,\"impl-Debug-for-ReadError\"]D`[4595,13,4596,1039,4157,1914,0,\"impl-Display-for-ReadableError\"]Cn[4595,13,4596,1039,4157,1287,0,\"impl-Debug-for-ReadableError\"]Ao[4595,13,4596,1044,1953,1287,0]Ck[4595,13,4596,1044,3250,1287,0,\"impl-Debug-for-WriteError\"]Cm[4595,13,4596,1044,3250,1914,0,\"impl-Display-for-WriteError\"]Cn[4595,13,4596,1044,3861,1914,0,\"impl-Display-for-FinishError\"]Cl[4595,13,4596,1044,3861,1287,0,\"impl-Debug-for-FinishError\"]Ao[4595,13,4596,2086,3879,1287,0]Ao[4595,13,4596,2086,4268,1287,0]Cm[4595,13,4596,2086,3992,1287,0,\"impl-Debug-for-ClosedStream\"]Co[4595,13,4596,2086,3992,1914,0,\"impl-Display-for-ClosedStream\"]Db[4595,13,4596,3594,4362,1914,0,\"impl-Display-for-ConnectionError\"]D`[4595,13,4596,3594,4362,1287,0,\"impl-Debug-for-ConnectionError\"]Cl[4595,13,4596,3594,3044,1914,0,\"impl-Display-for-PathError\"]Cj[4595,13,4596,3594,3044,1287,0,\"impl-Debug-for-PathError\"]Da[4595,13,4596,3594,4235,1914,0,\"impl-Display-for-ClosePathError\"]Co[4595,13,4596,3594,4235,1287,0,\"impl-Debug-for-ClosePathError\"]Ao[4595,13,4596,3594,1302,1287,0]Ao[4595,13,4596,1035,4376,1287,0]Ao[4595,13,4596,3189,4383,1287,0]Ao[4595,13,4596,3189,4662,1287,0]Ao[4595,13,4596,3189,4677,1287,0]Ao[4595,13,4596,3189,3863,1287,0]Ao[4595,13,4596,1783,4245,1287,0]Ao[4595,13,4596,1783,4031,1287,0]Ao[4595,13,4596,1783,4854,1287,0]Ao[4595,13,4596,1783,3984,1287,0]Cl[4595,13,4596,1783,3853,1287,0,\"impl-Debug-for-ConfigError\"]Cn[4595,13,4596,1783,3853,1914,0,\"impl-Display-for-ConfigError\"]Cj[4595,13,4596,1466,3032,1287,0,\"impl-Debug-for-FrameType\"]Cl[4595,13,4596,1466,3032,1914,0,\"impl-Display-for-FrameType\"]D`[4595,13,4596,1466,4360,1287,0,\"impl-Debug-for-ConnectionClose\"]Db[4595,13,4596,1466,4360,1914,0,\"impl-Display-for-ConnectionClose\"]Da[4595,13,4596,1466,4542,1287,0,\"impl-Debug-for-ApplicationClose\"]Dc[4595,13,4596,1466,4542,1914,0,\"impl-Display-for-ApplicationClose\"]Ao[4595,13,4596,1466,2319,1287,0]Ao[4595,13,4596,2371,4546,1287,0]Ao[4595,13,4596,2371,2330,1287,0]Co[4595,13,4596,2371,3995,1914,0,\"impl-Display-for-ConnectError\"]Cm[4595,13,4596,2371,3995,1287,0,\"impl-Debug-for-ConnectError\"]Ao[4595,13,4596,2371,3847,1287,0]Cm[4595,13,4596,2371,3234,1914,0,\"impl-Display-for-RetryError\"]Ck[4595,13,4596,2371,3234,1287,0,\"impl-Debug-for-RetryError\"]Ao[4595,13,4596,1846,4152,1287,0]Ao[4595,13,4596,1846,4374,1287,0]Ao[4595,13,4596,1846,4882,1287,0]Ao[4595,13,4596,1846,2332,1287,0]Dd[4595,13,4596,1846,4636,1914,0,\"impl-Display-for-PacketDecodeError\"]Db[4595,13,4596,1846,4636,1287,0,\"impl-Debug-for-PacketDecodeError\"]Ao[4595,13,4596,1873,4364,1287,0]Ao[4595,13,4596,1873,4129,1287,0]Cm[4595,13,4596,1873,3998,1287,0,\"impl-Debug-for-ConnectionId\"]Co[4595,13,4596,1873,3998,1914,0,\"impl-Display-for-ConnectionId\"]Ao[4595,13,4596,1873,4002,1287,0]Cc[4595,13,4596,0,4272,1287,0,\"impl-Debug-for-Error\"]Ce[4595,13,4596,0,4272,1914,0,\"impl-Display-for-Error\"]Cd[4595,13,4596,0,4696,1914,0,\"impl-Display-for-Code\"]Cb[4595,13,4596,0,4696,1287,0,\"impl-Debug-for-Code\"]Ao[4595,13,4596,4170,3216,1287,0]Ao[4595,13,4596,4170,4977,1287,0]Ao[4595,13,4596,4726,4572,1287,0]An[4595,13,4596,4596,467,1287,0]Cc[4595,13,4596,4596,114,1287,0,\"impl-Debug-for-Dir\"]Ce[4595,13,4596,4596,114,1914,0,\"impl-Display-for-Dir\"]Ci[4595,13,4596,4596,2349,1287,0,\"impl-Debug-for-StreamId\"]Ck[4595,13,4596,4596,2349,1914,0,\"impl-Display-for-StreamId\"]Ao[4595,13,4596,4596,2358,1287,0]Cj[4595,13,4596,4596,3030,1287,0,\"impl-Debug-for-FourTuple\"]Cl[4595,13,4596,4596,3030,1914,0,\"impl-Display-for-FourTuple\"]Cn[4595,13,1781,1781,4163,1287,0,\"impl-Debug-for-UnexpectedEnd\"]D`[4595,13,1781,1781,4163,1914,0,\"impl-Display-for-UnexpectedEnd\"]Ao[4595,13,4838,4838,4737,1287,0]Ch[4595,13,4838,4838,1299,1914,0,\"impl-Display-for-Error\"]Cf[4595,13,4838,4838,1299,1287,0,\"impl-Debug-for-Error\"]Ao[4595,13,1786,1786,4944,1287,0]Ao[4595,13,1786,1786,3856,1287,0]Ao[4595,13,1786,1786,4702,1287,0]De[4595,13,1863,1863,4793,1287,0,\"impl-Debug-for-NoInitialCipherSuite\"]Dg[4595,13,1863,1863,4793,1914,0,\"impl-Display-for-NoInitialCipherSuite\"]Cc[4595,13,1863,0,1298,1287,0,\"impl-Debug-for-Error\"]Ce[4595,13,1863,0,1298,1914,0,\"impl-Display-for-Error\"]Am[4595,13,3589,129,108,1287,0]An[4595,13,3589,129,3021,1287,0]Ao[4595,13,3589,1461,1285,1287,0]Ao[4595,13,3589,1461,3858,1287,0]Ao[4595,13,3589,2419,1928,1287,0]Ao[4595,13,3589,2419,4150,1287,0]Ao[4595,13,3589,3589,4630,1287,0]Cf[4595,13,2057,2057,1300,1287,0,\"impl-Debug-for-Error\"]Ch[4595,13,2057,2057,1300,1914,0,\"impl-Display-for-Error\"]Ao[4595,13,2057,2057,1303,1287,0]An[4307,13,4308,375,4273,1287,0]Ao[4307,13,4308,4308,2346,1287,0]Ao[4307,13,4308,4308,2359,1287,0]Ao[4307,13,4308,4308,4003,1287,0]Al[4595,13,4596,1476,4025,0,0]Ai[3817,14,3818,0,2360,0,0]Al[4595,14,4596,1491,2361,0,0]Al[4595,13,4596,1846,4152,0,0]Al[4595,14,4596,1846,4882,0,0]Ah[4595,14,4373,0,456,0,0]Al[4307,14,4308,4308,2346,0,0]Ai[3817,13,3818,0,4853,0,0]Al[4595,13,4596,1783,4854,0,0]8Al[1320,13,1490,1490,4269,0,0]Al[3886,13,3887,3887,4883,0,0]Ai[3817,13,3818,0,4121,0,0]Al[3817,13,3818,2370,2325,0,0]Ai[3817,13,3818,0,3227,0,0]Ai[3817,13,3818,0,4375,0,0]Ai[3817,13,3818,0,4244,0,0]Ai[3817,13,3818,0,4029,0,0]Ai[3817,13,3818,0,3982,0,0]Ai[3817,13,3818,0,3997,0,0]Ai[3817,13,3818,0,4571,0,0]Ai[3817,13,3818,0,2348,0,0]Ah[3817,13,3588,0,107,0,0]Ai[3817,13,3588,0,1284,0,0]Ai[3817,13,3588,0,1927,0,0]Al[4595,13,4596,4392,4122,0,0]Al[4595,13,4596,1035,3228,0,0]Al[4595,13,4596,2371,2326,0,0]Al[4595,13,4596,1846,5002,0,0]Al[4595,13,4596,4170,4972,0,0]Al[4595,13,4596,1035,4376,0,0]Al[4595,13,4596,1783,4245,0,0]Al[4595,13,4596,1783,4031,0,0]Al[4595,13,4596,1783,3984,0,0]Al[4595,13,4596,1846,4152,0,0]Al[4595,13,4596,1873,3998,0,0]Ai[4595,13,4596,0,4272,0,0]Al[4595,13,4596,4170,4977,0,0]Al[4595,13,4596,4726,4572,0,0]Al[4595,13,4596,4596,2349,0,0]Aj[4595,13,3589,129,108,0,0]Al[4595,13,3589,1461,1285,0,0]Al[4595,13,3589,2419,1928,0,0]Ak[4307,13,4308,375,4273,0,0]Al[1015,13,4088,4088,4929,0,0]Al[1015,13,4088,4088,4930,0,0]Aj[3817,13,3818,0,466,120,0]Am[4595,13,4596,4596,467,120,0]Ai[3817,12,3818,0,3242,0,0]Al[3817,13,3818,2078,1939,0,0]0Ao[3817,13,3818,1502,4036,1939,0]Al[3817,13,3818,0,4161,3242,0]Al[4595,12,4596,1783,3243,0,0]Ao[4595,13,4596,1783,4162,3243,0]Al[3817,13,3818,3593,3206,0,0]Ai[3817,14,3818,0,3048,0,0]Al[4595,13,4596,3594,3207,0,0]Al[4595,14,4596,1491,3049,0,0]Ae[1015,7,1775,0,0,0,0]Ae[1015,7,1868,0,0,0,0]Ae[3817,4,3818,0,0,0,0]Ai[3817,15,3818,0,4001,0,0]Al[4595,15,4596,1873,4002,0,0]Al[4307,15,4308,4308,4003,0,0]210Ah[3817,5,1785,1786,0,0,0]Ae[4595,5,1786,0,0,0,0]Al[4595,15,4596,1846,4374,0,0]Ah[3817,5,3818,1014,0,0,0]Al[4595,15,4596,3594,1302,0,0]Ai[3817,15,3818,0,3031,0,0]Al[4595,15,4596,1466,3032,0,0]Al[3817,15,3818,3952,4267,0,0]Ah[3817,6,3818,4596,0,0,0]Ae[4595,6,4596,0,0,0,0]Ai[3817,19,3818,0,1536,0,0]Al[4595,19,4596,1476,1537,0,0]Ai[3817,14,3818,0,3212,0,0]Al[4595,14,4596,1491,3213,0,0]Ai[4595,14,3045,0,4015,0,0]Al[4307,14,4308,4308,2346,0,0]Aa[476,3,0,0,0,0,0]Ai[4595,14,4596,0,4272,0,0]Ai[3817,14,3818,0,3048,0,0]Al[4595,14,4596,1491,3049,0,0]Al[4595,14,4596,1466,2319,0,0]An[3817,13,3818,3593,2339,444,0]An[3817,13,3818,2397,2329,444,0]An[3817,13,3818,3952,3230,444,0]An[3817,13,3818,3957,3236,444,0]An[4595,13,4596,1039,1516,444,0]Am[1320,13,1321,1321,121,451,0]An[1320,13,1321,1321,3849,451,0]An[1320,13,1490,1490,3880,451,0]An[1320,13,1490,1490,1316,451,0]An[1320,13,1490,1490,4269,451,0]Ak[476,13,477,477,3850,451,0]An[3886,13,3887,3887,4883,451,0]An[3817,13,3818,3593,4387,451,0]An[3817,13,3818,3593,2339,451,0]Ak[3817,13,3818,0,4121,451,0]An[3817,13,3818,1014,2340,451,0]An[3817,13,3818,3593,3204,451,0]An[3817,13,3818,3593,3206,451,0]An[3817,13,3818,3593,1930,451,0]An[3817,13,3818,3593,1529,451,0]An[3817,13,3818,3593,3013,451,0]An[3817,13,3818,3593,2313,451,0]An[3817,13,3818,3593,4021,451,0]An[3817,13,3818,3593,4026,451,0]An[3817,13,3818,3593,4799,451,0]An[3817,13,3818,3593,4637,451,0]0An[3817,13,3818,2370,2325,451,0]An[3817,13,3818,2370,4130,451,0]An[3817,13,3818,2370,1508,451,0]An[3817,13,3818,2397,2329,451,0]An[3817,13,3818,2397,3233,451,0]An[3817,13,3818,2397,4249,451,0]Am[3817,13,3818,1014,459,451,0]An[3817,13,3818,3952,3230,451,0]An[3817,13,3818,3952,4738,451,0]An[3817,13,3818,3952,4267,451,0]0An[3817,13,3818,3952,3051,451,0]Dh[3817,13,3818,3952,3051,451,0,\"impl-From%3CResetError%3E-for-ReadError\"]Dm[3817,13,3818,3952,3051,451,0,\"impl-From%3CConnectionError%3E-for-ReadError\"]An[3817,13,3818,3952,3232,451,0]0An[3817,13,3818,3952,4266,451,0]0An[3817,13,3818,1502,4036,451,0]An[3817,13,3818,1056,3875,451,0]An[3817,13,3818,3957,3236,451,0]Dn[3817,13,3818,3957,3249,451,0,\"impl-From%3CConnectionError%3E-for-WriteError\"]Dk[3817,13,3818,3957,3249,451,0,\"impl-From%3CStoppedError%3E-for-WriteError\"]Dk[3817,13,3818,3957,3249,451,0,\"impl-From%3CClosedStream%3E-for-WriteError\"]An[3817,13,3818,3957,3249,451,0]An[3817,13,3818,3957,4035,451,0]0Ak[3817,13,3818,0,3227,451,0]Ak[3817,13,3818,0,4161,451,0]Ak[3817,13,3818,0,4379,451,0]Ak[3817,13,3818,0,4013,451,0]Ak[3817,13,3818,0,4257,451,0]Ak[3817,13,3818,0,1552,451,0]Ck[3817,13,3818,0,1552,451,0,\"impl-From%3Cu32%3E-for-VarInt\"]Cj[3817,13,3818,0,1552,451,0,\"impl-From%3Cu8%3E-for-VarInt\"]Ck[3817,13,3818,0,1552,451,0,\"impl-From%3Cu16%3E-for-VarInt\"]D`[3817,13,3818,0,1552,451,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Dj[3817,13,3818,0,1552,451,0,\"impl-From%3CTransportErrorCode%3E-for-VarInt\"]Ak[3817,13,3818,0,4796,451,0]Ak[3817,13,3818,0,1280,451,0]Ak[3817,13,3818,0,1536,451,0]0Ak[3817,13,3818,0,2360,451,0]Ak[3817,13,3818,0,3212,451,0]Ak[3817,13,3818,0,3048,451,0]Ak[3817,13,3818,0,4365,451,0]Ak[3817,13,3818,0,1952,451,0]Ak[3817,13,3818,0,3988,451,0]Dl[3817,13,3818,0,4361,451,0,\"impl-From%3CCloseReason%3E-for-ConnectionError\"]Ak[3817,13,3818,0,4361,451,0]Df[3817,13,3818,0,4361,451,0,\"impl-From%3CClose%3E-for-ConnectionError\"]Do[3817,13,3818,0,4361,451,0,\"impl-From%3CTransportError%3E-for-ConnectionError\"]Ak[3817,13,3818,0,4375,451,0]Ak[3817,13,3818,0,4382,451,0]Ak[3817,13,3818,0,4661,451,0]Ak[3817,13,3818,0,4676,451,0]Ak[3817,13,3818,0,3862,451,0]0Ak[3817,13,3818,0,4244,451,0]Ak[3817,13,3818,0,4029,451,0]Ak[3817,13,3818,0,4853,451,0]Ak[3817,13,3818,0,3982,451,0]Dl[3817,13,3818,0,3852,451,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]Ak[3817,13,3818,0,3852,451,0]Ea[3817,13,3818,0,3852,451,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]Ak[3817,13,3818,0,3031,451,0]Ak[3817,13,3818,0,4358,451,0]0Ak[3817,13,3818,0,4540,451,0]Ak[3817,13,3818,0,3994,451,0]0Ak[3817,13,3818,0,3997,451,0]Ak[3817,13,3818,0,4001,451,0]Ak[3817,13,3818,0,4695,451,0]0Ak[3817,13,3818,0,4571,451,0]Aj[3817,13,3818,0,466,451,0]Aj[3817,13,3818,0,113,451,0]Ak[3817,13,3818,0,2348,451,0]0Ak[3817,13,3818,0,2357,451,0]Aj[3817,13,3588,0,107,451,0]Ak[3817,13,3588,0,3020,451,0]Ak[3817,13,3588,0,1284,451,0]Ak[3817,13,3588,0,3857,451,0]Ak[3817,13,3588,0,1927,451,0]Ak[3817,13,3588,0,4149,451,0]Ak[3817,13,3588,0,4629,451,0]Ak[3817,13,1785,0,1922,451,0]Aj[3817,13,1785,0,454,451,0]Ak[3817,13,1785,0,4943,451,0]Ak[3817,13,1785,0,3855,451,0]0Ak[3817,13,1785,0,4699,451,0]Ak[3817,13,1862,0,3244,451,0]Ak[3817,13,1862,0,4132,451,0]Ak[3817,13,1862,0,4567,451,0]Ak[3817,13,1862,0,4569,451,0]Ak[3817,13,1862,0,4792,451,0]Dg[3817,13,1862,0,1297,451,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]E`[3817,13,1862,0,1297,451,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Df[3817,13,1862,0,1297,451,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Df[3817,13,1862,0,1297,451,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]E`[3817,13,1862,0,1297,451,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Dn[3817,13,1862,0,1297,451,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]Dg[3817,13,1862,0,1297,451,0,\"impl-From%3CCertificateError%3E-for-Error\"]Dg[3817,13,1862,0,1297,451,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]De[3817,13,1862,0,1297,451,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]De[3817,13,1862,0,1297,451,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]Ak[3817,13,1862,0,1297,451,0]Da[3817,13,1862,0,1297,451,0,\"impl-From%3COtherError%3E-for-Error\"]An[4595,13,4596,4392,4122,451,0]An[4595,13,4596,3076,3026,451,0]An[4595,13,4596,1039,1516,451,0]An[4595,13,4596,2086,1947,451,0]An[4595,13,4596,2086,3231,451,0]An[4595,13,4596,2086,3237,451,0]An[4595,13,4596,3594,3207,451,0]An[4595,13,4596,1035,3228,451,0]An[4595,13,4596,1783,4162,451,0]An[4595,13,4596,2371,2326,451,0]An[4595,13,4596,2371,4128,451,0]An[4595,13,4596,1846,5002,451,0]An[4595,13,4596,4170,4972,451,0]An[4595,13,4596,1501,4380,451,0]An[4595,13,4596,1501,4014,451,0]An[4595,13,4596,1501,4258,451,0]Dc[4595,13,4596,1895,1553,451,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Co[4595,13,4596,1895,1553,451,0,\"impl-From%3CCode%3E-for-VarInt\"]Cn[4595,13,4596,1895,1553,451,0,\"impl-From%3Cu16%3E-for-VarInt\"]An[4595,13,4596,1895,1553,451,0]Cn[4595,13,4596,1895,1553,451,0,\"impl-From%3Cu32%3E-for-VarInt\"]Cm[4595,13,4596,1895,1553,451,0,\"impl-From%3Cu8%3E-for-VarInt\"]An[4595,13,4596,1895,4797,451,0]An[4595,13,4596,3067,1281,451,0]An[4595,13,4596,3076,4638,451,0]An[4595,13,4596,1476,1537,451,0]0An[4595,13,4596,1476,4025,451,0]An[4595,13,4596,1476,3226,451,0]An[4595,13,4596,1476,3047,451,0]An[4595,13,4596,1476,4692,451,0]An[4595,13,4596,1476,3202,451,0]An[4595,13,4596,1491,2361,451,0]An[4595,13,4596,1491,3213,451,0]An[4595,13,4596,1491,3049,451,0]An[4595,13,4596,1491,4366,451,0]An[4595,13,4596,1039,3052,451,0]An[4595,13,4596,1039,4157,451,0]An[4595,13,4596,1044,1953,451,0]An[4595,13,4596,1044,3250,451,0]An[4595,13,4596,1044,3861,451,0]An[4595,13,4596,2086,3879,451,0]An[4595,13,4596,2086,4268,451,0]An[4595,13,4596,2086,3992,451,0]An[4595,13,4596,3594,4362,451,0]0An[4595,13,4596,3594,3044,451,0]An[4595,13,4596,3594,4235,451,0]An[4595,13,4596,3594,1302,451,0]0An[4595,13,4596,1035,4376,451,0]An[4595,13,4596,3189,4383,451,0]An[4595,13,4596,3189,4662,451,0]An[4595,13,4596,3189,4677,451,0]An[4595,13,4596,3189,3863,451,0]0An[4595,13,4596,1783,4245,451,0]An[4595,13,4596,1783,4031,451,0]An[4595,13,4596,1783,4854,451,0]An[4595,13,4596,1783,3984,451,0]Do[4595,13,4596,1783,3853,451,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]An[4595,13,4596,1783,3853,451,0]Ed[4595,13,4596,1783,3853,451,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]An[4595,13,4596,1466,3032,451,0]An[4595,13,4596,1466,4360,451,0]0An[4595,13,4596,1466,4542,451,0]An[4595,13,4596,1466,2319,451,0]An[4595,13,4596,2371,4546,451,0]An[4595,13,4596,2371,2330,451,0]An[4595,13,4596,2371,3995,451,0]0An[4595,13,4596,2371,3847,451,0]An[4595,13,4596,2371,3234,451,0]An[4595,13,4596,1846,4152,451,0]An[4595,13,4596,1846,4374,451,0]An[4595,13,4596,1846,4882,451,0]An[4595,13,4596,1846,2332,451,0]An[4595,13,4596,1846,4636,451,0]0An[4595,13,4596,1873,4364,451,0]An[4595,13,4596,1873,4129,451,0]An[4595,13,4596,1873,3998,451,0]An[4595,13,4596,1873,4002,451,0]Ak[4595,13,4596,0,4272,451,0]0Ak[4595,13,4596,0,4696,451,0]0An[4595,13,4596,4170,3216,451,0]An[4595,13,4596,4170,4977,451,0]An[4595,13,4596,4726,4572,451,0]Am[4595,13,4596,4596,467,451,0]Am[4595,13,4596,4596,114,451,0]An[4595,13,4596,4596,2349,451,0]0An[4595,13,4596,4596,2358,451,0]An[4595,13,4596,4596,3030,451,0]An[4595,13,1781,1781,4163,451,0]An[4595,13,4838,4838,4737,451,0]An[4595,13,4838,4838,1299,451,0]0An[4595,13,1786,1786,1923,451,0]Am[4595,13,1786,1786,455,451,0]An[4595,13,1786,1786,4944,451,0]An[4595,13,1786,1786,3856,451,0]0An[4595,13,1786,1786,4702,451,0]An[4595,13,1863,1863,3245,451,0]An[4595,13,1863,1863,4133,451,0]An[4595,13,1863,1863,4568,451,0]An[4595,13,1863,1863,4570,451,0]An[4595,13,1863,1863,4793,451,0]De[4595,13,1863,0,1298,451,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]Dn[4595,13,1863,0,1298,451,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]Ak[4595,13,1863,0,1298,451,0]E`[4595,13,1863,0,1298,451,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Dg[4595,13,1863,0,1298,451,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]Da[4595,13,1863,0,1298,451,0,\"impl-From%3COtherError%3E-for-Error\"]Df[4595,13,1863,0,1298,451,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Df[4595,13,1863,0,1298,451,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]E`[4595,13,1863,0,1298,451,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]De[4595,13,1863,0,1298,451,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]Dg[4595,13,1863,0,1298,451,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]Dg[4595,13,1863,0,1298,451,0,\"impl-From%3CCertificateError%3E-for-Error\"]Al[4595,13,3589,129,108,451,0]Am[4595,13,3589,129,3021,451,0]An[4595,13,3589,1461,1285,451,0]An[4595,13,3589,1461,3858,451,0]An[4595,13,3589,2419,1928,451,0]An[4595,13,3589,2419,4150,451,0]An[4595,13,3589,3589,4630,451,0]An[4595,13,2057,2057,1300,451,0]An[4595,13,2057,2057,1303,451,0]An[4307,13,4308,4308,3248,451,0]0Am[4307,13,4308,375,4273,451,0]An[4307,13,4308,4308,2346,451,0]An[4307,13,4308,4308,2359,451,0]An[4307,13,4308,4308,4003,451,0]An[1015,13,1016,1016,3023,451,0]An[1015,13,1016,1016,4730,451,0]Am[1015,13,1775,1775,122,451,0]An[1015,13,1492,1492,1317,451,0]An[1015,13,1492,1492,3881,451,0]An[1015,13,1492,1492,4370,451,0]An[1015,13,4088,4088,4929,451,0]An[1015,13,4088,4088,4930,451,0]Am[1015,13,1868,1868,123,451,0]Am[3837,13,3838,3838,112,451,0]An[3837,13,3838,3838,2316,451,0]Ak[3817,13,3818,0,1552,452,0]Ak[3817,13,3818,0,1536,452,0]Ak[3817,13,3818,0,3862,452,0]Ak[3817,13,3818,0,3997,452,0]Aj[3817,13,3818,0,466,452,0]Aj[3817,13,3818,0,113,452,0]Ak[3817,13,3818,0,2348,452,0]An[4595,13,4596,1895,1553,452,0]An[4595,13,4596,3076,4638,452,0]An[4595,13,4596,1476,1537,452,0]An[4595,13,4596,1039,3052,452,0]An[4595,13,4596,1039,4157,452,0]An[4595,13,4596,1044,3250,452,0]An[4595,13,4596,3189,3863,452,0]An[4595,13,4596,2371,4546,452,0]An[4595,13,4596,1846,4636,452,0]An[4595,13,4596,1873,3998,452,0]Am[4595,13,4596,4596,467,452,0]Am[4595,13,4596,4596,114,452,0]An[4595,13,4596,4596,2349,452,0]An[4595,13,4596,4596,3030,452,0]Am[1320,13,1321,1321,121,453,0]An[1320,13,1321,1321,3849,453,0]An[1320,13,1490,1490,3880,453,0]An[1320,13,1490,1490,1316,453,0]An[1320,13,1490,1490,4269,453,0]Ak[476,13,477,477,3850,453,0]An[3886,13,3887,3887,4883,453,0]An[3817,13,3818,3593,4387,453,0]An[3817,13,3818,3593,2339,453,0]Ak[3817,13,3818,0,4121,453,0]An[3817,13,3818,1014,2340,453,0]An[3817,13,3818,3593,3204,453,0]An[3817,13,3818,3593,3206,453,0]An[3817,13,3818,3593,1930,453,0]An[3817,13,3818,3593,1529,453,0]An[3817,13,3818,3593,3013,453,0]An[3817,13,3818,3593,2313,453,0]An[3817,13,3818,3593,4021,453,0]An[3817,13,3818,3593,4026,453,0]An[3817,13,3818,3593,4799,453,0]An[3817,13,3818,3593,4637,453,0]An[3817,13,3818,2370,2325,453,0]An[3817,13,3818,2370,4130,453,0]An[3817,13,3818,2370,1508,453,0]An[3817,13,3818,2397,2329,453,0]An[3817,13,3818,2397,3233,453,0]An[3817,13,3818,2397,4249,453,0]Am[3817,13,3818,1014,459,453,0]An[3817,13,3818,3952,3230,453,0]An[3817,13,3818,3952,4738,453,0]An[3817,13,3818,3952,4267,453,0]An[3817,13,3818,3952,3051,453,0]An[3817,13,3818,3952,3232,453,0]An[3817,13,3818,3952,4266,453,0]An[3817,13,3818,1502,4036,453,0]An[3817,13,3818,1056,3875,453,0]An[3817,13,3818,3957,3236,453,0]An[3817,13,3818,3957,3249,453,0]An[3817,13,3818,3957,4035,453,0]Ak[3817,13,3818,0,3227,453,0]Ak[3817,13,3818,0,4161,453,0]Ak[3817,13,3818,0,4379,453,0]Ak[3817,13,3818,0,4013,453,0]Ak[3817,13,3818,0,4257,453,0]Ak[3817,13,3818,0,1552,453,0]Ak[3817,13,3818,0,4796,453,0]Ak[3817,13,3818,0,1280,453,0]Ak[3817,13,3818,0,1536,453,0]Ak[3817,13,3818,0,2360,453,0]Ak[3817,13,3818,0,3212,453,0]Ak[3817,13,3818,0,3048,453,0]Ak[3817,13,3818,0,4365,453,0]Ak[3817,13,3818,0,1952,453,0]Ak[3817,13,3818,0,3988,453,0]Ak[3817,13,3818,0,4361,453,0]Ak[3817,13,3818,0,4375,453,0]Ak[3817,13,3818,0,4382,453,0]Ak[3817,13,3818,0,4661,453,0]Ak[3817,13,3818,0,4676,453,0]Ak[3817,13,3818,0,3862,453,0]Ak[3817,13,3818,0,4244,453,0]Ak[3817,13,3818,0,4029,453,0]Ak[3817,13,3818,0,4853,453,0]Ak[3817,13,3818,0,3982,453,0]Ak[3817,13,3818,0,3852,453,0]Ak[3817,13,3818,0,3031,453,0]Ak[3817,13,3818,0,4358,453,0]Ak[3817,13,3818,0,4540,453,0]Ak[3817,13,3818,0,3994,453,0]Ak[3817,13,3818,0,3997,453,0]Ak[3817,13,3818,0,4001,453,0]Ak[3817,13,3818,0,4695,453,0]Ak[3817,13,3818,0,4571,453,0]Aj[3817,13,3818,0,466,453,0]Aj[3817,13,3818,0,113,453,0]Ak[3817,13,3818,0,2348,453,0]Ak[3817,13,3818,0,2357,453,0]Aj[3817,13,3588,0,107,453,0]Ak[3817,13,3588,0,3020,453,0]Ak[3817,13,3588,0,1284,453,0]Ak[3817,13,3588,0,3857,453,0]Ak[3817,13,3588,0,1927,453,0]Ak[3817,13,3588,0,4149,453,0]Ak[3817,13,3588,0,4629,453,0]Ak[3817,13,1785,0,1922,453,0]Aj[3817,13,1785,0,454,453,0]Ak[3817,13,1785,0,4943,453,0]Ak[3817,13,1785,0,3855,453,0]Ak[3817,13,1785,0,4699,453,0]Ak[3817,13,1862,0,3244,453,0]Ak[3817,13,1862,0,4132,453,0]Ak[3817,13,1862,0,4567,453,0]Ak[3817,13,1862,0,4569,453,0]Ak[3817,13,1862,0,4792,453,0]Ak[3817,13,1862,0,1297,453,0]An[4595,13,4596,4392,4122,453,0]An[4595,13,4596,3076,3026,453,0]An[4595,13,4596,1039,1516,453,0]An[4595,13,4596,2086,1947,453,0]An[4595,13,4596,2086,3231,453,0]An[4595,13,4596,2086,3237,453,0]An[4595,13,4596,3594,3207,453,0]An[4595,13,4596,1035,3228,453,0]An[4595,13,4596,1783,4162,453,0]An[4595,13,4596,2371,2326,453,0]An[4595,13,4596,2371,4128,453,0]An[4595,13,4596,1846,5002,453,0]An[4595,13,4596,4170,4972,453,0]An[4595,13,4596,1501,4380,453,0]An[4595,13,4596,1501,4014,453,0]An[4595,13,4596,1501,4258,453,0]An[4595,13,4596,1895,1553,453,0]An[4595,13,4596,1895,4797,453,0]An[4595,13,4596,3067,1281,453,0]An[4595,13,4596,3076,4638,453,0]An[4595,13,4596,1476,1537,453,0]An[4595,13,4596,1476,4025,453,0]An[4595,13,4596,1476,3226,453,0]An[4595,13,4596,1476,3047,453,0]An[4595,13,4596,1476,4692,453,0]An[4595,13,4596,1476,3202,453,0]An[4595,13,4596,1491,2361,453,0]An[4595,13,4596,1491,3213,453,0]An[4595,13,4596,1491,3049,453,0]An[4595,13,4596,1491,4366,453,0]An[4595,13,4596,1039,3052,453,0]An[4595,13,4596,1039,4157,453,0]An[4595,13,4596,1044,1953,453,0]An[4595,13,4596,1044,3250,453,0]An[4595,13,4596,1044,3861,453,0]An[4595,13,4596,2086,3879,453,0]An[4595,13,4596,2086,4268,453,0]An[4595,13,4596,2086,3992,453,0]An[4595,13,4596,3594,4362,453,0]An[4595,13,4596,3594,3044,453,0]An[4595,13,4596,3594,4235,453,0]An[4595,13,4596,3594,1302,453,0]An[4595,13,4596,1035,4376,453,0]An[4595,13,4596,3189,4383,453,0]An[4595,13,4596,3189,4662,453,0]An[4595,13,4596,3189,4677,453,0]An[4595,13,4596,3189,3863,453,0]An[4595,13,4596,1783,4245,453,0]An[4595,13,4596,1783,4031,453,0]An[4595,13,4596,1783,4854,453,0]An[4595,13,4596,1783,3984,453,0]An[4595,13,4596,1783,3853,453,0]An[4595,13,4596,1466,3032,453,0]An[4595,13,4596,1466,4360,453,0]An[4595,13,4596,1466,4542,453,0]An[4595,13,4596,1466,2319,453,0]An[4595,13,4596,2371,4546,453,0]An[4595,13,4596,2371,2330,453,0]An[4595,13,4596,2371,3995,453,0]An[4595,13,4596,2371,3847,453,0]An[4595,13,4596,2371,3234,453,0]An[4595,13,4596,1846,4152,453,0]An[4595,13,4596,1846,4374,453,0]An[4595,13,4596,1846,4882,453,0]An[4595,13,4596,1846,2332,453,0]An[4595,13,4596,1846,4636,453,0]An[4595,13,4596,1873,4364,453,0]An[4595,13,4596,1873,4129,453,0]An[4595,13,4596,1873,3998,453,0]An[4595,13,4596,1873,4002,453,0]Ak[4595,13,4596,0,4272,453,0]Ak[4595,13,4596,0,4696,453,0]An[4595,13,4596,4170,3216,453,0]An[4595,13,4596,4170,4977,453,0]An[4595,13,4596,4726,4572,453,0]Am[4595,13,4596,4596,467,453,0]Am[4595,13,4596,4596,114,453,0]An[4595,13,4596,4596,2349,453,0]An[4595,13,4596,4596,2358,453,0]An[4595,13,4596,4596,3030,453,0]An[4595,13,1781,1781,4163,453,0]An[4595,13,4838,4838,4737,453,0]An[4595,13,4838,4838,1299,453,0]An[4595,13,1786,1786,1923,453,0]Am[4595,13,1786,1786,455,453,0]An[4595,13,1786,1786,4944,453,0]An[4595,13,1786,1786,3856,453,0]An[4595,13,1786,1786,4702,453,0]An[4595,13,1863,1863,3245,453,0]An[4595,13,1863,1863,4133,453,0]An[4595,13,1863,1863,4568,453,0]An[4595,13,1863,1863,4570,453,0]An[4595,13,1863,1863,4793,453,0]Ak[4595,13,1863,0,1298,453,0]Al[4595,13,3589,129,108,453,0]Am[4595,13,3589,129,3021,453,0]An[4595,13,3589,1461,1285,453,0]An[4595,13,3589,1461,3858,453,0]An[4595,13,3589,2419,1928,453,0]An[4595,13,3589,2419,4150,453,0]An[4595,13,3589,3589,4630,453,0]An[4595,13,2057,2057,1300,453,0]An[4595,13,2057,2057,1303,453,0]An[4307,13,4308,4308,3248,453,0]Am[4307,13,4308,375,4273,453,0]An[4307,13,4308,4308,2346,453,0]An[4307,13,4308,4308,2359,453,0]An[4307,13,4308,4308,4003,453,0]An[1015,13,1016,1016,3023,453,0]An[1015,13,1016,1016,4730,453,0]Am[1015,13,1775,1775,122,453,0]An[1015,13,1492,1492,1317,453,0]An[1015,13,1492,1492,3881,453,0]An[1015,13,1492,1492,4370,453,0]An[1015,13,4088,4088,4929,453,0]An[1015,13,4088,4088,4930,453,0]Am[1015,13,1868,1868,123,453,0]Am[3837,13,3838,3838,112,453,0]An[3837,13,3838,3838,2316,453,0]Ac[476,7,477,0,0,0,0]Ae[3886,7,3887,0,0,0,0]Ae[4173,7,4174,0,0,0,0]Ae[3837,7,3838,0,0,0,0]Ae[4656,7,4657,0,0,0,0]Al[4595,13,4596,1039,1516,0,0]Ai[3817,12,1785,0,1906,0,0]Al[4595,13,4596,2086,1947,0,0]Al[4595,12,1786,1786,1907,0,0]Al[3817,13,3818,3593,3206,0,0]Ab[1015,3,0,0,0,0,0]Ak[3817,13,3818,1014,459,0,0]Ai[3817,14,3818,0,3212,0,0]Al[4595,13,4596,3594,3207,0,0]Al[4595,14,4596,1491,3213,0,0]Al[3817,12,3818,2078,3195,0,0]Ao[3817,13,3818,3593,4387,1526,0]Ao[3817,13,3818,3593,2339,1526,0]Ao[3817,13,3818,1014,2340,1526,0]Ao[3817,13,3818,3593,3204,1526,0]Ao[3817,13,3818,3593,1930,1526,0]Ao[3817,13,3818,3593,1529,1526,0]Ao[3817,13,3818,3593,3013,1526,0]Ao[3817,13,3818,3593,2313,1526,0]Ao[3817,13,3818,3593,4021,1526,0]Ao[3817,13,3818,3593,4026,1526,0]Ao[3817,13,3818,2370,1508,1526,0]Ao[3817,13,3818,2397,4249,1526,0]>Al[3817,13,3818,3952,3230,0,0]Al[4595,13,4596,2086,3231,0,0]Al[4595,13,4838,4838,4737,0,0]Al[4595,13,4596,3076,3026,0,0]Ak[4307,13,4308,375,4273,0,0]Ai[3817,12,1785,0,1906,0,0]Al[4595,12,1786,1786,1907,0,0]32Ai[3817,13,3818,0,4853,0,0]Al[4595,13,4596,1783,4854,0,0]Al[3817,13,3818,3593,3206,0,0]Al[4595,13,4596,3594,3207,0,0]Ai[3817,12,1785,0,1918,0,0]Al[4595,12,1786,1786,1919,0,0]Al[1320,14,1490,1490,4269,0,0]Ai[3817,14,3818,0,2357,0,0]Al[4595,14,4596,4596,2358,0,0]Ai[4595,14,4373,0,1313,0,0]Al[3817,13,3818,3952,3230,0,0]Al[3817,13,3818,3952,4738,0,0]Al[4595,13,4596,2086,3231,0,0]Ai[3817,12,3818,0,3246,0,0]Al[3817,13,3818,0,4257,3246,0]Al[3817,13,3818,0,4571,3246,0]Al[4595,12,4596,1501,3247,0,0]Ao[4595,13,4596,1501,4258,3247,0]Ao[4595,13,4596,4726,4572,3247,0]Am[1320,13,1321,1321,121,469,0]An[1320,13,1321,1321,3849,469,0]An[1320,13,1490,1490,3880,469,0]An[1320,13,1490,1490,1316,469,0]An[1320,13,1490,1490,4269,469,0]Ak[476,13,477,477,3850,469,0]An[3886,13,3887,3887,4883,469,0]An[3817,13,3818,3593,4387,469,0]An[3817,13,3818,3593,2339,469,0]Ak[3817,13,3818,0,4121,469,0]An[3817,13,3818,1014,2340,469,0]An[3817,13,3818,3593,3204,469,0]An[3817,13,3818,3593,3206,469,0]An[3817,13,3818,3593,1930,469,0]An[3817,13,3818,3593,1529,469,0]An[3817,13,3818,3593,3013,469,0]An[3817,13,3818,3593,2313,469,0]An[3817,13,3818,3593,4021,469,0]An[3817,13,3818,3593,4026,469,0]An[3817,13,3818,3593,4799,469,0]An[3817,13,3818,3593,4637,469,0]An[3817,13,3818,2370,2325,469,0]An[3817,13,3818,2370,4130,469,0]An[3817,13,3818,2370,1508,469,0]An[3817,13,3818,2397,2329,469,0]An[3817,13,3818,2397,3233,469,0]An[3817,13,3818,2397,4249,469,0]Am[3817,13,3818,1014,459,469,0]An[3817,13,3818,3952,3230,469,0]An[3817,13,3818,3952,4738,469,0]An[3817,13,3818,3952,4267,469,0]An[3817,13,3818,3952,3051,469,0]An[3817,13,3818,3952,3232,469,0]An[3817,13,3818,3952,4266,469,0]An[3817,13,3818,1502,4036,469,0]An[3817,13,3818,1056,3875,469,0]An[3817,13,3818,3957,3236,469,0]An[3817,13,3818,3957,3249,469,0]An[3817,13,3818,3957,4035,469,0]Ak[3817,13,3818,0,3227,469,0]Ak[3817,13,3818,0,4161,469,0]Ak[3817,13,3818,0,4379,469,0]Ak[3817,13,3818,0,4013,469,0]Ak[3817,13,3818,0,4257,469,0]Ak[3817,13,3818,0,1552,469,0]Ak[3817,13,3818,0,4796,469,0]Ak[3817,13,3818,0,1280,469,0]Ak[3817,13,3818,0,1536,469,0]Ak[3817,13,3818,0,2360,469,0]Ak[3817,13,3818,0,3212,469,0]Ak[3817,13,3818,0,3048,469,0]Ak[3817,13,3818,0,4365,469,0]Ak[3817,13,3818,0,1952,469,0]Ak[3817,13,3818,0,3988,469,0]Ak[3817,13,3818,0,4361,469,0]Ak[3817,13,3818,0,4375,469,0]Ak[3817,13,3818,0,4382,469,0]Ak[3817,13,3818,0,4661,469,0]Ak[3817,13,3818,0,4676,469,0]Ak[3817,13,3818,0,3862,469,0]Ak[3817,13,3818,0,4244,469,0]Ak[3817,13,3818,0,4029,469,0]Ak[3817,13,3818,0,4853,469,0]Ak[3817,13,3818,0,3982,469,0]Ak[3817,13,3818,0,3852,469,0]Ak[3817,13,3818,0,3031,469,0]Ak[3817,13,3818,0,4358,469,0]Ak[3817,13,3818,0,4540,469,0]Ak[3817,13,3818,0,3994,469,0]Ak[3817,13,3818,0,3997,469,0]Ak[3817,13,3818,0,4001,469,0]Ak[3817,13,3818,0,4695,469,0]Ak[3817,13,3818,0,4571,469,0]Aj[3817,13,3818,0,466,469,0]Aj[3817,13,3818,0,113,469,0]Ak[3817,13,3818,0,2348,469,0]Ak[3817,13,3818,0,2357,469,0]Aj[3817,13,3588,0,107,469,0]Ak[3817,13,3588,0,3020,469,0]Ak[3817,13,3588,0,1284,469,0]Ak[3817,13,3588,0,3857,469,0]Ak[3817,13,3588,0,1927,469,0]Ak[3817,13,3588,0,4149,469,0]Ak[3817,13,3588,0,4629,469,0]Ak[3817,13,1785,0,1922,469,0]Aj[3817,13,1785,0,454,469,0]Ak[3817,13,1785,0,4943,469,0]Ak[3817,13,1785,0,3855,469,0]Ak[3817,13,1785,0,4699,469,0]Ak[3817,13,1862,0,3244,469,0]Ak[3817,13,1862,0,4132,469,0]Ak[3817,13,1862,0,4567,469,0]Ak[3817,13,1862,0,4569,469,0]Ak[3817,13,1862,0,4792,469,0]Ak[3817,13,1862,0,1297,469,0]An[4595,13,4596,4392,4122,469,0]An[4595,13,4596,3076,3026,469,0]An[4595,13,4596,1039,1516,469,0]An[4595,13,4596,2086,1947,469,0]An[4595,13,4596,2086,3231,469,0]An[4595,13,4596,2086,3237,469,0]An[4595,13,4596,3594,3207,469,0]An[4595,13,4596,1035,3228,469,0]An[4595,13,4596,1783,4162,469,0]An[4595,13,4596,2371,2326,469,0]An[4595,13,4596,2371,4128,469,0]An[4595,13,4596,1846,5002,469,0]An[4595,13,4596,4170,4972,469,0]An[4595,13,4596,1501,4380,469,0]An[4595,13,4596,1501,4014,469,0]An[4595,13,4596,1501,4258,469,0]An[4595,13,4596,1895,1553,469,0]An[4595,13,4596,1895,4797,469,0]An[4595,13,4596,3067,1281,469,0]An[4595,13,4596,3076,4638,469,0]An[4595,13,4596,1476,1537,469,0]An[4595,13,4596,1476,4025,469,0]An[4595,13,4596,1476,3226,469,0]An[4595,13,4596,1476,3047,469,0]An[4595,13,4596,1476,4692,469,0]An[4595,13,4596,1476,3202,469,0]An[4595,13,4596,1491,2361,469,0]An[4595,13,4596,1491,3213,469,0]An[4595,13,4596,1491,3049,469,0]An[4595,13,4596,1491,4366,469,0]An[4595,13,4596,1039,3052,469,0]An[4595,13,4596,1039,4157,469,0]An[4595,13,4596,1044,1953,469,0]An[4595,13,4596,1044,3250,469,0]An[4595,13,4596,1044,3861,469,0]An[4595,13,4596,2086,3879,469,0]An[4595,13,4596,2086,4268,469,0]An[4595,13,4596,2086,3992,469,0]An[4595,13,4596,3594,4362,469,0]An[4595,13,4596,3594,3044,469,0]An[4595,13,4596,3594,4235,469,0]An[4595,13,4596,3594,1302,469,0]An[4595,13,4596,1035,4376,469,0]An[4595,13,4596,3189,4383,469,0]An[4595,13,4596,3189,4662,469,0]An[4595,13,4596,3189,4677,469,0]An[4595,13,4596,3189,3863,469,0]An[4595,13,4596,1783,4245,469,0]An[4595,13,4596,1783,4031,469,0]An[4595,13,4596,1783,4854,469,0]An[4595,13,4596,1783,3984,469,0]An[4595,13,4596,1783,3853,469,0]An[4595,13,4596,1466,3032,469,0]An[4595,13,4596,1466,4360,469,0]An[4595,13,4596,1466,4542,469,0]An[4595,13,4596,1466,2319,469,0]An[4595,13,4596,2371,4546,469,0]An[4595,13,4596,2371,2330,469,0]An[4595,13,4596,2371,3995,469,0]An[4595,13,4596,2371,3847,469,0]An[4595,13,4596,2371,3234,469,0]An[4595,13,4596,1846,4152,469,0]An[4595,13,4596,1846,4374,469,0]An[4595,13,4596,1846,4882,469,0]An[4595,13,4596,1846,2332,469,0]An[4595,13,4596,1846,4636,469,0]An[4595,13,4596,1873,4364,469,0]An[4595,13,4596,1873,4129,469,0]An[4595,13,4596,1873,3998,469,0]An[4595,13,4596,1873,4002,469,0]Ak[4595,13,4596,0,4272,469,0]Ak[4595,13,4596,0,4696,469,0]An[4595,13,4596,4170,3216,469,0]An[4595,13,4596,4170,4977,469,0]An[4595,13,4596,4726,4572,469,0]Am[4595,13,4596,4596,467,469,0]Am[4595,13,4596,4596,114,469,0]An[4595,13,4596,4596,2349,469,0]An[4595,13,4596,4596,2358,469,0]An[4595,13,4596,4596,3030,469,0]An[4595,13,1781,1781,4163,469,0]An[4595,13,4838,4838,4737,469,0]An[4595,13,4838,4838,1299,469,0]An[4595,13,1786,1786,1923,469,0]Am[4595,13,1786,1786,455,469,0]An[4595,13,1786,1786,4944,469,0]An[4595,13,1786,1786,3856,469,0]An[4595,13,1786,1786,4702,469,0]An[4595,13,1863,1863,3245,469,0]An[4595,13,1863,1863,4133,469,0]An[4595,13,1863,1863,4568,469,0]An[4595,13,1863,1863,4570,469,0]An[4595,13,1863,1863,4793,469,0]Ak[4595,13,1863,0,1298,469,0]Al[4595,13,3589,129,108,469,0]Am[4595,13,3589,129,3021,469,0]An[4595,13,3589,1461,1285,469,0]An[4595,13,3589,1461,3858,469,0]An[4595,13,3589,2419,1928,469,0]An[4595,13,3589,2419,4150,469,0]An[4595,13,3589,3589,4630,469,0]An[4595,13,2057,2057,1300,469,0]An[4595,13,2057,2057,1303,469,0]An[1015,13,1016,1016,3023,469,0]An[1015,13,1016,1016,4730,469,0]Am[1015,13,1775,1775,122,469,0]An[1015,13,1492,1492,1317,469,0]An[1015,13,1492,1492,3881,469,0]An[1015,13,1492,1492,4370,469,0]An[1015,13,4088,4088,4929,469,0]An[1015,13,4088,4088,4930,469,0]Am[1015,13,1868,1868,123,469,0]Am[3837,13,3838,3838,112,469,0]An[3837,13,3838,3838,2316,469,0]Ah[3817,5,3818,3067,0,0,0]Ah[4595,5,4596,3067,0,0,0]Ah[3817,5,3588,1461,0,0,0]Ah[4595,5,3589,1461,0,0,0]Al[1015,15,1016,1016,4730,0,0]Ah[3817,6,1862,1464,0,0,0]Ah[4595,6,1863,1464,0,0,0]Ae[4595,6,4838,0,0,0,0]Ae[4595,6,2057,0,0,0,0]Ah[4595,6,4596,3594,0,0,0]1Ai[3817,15,1862,0,1297,0,0]Ai[4595,15,1863,0,1298,0,0]Al[3817,15,3818,3952,3051,0,0]Ai[3817,15,3818,0,4361,0,0]Al[4595,15,4596,1039,3052,0,0]Al[4595,15,4596,3594,4362,0,0]Al[4595,15,4596,1846,4374,0,0]0Ae[1320,5,1490,0,0,0,0]Ae[1015,5,1492,0,0,0,0]Ab[1320,3,0,0,0,0,0]Ai[3817,12,3588,0,4627,0,0]Al[3817,13,3588,0,3020,4627,0]Al[3817,13,3588,0,3857,4627,0]Al[3817,13,3588,0,4149,4627,0]Al[4595,12,3589,3589,4628,0,0]An[4595,13,3589,129,3021,4628,0]Ao[4595,13,3589,1461,3858,4628,0]Ao[4595,13,3589,2419,4150,4628,0]Al[1015,13,1016,1016,4730,0,0]Ai[3817,14,3818,0,1280,0,0]Ai[3817,14,3818,0,2360,0,0]Ai[3817,14,3818,0,1952,0,0]Al[4595,14,4596,3067,1281,0,0]Al[4595,14,4596,1491,2361,0,0]Al[4595,14,4596,1044,1953,0,0]Al[4595,14,4596,2371,3847,0,0]An[1320,13,1321,1321,121,1282,0]Ao[1320,13,1321,1321,3849,1282,0]Ao[3817,13,3818,3593,3206,1282,0]Ao[3817,13,3818,3593,4799,1282,0]Ao[3817,13,3818,3593,4637,1282,0]Ao[3817,13,3818,2370,2325,1282,0]Ao[3817,13,3818,2370,4130,1282,0]Ao[3817,13,3818,3952,4267,1282,0]Ao[3817,13,3818,3952,3051,1282,0]Ao[3817,13,3818,3952,3232,1282,0]Ao[3817,13,3818,3952,4266,1282,0]Ao[3817,13,3818,3957,3249,1282,0]Ao[3817,13,3818,3957,4035,1282,0]Al[3817,13,3818,0,1552,1282,0]Al[3817,13,3818,0,4796,1282,0]Al[3817,13,3818,0,1536,1282,0]Al[3817,13,3818,0,2360,1282,0]Al[3817,13,3818,0,3212,1282,0]Al[3817,13,3818,0,3048,1282,0]Al[3817,13,3818,0,4365,1282,0]Al[3817,13,3818,0,1952,1282,0]Al[3817,13,3818,0,3988,1282,0]Al[3817,13,3818,0,4361,1282,0]Al[3817,13,3818,0,4382,1282,0]Al[3817,13,3818,0,4661,1282,0]Al[3817,13,3818,0,4676,1282,0]Al[3817,13,3818,0,3862,1282,0]Al[3817,13,3818,0,4244,1282,0]Al[3817,13,3818,0,4029,1282,0]Al[3817,13,3818,0,4853,1282,0]Al[3817,13,3818,0,3982,1282,0]Al[3817,13,3818,0,3852,1282,0]Al[3817,13,3818,0,3031,1282,0]Al[3817,13,3818,0,4358,1282,0]Al[3817,13,3818,0,4540,1282,0]Al[3817,13,3818,0,3994,1282,0]Al[3817,13,3818,0,3997,1282,0]Al[3817,13,3818,0,4001,1282,0]Al[3817,13,3818,0,4695,1282,0]Ak[3817,13,3818,0,466,1282,0]Ak[3817,13,3818,0,113,1282,0]Al[3817,13,3818,0,2348,1282,0]Ak[3817,13,3588,0,107,1282,0]Al[3817,13,3588,0,3020,1282,0]Al[3817,13,3588,0,1284,1282,0]Al[3817,13,3588,0,3857,1282,0]Al[3817,13,3588,0,1927,1282,0]Al[3817,13,3588,0,4149,1282,0]Al[3817,13,1862,0,4792,1282,0]Al[3817,13,1862,0,1297,1282,0]Ao[4595,13,4596,1895,1553,1282,0]Ao[4595,13,4596,1895,4797,1282,0]Ao[4595,13,4596,3076,4638,1282,0]Ao[4595,13,4596,1476,1537,1282,0]Ao[4595,13,4596,1476,4025,1282,0]Ao[4595,13,4596,1476,3226,1282,0]Ao[4595,13,4596,1476,3047,1282,0]Ao[4595,13,4596,1476,4692,1282,0]Ao[4595,13,4596,1476,3202,1282,0]Ao[4595,13,4596,1491,2361,1282,0]Ao[4595,13,4596,1491,3213,1282,0]Ao[4595,13,4596,1491,3049,1282,0]Ao[4595,13,4596,1491,4366,1282,0]Ao[4595,13,4596,1039,3052,1282,0]Ao[4595,13,4596,1039,4157,1282,0]Ao[4595,13,4596,1044,1953,1282,0]Ao[4595,13,4596,1044,3250,1282,0]Ao[4595,13,4596,1044,3861,1282,0]Ao[4595,13,4596,2086,4268,1282,0]Ao[4595,13,4596,2086,3992,1282,0]Ao[4595,13,4596,3594,4362,1282,0]Ao[4595,13,4596,3594,3044,1282,0]Ao[4595,13,4596,3594,4235,1282,0]Ao[4595,13,4596,3189,4383,1282,0]Ao[4595,13,4596,3189,4662,1282,0]Ao[4595,13,4596,3189,4677,1282,0]Ao[4595,13,4596,3189,3863,1282,0]Ao[4595,13,4596,1783,4245,1282,0]Ao[4595,13,4596,1783,4031,1282,0]Ao[4595,13,4596,1783,4854,1282,0]Ao[4595,13,4596,1783,3984,1282,0]Ao[4595,13,4596,1783,3853,1282,0]Ao[4595,13,4596,1466,3032,1282,0]Ao[4595,13,4596,1466,4360,1282,0]Ao[4595,13,4596,1466,4542,1282,0]Ao[4595,13,4596,1466,2319,1282,0]Ao[4595,13,4596,2371,4546,1282,0]Ao[4595,13,4596,2371,3995,1282,0]Ao[4595,13,4596,1846,4374,1282,0]Ao[4595,13,4596,1846,4882,1282,0]Ao[4595,13,4596,1846,2332,1282,0]Ao[4595,13,4596,1846,4636,1282,0]Ao[4595,13,4596,1873,3998,1282,0]Ao[4595,13,4596,1873,4002,1282,0]Al[4595,13,4596,0,4272,1282,0]Al[4595,13,4596,0,4696,1282,0]Ao[4595,13,4596,4170,3216,1282,0]Ao[4595,13,4596,4170,4977,1282,0]An[4595,13,4596,4596,467,1282,0]An[4595,13,4596,4596,114,1282,0]Ao[4595,13,4596,4596,2349,1282,0]Ao[4595,13,4596,4596,3030,1282,0]Ao[4595,13,1781,1781,4163,1282,0]Ao[4595,13,4838,4838,4737,1282,0]Ao[4595,13,4838,4838,1299,1282,0]Ao[4595,13,1863,1863,4793,1282,0]Al[4595,13,1863,0,1298,1282,0]Am[4595,13,3589,129,108,1282,0]An[4595,13,3589,129,3021,1282,0]Ao[4595,13,3589,1461,1285,1282,0]Ao[4595,13,3589,1461,3858,1282,0]Ao[4595,13,3589,2419,1928,1282,0]Ao[4595,13,3589,2419,4150,1282,0]Ao[4595,13,2057,2057,1303,1282,0]Ao[4307,13,4308,4308,2346,1282,0]Ao[4307,13,4308,4308,2359,1282,0]Ao[4307,13,4308,4308,4003,1282,0]Ao[1015,13,1016,1016,4730,1282,0]Ao[1015,13,1492,1492,4370,1282,0]Al[3817,13,3818,3593,3206,0,0]Al[3817,13,3818,2370,2325,0,0]Ak[3817,13,3818,1014,459,0,0]Al[4595,13,4596,3594,3207,0,0]Al[3817,13,3818,0,3997,1288,0]Ao[4595,13,4596,1873,3998,1288,0]Ai[4595,14,3045,0,4140,0,0]Ai[4595,14,4596,0,4272,0,0]Ai[3817,13,3818,0,2348,0,0]Al[4595,13,4596,4596,2349,0,0]Ai[3817,13,3818,0,4001,0,0]Al[4595,13,4596,1873,4002,0,0]Ai[3817,14,1785,0,1922,0,0]Al[4595,14,1786,1786,1923,0,0]Al[4595,12,4596,1846,4667,0,0]Ao[4595,13,4596,1846,5002,4667,0]=Al[4595,13,4838,4838,4737,0,0]Ah[3817,5,3818,2370,0,0,0]Ai[3817,15,3818,0,3031,0,0]Al[4595,15,4596,1466,3032,0,0]Al[1320,15,1321,1321,3849,0,0]0Al[4595,15,4596,1476,3226,0,0]Ah[4595,5,4596,1039,0,0,0]Ah[3817,15,3818,0,466,0,0]Ak[4595,15,4596,4596,467,0,0]Al[3837,15,3838,3838,2316,0,0]Al[4595,15,4596,1476,3047,0,0]Al[4595,15,2057,2057,1300,0,0]98Ah[3817,5,3818,3593,0,0,0]2Al[4595,15,4596,2086,3879,0,0]Ah[3817,5,3818,1476,0,0,0]Ah[4595,5,4596,1476,0,0,0]Ae[4595,8,1781,0,0,0,0]987>Al[4595,15,4596,3594,1302,0,0]>Ah[3817,5,3818,1895,0,0,0]Ah[4595,5,4596,1895,0,0,0]Al[3817,13,3818,2370,2325,0,0]Al[3817,13,3818,2397,2329,0,0]Al[4595,13,4596,2086,1947,0,0]Al[4595,13,4596,2371,2326,0,0]An[1320,13,1321,1321,121,1514,0]Ao[1320,13,1321,1321,3849,1514,0]Ao[1320,13,1490,1490,3880,1514,0]Ao[1320,13,1490,1490,1316,1514,0]Ao[1320,13,1490,1490,4269,1514,0]Al[476,13,477,477,3850,1514,0]Ao[3886,13,3887,3887,4883,1514,0]Ao[3817,13,3818,3593,4387,1514,0]Ao[3817,13,3818,3593,2339,1514,0]Al[3817,13,3818,0,4121,1514,0]Ao[3817,13,3818,1014,2340,1514,0]Ao[3817,13,3818,3593,3204,1514,0]Ao[3817,13,3818,3593,3206,1514,0]Ao[3817,13,3818,3593,1930,1514,0]Ao[3817,13,3818,3593,1529,1514,0]Ao[3817,13,3818,3593,3013,1514,0]Ao[3817,13,3818,3593,2313,1514,0]Ao[3817,13,3818,3593,4021,1514,0]Ao[3817,13,3818,3593,4026,1514,0]Ao[3817,13,3818,3593,4799,1514,0]Ao[3817,13,3818,3593,4637,1514,0]Ao[3817,13,3818,2370,2325,1514,0]Ao[3817,13,3818,2370,4130,1514,0]Ao[3817,13,3818,2370,1508,1514,0]Ao[3817,13,3818,2397,2329,1514,0]Ao[3817,13,3818,2397,3233,1514,0]Ao[3817,13,3818,2397,4249,1514,0]An[3817,13,3818,1014,459,1514,0]Ao[3817,13,3818,3952,3230,1514,0]Ao[3817,13,3818,3952,4738,1514,0]Ao[3817,13,3818,3952,4267,1514,0]Ao[3817,13,3818,3952,3051,1514,0]Ao[3817,13,3818,3952,3232,1514,0]Ao[3817,13,3818,3952,4266,1514,0]Ao[3817,13,3818,1502,4036,1514,0]Ao[3817,13,3818,1056,3875,1514,0]Ao[3817,13,3818,3957,3236,1514,0]Ao[3817,13,3818,3957,3249,1514,0]Ao[3817,13,3818,3957,4035,1514,0]Al[3817,13,3818,0,3227,1514,0]Al[3817,13,3818,0,4161,1514,0]Al[3817,13,3818,0,4379,1514,0]Al[3817,13,3818,0,4013,1514,0]Al[3817,13,3818,0,4257,1514,0]Al[3817,13,3818,0,1552,1514,0]Al[3817,13,3818,0,4796,1514,0]Al[3817,13,3818,0,1280,1514,0]Al[3817,13,3818,0,1536,1514,0]Al[3817,13,3818,0,2360,1514,0]Al[3817,13,3818,0,3212,1514,0]Al[3817,13,3818,0,3048,1514,0]Al[3817,13,3818,0,4365,1514,0]Al[3817,13,3818,0,1952,1514,0]Al[3817,13,3818,0,3988,1514,0]Al[3817,13,3818,0,4361,1514,0]Al[3817,13,3818,0,4375,1514,0]Al[3817,13,3818,0,4382,1514,0]Al[3817,13,3818,0,4661,1514,0]Al[3817,13,3818,0,4676,1514,0]Al[3817,13,3818,0,3862,1514,0]Al[3817,13,3818,0,4244,1514,0]Al[3817,13,3818,0,4029,1514,0]Al[3817,13,3818,0,4853,1514,0]Al[3817,13,3818,0,3982,1514,0]Al[3817,13,3818,0,3852,1514,0]Al[3817,13,3818,0,3031,1514,0]Al[3817,13,3818,0,4358,1514,0]Al[3817,13,3818,0,4540,1514,0]Al[3817,13,3818,0,3994,1514,0]Al[3817,13,3818,0,3997,1514,0]Al[3817,13,3818,0,4001,1514,0]Al[3817,13,3818,0,4695,1514,0]Al[3817,13,3818,0,4571,1514,0]Ak[3817,13,3818,0,466,1514,0]Ak[3817,13,3818,0,113,1514,0]Al[3817,13,3818,0,2348,1514,0]Al[3817,13,3818,0,2357,1514,0]Ak[3817,13,3588,0,107,1514,0]Al[3817,13,3588,0,3020,1514,0]Al[3817,13,3588,0,1284,1514,0]Al[3817,13,3588,0,3857,1514,0]Al[3817,13,3588,0,1927,1514,0]Al[3817,13,3588,0,4149,1514,0]Al[3817,13,3588,0,4629,1514,0]Al[3817,13,1785,0,1922,1514,0]Ak[3817,13,1785,0,454,1514,0]Al[3817,13,1785,0,4943,1514,0]Al[3817,13,1785,0,3855,1514,0]Al[3817,13,1785,0,4699,1514,0]Al[3817,13,1862,0,3244,1514,0]Al[3817,13,1862,0,4132,1514,0]Al[3817,13,1862,0,4567,1514,0]Al[3817,13,1862,0,4569,1514,0]Al[3817,13,1862,0,4792,1514,0]Al[3817,13,1862,0,1297,1514,0]Ao[4595,13,4596,4392,4122,1514,0]Ao[4595,13,4596,3076,3026,1514,0]Ao[4595,13,4596,1039,1516,1514,0]Ao[4595,13,4596,2086,1947,1514,0]Ao[4595,13,4596,2086,3231,1514,0]Ao[4595,13,4596,2086,3237,1514,0]Ao[4595,13,4596,3594,3207,1514,0]Ao[4595,13,4596,1035,3228,1514,0]Ao[4595,13,4596,1783,4162,1514,0]Ao[4595,13,4596,2371,2326,1514,0]Ao[4595,13,4596,2371,4128,1514,0]Ao[4595,13,4596,1846,5002,1514,0]Ao[4595,13,4596,4170,4972,1514,0]Ao[4595,13,4596,1501,4380,1514,0]Ao[4595,13,4596,1501,4014,1514,0]Ao[4595,13,4596,1501,4258,1514,0]Ao[4595,13,4596,1895,1553,1514,0]Ao[4595,13,4596,1895,4797,1514,0]Ao[4595,13,4596,3067,1281,1514,0]Ao[4595,13,4596,3076,4638,1514,0]Ao[4595,13,4596,1476,1537,1514,0]Ao[4595,13,4596,1476,4025,1514,0]Ao[4595,13,4596,1476,3226,1514,0]Ao[4595,13,4596,1476,3047,1514,0]Ao[4595,13,4596,1476,4692,1514,0]Ao[4595,13,4596,1476,3202,1514,0]Ao[4595,13,4596,1491,2361,1514,0]Ao[4595,13,4596,1491,3213,1514,0]Ao[4595,13,4596,1491,3049,1514,0]Ao[4595,13,4596,1491,4366,1514,0]Ao[4595,13,4596,1039,3052,1514,0]Ao[4595,13,4596,1039,4157,1514,0]Ao[4595,13,4596,1044,1953,1514,0]Ao[4595,13,4596,1044,3250,1514,0]Ao[4595,13,4596,1044,3861,1514,0]Ao[4595,13,4596,2086,3879,1514,0]Ao[4595,13,4596,2086,4268,1514,0]Ao[4595,13,4596,2086,3992,1514,0]Ao[4595,13,4596,3594,4362,1514,0]Ao[4595,13,4596,3594,3044,1514,0]Ao[4595,13,4596,3594,4235,1514,0]Ao[4595,13,4596,3594,1302,1514,0]Ao[4595,13,4596,1035,4376,1514,0]Ao[4595,13,4596,3189,4383,1514,0]Ao[4595,13,4596,3189,4662,1514,0]Ao[4595,13,4596,3189,4677,1514,0]Ao[4595,13,4596,3189,3863,1514,0]Ao[4595,13,4596,1783,4245,1514,0]Ao[4595,13,4596,1783,4031,1514,0]Ao[4595,13,4596,1783,4854,1514,0]Ao[4595,13,4596,1783,3984,1514,0]Ao[4595,13,4596,1783,3853,1514,0]Ao[4595,13,4596,1466,3032,1514,0]Ao[4595,13,4596,1466,4360,1514,0]Ao[4595,13,4596,1466,4542,1514,0]Ao[4595,13,4596,1466,2319,1514,0]Ao[4595,13,4596,2371,4546,1514,0]Ao[4595,13,4596,2371,2330,1514,0]Ao[4595,13,4596,2371,3995,1514,0]Ao[4595,13,4596,2371,3847,1514,0]Ao[4595,13,4596,2371,3234,1514,0]Ao[4595,13,4596,1846,4152,1514,0]Ao[4595,13,4596,1846,4374,1514,0]Ao[4595,13,4596,1846,4882,1514,0]Ao[4595,13,4596,1846,2332,1514,0]Ao[4595,13,4596,1846,4636,1514,0]Ao[4595,13,4596,1873,4364,1514,0]Ao[4595,13,4596,1873,4129,1514,0]Ao[4595,13,4596,1873,3998,1514,0]Ao[4595,13,4596,1873,4002,1514,0]Al[4595,13,4596,0,4272,1514,0]Al[4595,13,4596,0,4696,1514,0]Ao[4595,13,4596,4170,3216,1514,0]Ao[4595,13,4596,4170,4977,1514,0]Ao[4595,13,4596,4726,4572,1514,0]An[4595,13,4596,4596,467,1514,0]An[4595,13,4596,4596,114,1514,0]Ao[4595,13,4596,4596,2349,1514,0]Ao[4595,13,4596,4596,2358,1514,0]Ao[4595,13,4596,4596,3030,1514,0]Ao[4595,13,1781,1781,4163,1514,0]Ao[4595,13,4838,4838,4737,1514,0]Ao[4595,13,4838,4838,1299,1514,0]Ao[4595,13,1786,1786,1923,1514,0]An[4595,13,1786,1786,455,1514,0]Ao[4595,13,1786,1786,4944,1514,0]Ao[4595,13,1786,1786,3856,1514,0]Ao[4595,13,1786,1786,4702,1514,0]Ao[4595,13,1863,1863,3245,1514,0]Ao[4595,13,1863,1863,4133,1514,0]Ao[4595,13,1863,1863,4568,1514,0]Ao[4595,13,1863,1863,4570,1514,0]Ao[4595,13,1863,1863,4793,1514,0]Al[4595,13,1863,0,1298,1514,0]Am[4595,13,3589,129,108,1514,0]An[4595,13,3589,129,3021,1514,0]Ao[4595,13,3589,1461,1285,1514,0]Ao[4595,13,3589,1461,3858,1514,0]Ao[4595,13,3589,2419,1928,1514,0]Ao[4595,13,3589,2419,4150,1514,0]Ao[4595,13,3589,3589,4630,1514,0]Ao[4595,13,2057,2057,1300,1514,0]Ao[4595,13,2057,2057,1303,1514,0]Ao[4307,13,4308,4308,3248,1514,0]An[4307,13,4308,375,4273,1514,0]Ao[4307,13,4308,4308,2346,1514,0]Ao[4307,13,4308,4308,2359,1514,0]Ao[4307,13,4308,4308,4003,1514,0]Ao[1015,13,1016,1016,3023,1514,0]Ao[1015,13,1016,1016,4730,1514,0]An[1015,13,1775,1775,122,1514,0]Ao[1015,13,1492,1492,1317,1514,0]Ao[1015,13,1492,1492,3881,1514,0]Ao[1015,13,1492,1492,4370,1514,0]Ao[1015,13,4088,4088,4929,1514,0]Ao[1015,13,4088,4088,4930,1514,0]An[1015,13,1868,1868,123,1514,0]An[3837,13,3838,3838,112,1514,0]Ao[3837,13,3838,3838,2316,1514,0]Ai[3817,14,3818,0,1952,0,0]Al[4595,14,4596,1044,1953,0,0]Ak[1320,14,1321,1321,121,0,0]Ac[476,7,477,0,0,0,0]Al[3817,13,3818,2370,2325,0,0]Ae[1015,2,1016,0,0,0,0]Ae[4656,7,4657,0,0,0,0]Al[3817,13,3818,3593,3206,0,0]Ae[4595,2,4596,0,0,0,0]Al[4595,13,4596,2371,2326,0,0]Ah[3817,2,3818,4596,0,0,0]Ai[3817,13,3818,0,4695,0,0]Ai[3817,14,3818,0,3212,0,0]Ai[3817,14,3818,0,4029,0,0]5Ai[4595,13,4596,0,4696,0,0]Al[4595,14,4596,1491,3213,0,0]Al[4595,14,4596,1783,4031,0,0]Ai[4595,14,4596,0,4272,0,0]Al[3817,13,3818,0,1552,3027,0]Al[3817,13,3818,0,1536,3027,0]Al[3817,13,3818,0,3031,3027,0]Al[3817,13,3818,0,4695,3027,0]Al[3817,13,3818,0,2348,3027,0]Ao[4595,13,4596,1895,1553,3027,0]Ao[4595,13,4596,1476,1537,3027,0]Ao[4595,13,4596,1466,3032,3027,0]Al[4595,13,4596,1846,4374,0,0]Al[4595,13,4596,0,4696,3027,0]Ao[4595,13,4596,4596,2349,3027,0]Al[4595,12,1781,1781,3027,0,0]Al[4307,14,4308,4308,2346,0,0]Al[3817,13,3818,0,1552,3028,0]Al[3817,13,3818,0,1536,3028,0]Al[3817,13,3818,0,3031,3028,0]Al[3817,13,3818,0,4695,3028,0]Al[3817,13,3818,0,2348,3028,0]Ao[4595,13,4596,1895,1553,3028,0]Ao[4595,13,4596,1476,1537,3028,0]Ao[4595,13,4596,1466,3032,3028,0]Ao[4595,13,4596,1466,2319,3028,0]Al[4595,13,4596,0,4696,3028,0]Ao[4595,13,4596,4596,2349,3028,0]Al[4595,12,1781,1781,3028,0,0]Al[3817,13,3818,3957,3236,0,0]Al[4595,13,4596,2086,3237,0,0]Al[1015,13,1492,1492,3881,0,0]Al[4595,13,4596,2371,2326,0,0]Ah[3817,14,1785,0,454,0,0]Ak[4595,14,1786,1786,455,0,0]Al[3817,13,3818,2397,2329,0,0]3Ai[3817,12,3818,0,3246,0,0]Al[3817,13,3818,0,4257,3246,0]Al[3817,13,3818,0,4571,3246,0]Al[4595,12,4596,1501,3247,0,0]Ao[4595,13,4596,1501,4258,3247,0]Ao[4595,13,4596,4726,4572,3247,0]Al[1015,14,1016,1016,3023,0,0]Ai[3817,14,3818,0,1280,0,0]Al[4595,14,4596,3067,1281,0,0]Ai[3817,13,3588,0,3208,0,0]Ak[3817,13,3588,0,107,3208,0]Al[3817,13,3588,0,1284,3208,0]Al[3817,13,3588,0,1927,3208,0]Al[4595,13,3589,3589,3209,0,0]Am[4595,13,3589,129,108,3209,0]Ao[4595,13,3589,1461,1285,3209,0]Ao[4595,13,3589,2419,1928,3209,0]Ah[3817,14,1785,0,454,0,0]Ak[4595,14,1786,1786,455,0,0]Ai[4595,14,4373,0,4575,0,0]Ai[3817,14,3818,0,4358,0,0]Ai[3817,14,3818,0,4540,0,0]Al[4595,14,4596,1466,4360,0,0]Al[4595,14,4596,1466,4542,0,0]Ai[4595,14,4596,0,4272,0,0]Ai[4595,14,1301,0,4237,0,0]Al[3817,13,3818,2370,2325,0,0]Al[3817,13,3818,2397,2329,0,0]Al[4595,13,4596,2371,2326,0,0]Ai[3817,14,1785,0,1922,0,0]Al[4595,14,4596,4596,3030,0,0]Al[4595,14,1786,1786,1923,0,0]Ae[3817,4,3818,0,0,0,0]Ah[3817,2,1785,1786,0,0,0]Ae[4595,4,4596,0,0,0,0]Ae[4595,2,1786,0,0,0,0]Ac[476,7,477,0,0,0,0]:Ae[1015,2,1016,0,0,0,0]Ae[4656,7,4657,0,0,0,0]Ao[3817,13,3818,3593,4637,1290,0]Ao[3817,13,3818,3952,4267,1290,0]Ao[3817,13,3818,3952,3051,1290,0]Ao[3817,13,3818,3952,3232,1290,0]Ao[3817,13,3818,3952,4266,1290,0]Ao[3817,13,3818,3957,3249,1290,0]Ao[3817,13,3818,3957,4035,1290,0]Al[3817,13,3818,0,4361,1290,0]Ao[4595,13,4596,3594,4362,1290,0]Ai[3817,14,3818,0,2357,0,0]Al[4595,14,4596,4596,2358,0,0]Al[4307,14,4308,4308,2359,0,0]Ak[3817,13,3818,1014,459,0,0]Ai[4595,14,3045,0,4022,0,0]Ai[3817,14,3818,0,3212,0,0]Al[4595,14,4596,1491,3213,0,0]Al[4307,14,4308,4308,2346,0,0]Ai[3817,14,3818,0,4365,0,0]Al[4595,14,4596,1491,4366,0,0]10Ai[3817,12,1785,0,1918,0,0]Al[4595,12,1786,1786,1919,0,0]Ai[3817,12,3588,0,3208,0,0]Ak[3817,13,3588,0,107,3208,0]Al[3817,13,3588,0,1284,3208,0]Al[3817,13,3588,0,1927,3208,0]Al[4595,12,3589,3589,3209,0,0]Am[4595,13,3589,129,108,3209,0]Ao[4595,13,3589,1461,1285,3209,0]Ao[4595,13,3589,2419,1928,3209,0]Ai[3817,10,1785,1786,0,0,0]Af[4595,10,1786,0,0,0,0]Al[4595,15,4596,3076,4638,0,0]Al[4595,15,4596,1039,3052,0,0]Al[4595,15,4596,1044,3250,0,0]Ai[3817,15,1862,0,1297,0,0]Ai[4595,15,1863,0,1298,0,0]65Al[4595,15,4596,1846,4374,0,0]Ah[3817,5,1785,1786,0,0,0]Ae[4595,5,1786,0,0,0,0]Ai[3817,15,3818,0,3031,0,0]Al[4595,15,4596,1466,3032,0,0]Ah[3817,5,3588,2419,0,0,0]Ah[4595,5,3589,2419,0,0,0]Al[1015,15,1016,1016,4730,0,0]Ah[3817,5,3818,3593,0,0,0]5454Ai[3817,10,3818,2078,0,0,0]Ai[3817,10,1785,1786,0,0,0]Af[4595,10,1786,0,0,0,0]Al[3817,15,3818,3957,3249,0,0]?Al[4595,15,4596,1044,3861,0,0]Al[4595,15,4596,2086,3879,0,0]Ah[4595,5,4596,2086,0,0,0]Al[3817,15,3818,3952,4267,0,0]Ah[3817,5,3818,1044,0,0,0]Ah[4595,5,4596,1044,0,0,0]Al[4595,15,4596,1846,2332,0,0]Ai[3817,12,3818,0,4844,0,0]Al[4595,12,4596,4170,4845,0,0]Ao[4595,13,4596,4170,4972,4845,0]Ao[4595,13,4596,4170,4977,4845,0]Ak[1320,14,1321,1321,121,0,0]An[1320,13,1321,1321,121,4236,0]Ao[1015,13,1016,1016,3023,4236,0]An[1015,13,1775,1775,122,4236,0]An[1015,13,1868,1868,123,4236,0]An[3837,13,3838,3838,112,4236,0]Ak[3837,14,3838,3838,112,0,0]Al[3817,13,3818,0,1552,3203,0]Al[3817,13,3818,0,1536,3203,0]Al[3817,13,3818,0,3862,3203,0]Al[3817,13,3818,0,3997,3203,0]Ak[3817,13,3818,0,466,3203,0]Ak[3817,13,3818,0,113,3203,0]Al[3817,13,3818,0,2348,3203,0]Ao[4595,13,4596,1895,1553,3203,0]Ao[4595,13,4596,3076,4638,3203,0]Ao[4595,13,4596,1476,1537,3203,0]Ao[4595,13,4596,1039,3052,3203,0]Ao[4595,13,4596,1039,4157,3203,0]Ao[4595,13,4596,1044,3250,3203,0]Ao[4595,13,4596,3189,3863,3203,0]Ao[4595,13,4596,2371,4546,3203,0]Ao[4595,13,4596,1846,4636,3203,0]Ao[4595,13,4596,1873,3998,3203,0]An[4595,13,4596,4596,467,3203,0]An[4595,13,4596,4596,114,3203,0]Ao[4595,13,4596,4596,2349,3203,0]Al[3817,13,3818,2370,2325,0,0]Al[4595,13,4596,2371,2326,0,0]Ai[3817,12,1785,0,3041,0,0]Ai[3817,12,1785,0,3034,0,0]Al[4595,12,1786,1786,3042,0,0]Al[4595,12,1786,1786,3035,0,0]Ao[1320,13,1490,1490,3880,1913,0]Ao[1320,13,1490,1490,1316,1913,0]Al[476,13,477,477,3850,1913,0]Al[3817,13,3818,0,4121,1913,0]Ao[3817,13,3818,2370,4130,1913,0]Al[3817,13,3818,0,1552,1913,0]Al[3817,13,3818,0,1536,1913,0]Al[3817,13,3818,0,2360,1913,0]Al[3817,13,3818,0,3212,1913,0]Al[3817,13,3818,0,3048,1913,0]Al[3817,13,3818,0,4365,1913,0]Al[3817,13,3818,0,1952,1913,0]Al[3817,13,3818,0,3988,1913,0]Al[3817,13,3818,0,4382,1913,0]Al[3817,13,3818,0,4661,1913,0]Al[3817,13,3818,0,4676,1913,0]Al[3817,13,3818,0,3862,1913,0]Al[3817,13,3818,0,4244,1913,0]Al[3817,13,3818,0,4853,1913,0]Al[3817,13,3818,0,4571,1913,0]Al[3817,13,3588,0,3020,1913,0]Al[3817,13,3588,0,3857,1913,0]Al[3817,13,3588,0,4149,1913,0]Al[3817,13,3588,0,4629,1913,0]Ao[4595,13,4596,4392,4122,1913,0]Ao[4595,13,4596,4170,4972,1913,0]Ao[4595,13,4596,1895,1553,1913,0]Ao[4595,13,4596,1476,1537,1913,0]Ao[4595,13,4596,1476,3226,1913,0]Ao[4595,13,4596,1476,3202,1913,0]Ao[4595,13,4596,1491,2361,1913,0]Ao[4595,13,4596,1491,3213,1913,0]Ao[4595,13,4596,1491,3049,1913,0]Ao[4595,13,4596,1491,4366,1913,0]Ao[4595,13,4596,1044,1953,1913,0]Ao[4595,13,4596,2086,4268,1913,0]Ao[4595,13,4596,2086,3992,1913,0]Ao[4595,13,4596,3189,4383,1913,0]Ao[4595,13,4596,3189,4662,1913,0]Ao[4595,13,4596,3189,4677,1913,0]Ao[4595,13,4596,3189,3863,1913,0]Ao[4595,13,4596,1783,4245,1913,0]Ao[4595,13,4596,1783,4854,1913,0]Ao[4595,13,4596,4170,4977,1913,0]Ao[4595,13,4596,4726,4572,1913,0]An[4595,13,3589,129,3021,1913,0]Ao[4595,13,3589,1461,3858,1913,0]Ao[4595,13,3589,2419,4150,1913,0]Ao[4595,13,3589,3589,4630,1913,0]Ao[4307,13,4308,4308,2346,1913,0]Ao[1015,13,1492,1492,1317,1913,0]Ao[1015,13,1492,1492,4370,1913,0]Al[4595,13,4596,1873,4129,0,0]Al[4595,13,4596,1846,4152,0,0]Al[4595,13,4596,1846,4374,0,0]Al[4595,14,4596,1846,4882,0,0]Ai[4595,14,4635,0,4697,0,0]Ah[4595,14,4373,0,456,0,0]Ai[4595,14,4373,0,1311,0,0]Ai[4595,14,4373,0,1313,0,0]Ai[4595,14,4373,0,4575,0,0]Ai[3817,12,1785,0,3041,0,0]Ai[3817,12,1785,0,3034,0,0]Al[4595,12,1786,1786,3042,0,0]Al[4595,12,1786,1786,3035,0,0]Ae[4595,2,4596,0,0,0,0]Al[3817,13,3818,3952,3230,0,0]Al[3817,13,3818,3952,4738,0,0]Ai[3817,13,3588,0,3208,0,0]Ak[3817,13,3588,0,107,3208,0]Al[3817,13,3588,0,1284,3208,0]Al[3817,13,3588,0,1927,3208,0]Al[4595,13,3589,3589,3209,0,0]Am[4595,13,3589,129,108,3209,0]Ao[4595,13,3589,1461,1285,3209,0]Ao[4595,13,3589,2419,1928,3209,0]Ai[3817,13,3818,0,4382,0,0]Al[4595,13,4596,3189,4383,0,0]9854Al[3817,13,3818,3593,3206,0,0]Al[3817,13,3818,1014,2340,0,0]Al[4595,14,4596,1846,4882,0,0]Ai[4595,14,4635,0,4697,0,0]Ah[4595,14,4373,0,456,0,0]Ai[4595,14,4373,0,1311,0,0]Ai[4595,14,4373,0,4575,0,0]Al[3817,13,3818,3957,3236,0,0]Al[4595,13,4596,2086,3237,0,0]Ak[1320,14,1321,1321,121,0,0]Al[1320,14,1490,1490,1316,0,0]Al[4595,13,4596,3594,3207,0,0]Ai[3817,12,1785,0,3041,0,0]Al[4595,12,1786,1786,3042,0,0]Am[1320,13,1321,1321,121,105,0]An[1320,13,1321,1321,3849,105,0]An[1320,13,1490,1490,3880,105,0]An[1320,13,1490,1490,1316,105,0]An[1320,13,1490,1490,4269,105,0]Ak[476,13,477,477,3850,105,0]An[3886,13,3887,3887,4883,105,0]An[3817,13,3818,3593,4387,105,0]An[3817,13,3818,3593,2339,105,0]Ak[3817,13,3818,0,4121,105,0]An[3817,13,3818,1014,2340,105,0]An[3817,13,3818,3593,3204,105,0]An[3817,13,3818,3593,3206,105,0]An[3817,13,3818,3593,1930,105,0]An[3817,13,3818,3593,1529,105,0]An[3817,13,3818,3593,3013,105,0]An[3817,13,3818,3593,2313,105,0]An[3817,13,3818,3593,4021,105,0]An[3817,13,3818,3593,4026,105,0]An[3817,13,3818,3593,4799,105,0]An[3817,13,3818,3593,4637,105,0]An[3817,13,3818,2370,2325,105,0]An[3817,13,3818,2370,4130,105,0]An[3817,13,3818,2370,1508,105,0]An[3817,13,3818,2397,2329,105,0]An[3817,13,3818,2397,3233,105,0]An[3817,13,3818,2397,4249,105,0]Am[3817,13,3818,1014,459,105,0]An[3817,13,3818,3952,3230,105,0]An[3817,13,3818,3952,4738,105,0]An[3817,13,3818,3952,4267,105,0]An[3817,13,3818,3952,3051,105,0]An[3817,13,3818,3952,3232,105,0]An[3817,13,3818,3952,4266,105,0]An[3817,13,3818,1502,4036,105,0]An[3817,13,3818,1056,3875,105,0]An[3817,13,3818,3957,3236,105,0]An[3817,13,3818,3957,3249,105,0]An[3817,13,3818,3957,4035,105,0]Ak[3817,13,3818,0,3227,105,0]Ak[3817,13,3818,0,4161,105,0]Ak[3817,13,3818,0,4379,105,0]Ak[3817,13,3818,0,4013,105,0]Ak[3817,13,3818,0,4257,105,0]Ak[3817,13,3818,0,1552,105,0]Ak[3817,13,3818,0,4796,105,0]Ak[3817,13,3818,0,1280,105,0]Ak[3817,13,3818,0,1536,105,0]Ak[3817,13,3818,0,2360,105,0]Ak[3817,13,3818,0,3212,105,0]Ak[3817,13,3818,0,3048,105,0]Ak[3817,13,3818,0,4365,105,0]Ak[3817,13,3818,0,1952,105,0]Ak[3817,13,3818,0,3988,105,0]Ak[3817,13,3818,0,4361,105,0]Ak[3817,13,3818,0,4375,105,0]Ak[3817,13,3818,0,4382,105,0]Ak[3817,13,3818,0,4661,105,0]Ak[3817,13,3818,0,4676,105,0]Ak[3817,13,3818,0,3862,105,0]Ak[3817,13,3818,0,4244,105,0]Ak[3817,13,3818,0,4029,105,0]Ak[3817,13,3818,0,4853,105,0]Ak[3817,13,3818,0,3982,105,0]Ak[3817,13,3818,0,3852,105,0]Ak[3817,13,3818,0,3031,105,0]Ak[3817,13,3818,0,4358,105,0]Ak[3817,13,3818,0,4540,105,0]Ak[3817,13,3818,0,3994,105,0]Ak[3817,13,3818,0,3997,105,0]Ak[3817,13,3818,0,4001,105,0]Ak[3817,13,3818,0,4695,105,0]Ak[3817,13,3818,0,4571,105,0]Aj[3817,13,3818,0,466,105,0]Aj[3817,13,3818,0,113,105,0]Ak[3817,13,3818,0,2348,105,0]Ak[3817,13,3818,0,2357,105,0]Aj[3817,13,3588,0,107,105,0]Ak[3817,13,3588,0,3020,105,0]Ak[3817,13,3588,0,1284,105,0]Ak[3817,13,3588,0,3857,105,0]Ak[3817,13,3588,0,1927,105,0]Ak[3817,13,3588,0,4149,105,0]Ak[3817,13,3588,0,4629,105,0]Ak[3817,13,1785,0,1922,105,0]Aj[3817,13,1785,0,454,105,0]Ak[3817,13,1785,0,4943,105,0]Ak[3817,13,1785,0,3855,105,0]Ak[3817,13,1785,0,4699,105,0]Ak[3817,13,1862,0,3244,105,0]Ak[3817,13,1862,0,4132,105,0]Ak[3817,13,1862,0,4567,105,0]Ak[3817,13,1862,0,4569,105,0]Ak[3817,13,1862,0,4792,105,0]Ak[3817,13,1862,0,1297,105,0]An[4595,13,4596,4392,4122,105,0]An[4595,13,4596,3076,3026,105,0]An[4595,13,4596,1039,1516,105,0]An[4595,13,4596,2086,1947,105,0]An[4595,13,4596,2086,3231,105,0]An[4595,13,4596,2086,3237,105,0]An[4595,13,4596,3594,3207,105,0]An[4595,13,4596,1035,3228,105,0]An[4595,13,4596,1783,4162,105,0]An[4595,13,4596,2371,2326,105,0]An[4595,13,4596,2371,4128,105,0]An[4595,13,4596,1846,5002,105,0]An[4595,13,4596,4170,4972,105,0]An[4595,13,4596,1501,4380,105,0]An[4595,13,4596,1501,4014,105,0]An[4595,13,4596,1501,4258,105,0]An[4595,13,4596,1895,1553,105,0]An[4595,13,4596,1895,4797,105,0]An[4595,13,4596,3067,1281,105,0]An[4595,13,4596,3076,4638,105,0]An[4595,13,4596,1476,1537,105,0]An[4595,13,4596,1476,4025,105,0]An[4595,13,4596,1476,3226,105,0]An[4595,13,4596,1476,3047,105,0]An[4595,13,4596,1476,4692,105,0]An[4595,13,4596,1476,3202,105,0]An[4595,13,4596,1491,2361,105,0]An[4595,13,4596,1491,3213,105,0]An[4595,13,4596,1491,3049,105,0]An[4595,13,4596,1491,4366,105,0]An[4595,13,4596,1039,3052,105,0]An[4595,13,4596,1039,4157,105,0]An[4595,13,4596,1044,1953,105,0]An[4595,13,4596,1044,3250,105,0]An[4595,13,4596,1044,3861,105,0]An[4595,13,4596,2086,3879,105,0]An[4595,13,4596,2086,4268,105,0]An[4595,13,4596,2086,3992,105,0]An[4595,13,4596,3594,4362,105,0]An[4595,13,4596,3594,3044,105,0]An[4595,13,4596,3594,4235,105,0]An[4595,13,4596,3594,1302,105,0]An[4595,13,4596,1035,4376,105,0]An[4595,13,4596,3189,4383,105,0]An[4595,13,4596,3189,4662,105,0]An[4595,13,4596,3189,4677,105,0]An[4595,13,4596,3189,3863,105,0]An[4595,13,4596,1783,4245,105,0]An[4595,13,4596,1783,4031,105,0]An[4595,13,4596,1783,4854,105,0]An[4595,13,4596,1783,3984,105,0]An[4595,13,4596,1783,3853,105,0]An[4595,13,4596,1466,3032,105,0]An[4595,13,4596,1466,4360,105,0]An[4595,13,4596,1466,4542,105,0]An[4595,13,4596,1466,2319,105,0]An[4595,13,4596,2371,4546,105,0]An[4595,13,4596,2371,2330,105,0]An[4595,13,4596,2371,3995,105,0]An[4595,13,4596,2371,3847,105,0]An[4595,13,4596,2371,3234,105,0]An[4595,13,4596,1846,4152,105,0]An[4595,13,4596,1846,4374,105,0]An[4595,13,4596,1846,4882,105,0]An[4595,13,4596,1846,2332,105,0]An[4595,13,4596,1846,4636,105,0]An[4595,13,4596,1873,4364,105,0]An[4595,13,4596,1873,4129,105,0]An[4595,13,4596,1873,3998,105,0]An[4595,13,4596,1873,4002,105,0]Ak[4595,13,4596,0,4272,105,0]Ak[4595,13,4596,0,4696,105,0]An[4595,13,4596,4170,3216,105,0]An[4595,13,4596,4170,4977,105,0]An[4595,13,4596,4726,4572,105,0]Am[4595,13,4596,4596,467,105,0]Am[4595,13,4596,4596,114,105,0]An[4595,13,4596,4596,2349,105,0]An[4595,13,4596,4596,2358,105,0]An[4595,13,4596,4596,3030,105,0]An[4595,13,1781,1781,4163,105,0]An[4595,13,4838,4838,4737,105,0]An[4595,13,4838,4838,1299,105,0]An[4595,13,1786,1786,1923,105,0]Am[4595,13,1786,1786,455,105,0]An[4595,13,1786,1786,4944,105,0]An[4595,13,1786,1786,3856,105,0]An[4595,13,1786,1786,4702,105,0]An[4595,13,1863,1863,3245,105,0]An[4595,13,1863,1863,4133,105,0]An[4595,13,1863,1863,4568,105,0]An[4595,13,1863,1863,4570,105,0]An[4595,13,1863,1863,4793,105,0]Ak[4595,13,1863,0,1298,105,0]Al[4595,13,3589,129,108,105,0]Am[4595,13,3589,129,3021,105,0]An[4595,13,3589,1461,1285,105,0]An[4595,13,3589,1461,3858,105,0]An[4595,13,3589,2419,1928,105,0]An[4595,13,3589,2419,4150,105,0]An[4595,13,3589,3589,4630,105,0]An[4595,13,2057,2057,1300,105,0]An[4595,13,2057,2057,1303,105,0]An[4307,13,4308,4308,3248,105,0]Am[4307,13,4308,375,4273,105,0]An[4307,13,4308,4308,2346,105,0]An[4307,13,4308,4308,2359,105,0]An[4307,13,4308,4308,4003,105,0]An[1015,13,1016,1016,3023,105,0]An[1015,13,1016,1016,4730,105,0]Am[1015,13,1775,1775,122,105,0]An[1015,13,1492,1492,1317,105,0]An[1015,13,1492,1492,3881,105,0]An[1015,13,1492,1492,4370,105,0]An[1015,13,4088,4088,4929,105,0]An[1015,13,4088,4088,4930,105,0]Am[1015,13,1868,1868,123,105,0]Am[3837,13,3838,3838,112,105,0]An[3837,13,3838,3838,2316,105,0]Al[3817,13,3818,3593,4799,0,0]Ai[3817,13,3818,0,3982,0,0]Al[4595,13,4596,1783,3984,0,0]Al[4595,14,4596,1846,4882,0,0]Ai[4595,14,4635,0,4697,0,0]Ah[4595,14,4373,0,456,0,0]Ai[4595,14,4373,0,1311,0,0]Ah[3817,5,3818,3593,0,0,0]Al[1320,15,1321,1321,3849,0,0]Ae[3837,6,3838,0,0,0,0]Ai[3817,15,3818,0,3031,0,0]Al[4595,15,4596,1466,3032,0,0]Ah[4595,5,4596,1466,0,0,0]Al[3817,15,3818,3593,4637,0,0]Al[4595,15,4596,3076,4638,0,0]Ah[3817,5,3818,2370,0,0,0]Ah[4595,5,4596,2371,0,0,0]Al[4595,15,4596,2086,3879,0,0]Ah[3817,5,3818,2397,0,0,0]2Ah[4595,6,4596,1846,0,0,0]Ai[3817,19,3818,0,1552,0,0]Al[4595,19,4596,1895,1553,0,0]Ai[3817,19,3818,0,4695,0,0]Ai[4595,19,4596,0,4696,0,0]=Ai[3817,12,1785,0,4030,0,0]>Al[3817,13,1862,0,4569,4030,0]>Al[4595,12,1786,1786,4032,0,0]>Ao[4595,13,1863,1863,4570,4032,0]Ao[1015,13,4088,4088,4930,4032,0]An[3837,13,3838,3838,112,3864,0]Ao[3837,13,3838,3838,2316,3864,0]Ai[3817,14,3818,0,3048,0,0]Al[4595,14,4596,1491,3049,0,0]Ai[3817,13,3818,0,4029,0,0]Al[4595,13,4596,1783,4031,0,0]Al[3817,13,3818,2078,4232,0,0]0>=Al[1015,13,1492,1492,4370,0,0]Ae[1015,2,1016,0,0,0,0]Al[3817,13,3818,2397,2329,0,0]Al[4595,13,4596,2371,2330,0,0]Ai[3817,14,3818,0,3212,0,0]Al[4595,14,4596,1491,3213,0,0]Al[4595,13,4596,3594,3207,0,0]Ai[3817,13,3818,0,4382,0,0]Al[4595,13,4596,3189,4383,0,0]43Ai[3817,14,3818,0,2357,0,0]Al[4595,14,4596,4596,2358,0,0]Al[4307,14,4308,4308,2359,0,0]Al[4595,13,4596,2086,1947,0,0]Ai[3817,14,3818,0,3048,0,0]Al[4595,14,4596,1491,3049,0,0]Al[4595,13,4596,4170,4972,0,0]Al[4595,13,4596,4170,4977,0,0]Al[3817,13,3818,3957,3236,0,0]Al[4595,13,4596,2086,3237,0,0]>=Al[1320,14,1490,1490,1316,0,0]Ai[476,14,477,477,3850,0,0]Ai[3817,13,1862,0,4567,0,0]Ai[3817,13,1862,0,4569,0,0]Al[4595,13,1863,1863,4568,0,0]Al[4595,13,1863,1863,4570,0,0]76Ai[3817,15,1862,0,1297,0,0]Ai[4595,15,1863,0,1298,0,0]Ah[3817,5,3818,4392,0,0,0]Ah[4595,5,4596,4392,0,0,0]Ai[3817,15,3818,0,4361,0,0]Ai[3817,15,3818,0,3994,0,0]Al[4595,15,4596,3594,4362,0,0]Al[4595,15,4596,2371,3995,0,0]Ah[4595,6,4596,2371,0,0,0]Ah[4595,5,4596,1873,0,0,0]Ah[3817,5,3818,2370,0,0,0]Al[3817,15,3818,3952,4266,0,0]Ah[3817,5,1862,1863,0,0,0]Ae[4595,5,1863,0,0,0,0]Ai[3817,15,3818,0,3031,0,0]Al[4595,15,4596,1466,3032,0,0]Ai[3817,19,3818,0,4695,0,0]Ai[4595,19,4596,0,4696,0,0]Al[4595,15,4596,1846,4636,0,0]>Al[4595,15,4596,1476,3047,0,0]=5454Al[4595,15,4596,2371,4128,0,0]Ah[3817,5,3588,2419,0,0,0]Ah[4595,5,3589,2419,0,0,0]Ah[4595,5,4596,1846,0,0,0]98Ah[4595,6,4596,1039,0,0,0]:9Ah[3817,5,3818,1783,0,0,0]Ah[4595,5,4596,1783,0,0,0]Ae[4595,5,1781,0,0,0,0]Al[4595,13,4596,3594,3207,0,0]Ai[3817,14,3818,0,3212,0,0]Al[4595,14,4596,1491,3213,0,0]Al[1015,14,1016,1016,3023,0,0]Ae[4595,2,4596,0,0,0,0]Ai[3817,13,3818,0,4244,0,0]Al[4595,13,4596,1783,4245,0,0]Al[3817,12,3818,2078,4232,0,0]Ab[4173,3,0,0,0,0,0]Ak[1320,14,1321,1321,121,0,0]Al[1320,14,1490,1490,3880,0,0]98Al[3817,13,3818,2397,3233,0,0]Al[4595,13,4596,2371,3234,0,0]Ai[3817,13,3818,0,4661,0,0]Al[4595,13,4596,3189,4662,0,0];=Al[3817,13,3818,3952,4738,0,0]Al[4595,13,4596,2086,3231,0,0]Al[3817,13,3818,3593,3204,0,0]Al[3817,13,3818,3593,3206,0,0]Al[3817,13,3818,2397,2329,0,0]Ak[3817,13,3818,1014,459,0,0]Al[4595,13,4596,2371,2330,0,0]<;Ai[3817,13,3818,0,1552,0,0]Ai[3817,13,3818,0,1536,0,0]Al[4595,13,4596,1895,1553,0,0]Al[4595,13,4596,1476,1537,0,0]20Ae[1320,7,1490,0,0,0,0]?>Al[1320,14,1490,1490,1316,0,0]Ao[1015,13,1016,1016,4730,3056,0]Ai[3817,15,3818,0,3031,0,0]Ah[3817,5,3818,1466,0,0,0]Al[4595,15,4596,1466,3032,0,0]Ah[4595,5,4596,1466,0,0,0]Ah[3817,6,3818,3594,0,0,0]Ah[4595,6,4596,3594,0,0,0]Al[4595,15,4596,2371,4128,0,0]Ah[4595,5,4596,1873,0,0,0]Ah[3817,5,3818,1491,0,0,0]Ah[4595,5,4596,1491,0,0,0]97Ae[1015,5,1492,0,0,0,0]:8Ah[4595,6,4596,1846,0,0,0]Ah[3817,5,3818,1035,0,0,0]Ah[4595,5,4596,1035,0,0,0]Ah[3817,5,3818,1501,0,0,0]Ah[4595,5,4596,1501,0,0,0]Ah[3817,5,3818,3189,0,0,0]Ah[4595,5,4596,3189,0,0,0]Ai[3817,15,3818,0,4361,0,0]Al[4595,15,4596,3594,4362,0,0]Ah[3817,5,3818,3593,0,0,0]Al[3817,15,3818,3952,3051,0,0]Al[3817,15,3818,3952,3232,0,0]Al[3817,15,3818,3957,3249,0,0]Al[3817,15,3818,3957,4035,0,0]An[1320,13,1321,1321,121,4127,0]Ao[1320,13,1321,1321,3849,4127,0]Ao[3817,13,3818,3593,3206,4127,0]Ao[3817,13,3818,3593,4799,4127,0]Ao[3817,13,3818,3593,4637,4127,0]Ao[3817,13,3818,2370,2325,4127,0]Ao[3817,13,3818,2370,4130,4127,0]Ao[3817,13,3818,3952,4267,4127,0]Ao[3817,13,3818,3952,3051,4127,0]Ao[3817,13,3818,3952,3232,4127,0]Ao[3817,13,3818,3952,4266,4127,0]Ao[3817,13,3818,3957,3249,4127,0]Ao[3817,13,3818,3957,4035,4127,0]Al[3817,13,3818,0,1552,4127,0]Al[3817,13,3818,0,4796,4127,0]Al[3817,13,3818,0,1536,4127,0]Al[3817,13,3818,0,2360,4127,0]Al[3817,13,3818,0,3212,4127,0]Al[3817,13,3818,0,3048,4127,0]Al[3817,13,3818,0,4365,4127,0]Al[3817,13,3818,0,1952,4127,0]Al[3817,13,3818,0,3988,4127,0]Al[3817,13,3818,0,4361,4127,0]Al[3817,13,3818,0,4382,4127,0]Al[3817,13,3818,0,4661,4127,0]Al[3817,13,3818,0,4676,4127,0]Al[3817,13,3818,0,3862,4127,0]Al[3817,13,3818,0,4244,4127,0]Al[3817,13,3818,0,4029,4127,0]Al[3817,13,3818,0,4853,4127,0]Al[3817,13,3818,0,3982,4127,0]Al[3817,13,3818,0,3852,4127,0]Al[3817,13,3818,0,3031,4127,0]Al[3817,13,3818,0,4358,4127,0]Al[3817,13,3818,0,4540,4127,0]Al[3817,13,3818,0,3994,4127,0]Al[3817,13,3818,0,3997,4127,0]Al[3817,13,3818,0,4001,4127,0]Al[3817,13,3818,0,4695,4127,0]Ak[3817,13,3818,0,466,4127,0]Ak[3817,13,3818,0,113,4127,0]Al[3817,13,3818,0,2348,4127,0]Ak[3817,13,3588,0,107,4127,0]Al[3817,13,3588,0,3020,4127,0]Al[3817,13,3588,0,1284,4127,0]Al[3817,13,3588,0,3857,4127,0]Al[3817,13,3588,0,1927,4127,0]Al[3817,13,3588,0,4149,4127,0]Al[3817,13,1862,0,4792,4127,0]Al[3817,13,1862,0,1297,4127,0]Ao[4595,13,4596,1895,1553,4127,0]Ao[4595,13,4596,1895,4797,4127,0]Ao[4595,13,4596,3076,4638,4127,0]Ao[4595,13,4596,1476,1537,4127,0]Ao[4595,13,4596,1476,4025,4127,0]Ao[4595,13,4596,1476,3226,4127,0]Ao[4595,13,4596,1476,3047,4127,0]Ao[4595,13,4596,1476,4692,4127,0]Ao[4595,13,4596,1476,3202,4127,0]Ao[4595,13,4596,1491,2361,4127,0]Ao[4595,13,4596,1491,3213,4127,0]Ao[4595,13,4596,1491,3049,4127,0]Ao[4595,13,4596,1491,4366,4127,0]Ao[4595,13,4596,1039,3052,4127,0]Ao[4595,13,4596,1039,4157,4127,0]Ao[4595,13,4596,1044,1953,4127,0]Ao[4595,13,4596,1044,3250,4127,0]Ao[4595,13,4596,1044,3861,4127,0]Ao[4595,13,4596,2086,4268,4127,0]Ao[4595,13,4596,2086,3992,4127,0]Ao[4595,13,4596,3594,4362,4127,0]Ao[4595,13,4596,3594,3044,4127,0]Ao[4595,13,4596,3594,4235,4127,0]Ao[4595,13,4596,3189,4383,4127,0]Ao[4595,13,4596,3189,4662,4127,0]Ao[4595,13,4596,3189,4677,4127,0]Ao[4595,13,4596,3189,3863,4127,0]Ao[4595,13,4596,1783,4245,4127,0]Ao[4595,13,4596,1783,4031,4127,0]Ao[4595,13,4596,1783,4854,4127,0]Ao[4595,13,4596,1783,3984,4127,0]Ao[4595,13,4596,1783,3853,4127,0]Ao[4595,13,4596,1466,3032,4127,0]Ao[4595,13,4596,1466,4360,4127,0]Ao[4595,13,4596,1466,4542,4127,0]Ao[4595,13,4596,1466,2319,4127,0]Ao[4595,13,4596,2371,4546,4127,0]Ao[4595,13,4596,2371,3995,4127,0]Ao[4595,13,4596,1846,4374,4127,0]Ao[4595,13,4596,1846,4882,4127,0]Ao[4595,13,4596,1846,2332,4127,0]Ao[4595,13,4596,1846,4636,4127,0]Ao[4595,13,4596,1873,3998,4127,0]Ao[4595,13,4596,1873,4002,4127,0]Al[4595,13,4596,0,4272,4127,0]Al[4595,13,4596,0,4696,4127,0]Ao[4595,13,4596,4170,3216,4127,0]Ao[4595,13,4596,4170,4977,4127,0]An[4595,13,4596,4596,467,4127,0]An[4595,13,4596,4596,114,4127,0]Ao[4595,13,4596,4596,2349,4127,0]Ao[4595,13,4596,4596,3030,4127,0]Ao[4595,13,1781,1781,4163,4127,0]Ao[4595,13,4838,4838,4737,4127,0]Ao[4595,13,4838,4838,1299,4127,0]Ao[4595,13,1863,1863,4793,4127,0]Al[4595,13,1863,0,1298,4127,0]Am[4595,13,3589,129,108,4127,0]An[4595,13,3589,129,3021,4127,0]Ao[4595,13,3589,1461,1285,4127,0]Ao[4595,13,3589,1461,3858,4127,0]Ao[4595,13,3589,2419,1928,4127,0]Ao[4595,13,3589,2419,4150,4127,0]Ao[4595,13,2057,2057,1303,4127,0]Ao[4307,13,4308,4308,2346,4127,0]Ao[4307,13,4308,4308,2359,4127,0]Ao[4307,13,4308,4308,4003,4127,0]Ao[1015,13,1016,1016,4730,4127,0]Ao[1015,13,1492,1492,4370,4127,0]Ah[3817,7,3818,2078,0,0,0]Ai[3817,13,3818,0,4244,0,0]Al[4595,13,4596,1783,4245,0,0]Ai[3817,12,1785,0,3041,0,0]Al[4595,12,1786,1786,3042,0,0]Al[4307,14,4308,4308,2346,0,0]Ai[3817,14,3818,0,3212,0,0]Al[4595,14,4596,1491,3213,0,0]10Ae[1015,7,1016,0,0,0,0]Al[3817,13,3818,2370,2325,0,0]Ae[4173,7,4174,0,0,0,0]Ae[1320,7,1321,0,0,0,0]Al[4595,13,4596,3594,3207,0,0]Al[3817,13,3818,3593,3206,0,0]1Al[4595,13,4596,2086,4268,0,0]Al[1320,13,1490,1490,1316,0,0]Al[1320,14,1490,1490,3880,0,0]Al[3817,12,3818,2078,1939,0,0]Ao[3817,13,3818,1502,4036,1939,0]Ao[3817,13,3818,1056,3875,1939,0]Ai[3817,12,1785,0,1940,0,0]Al[3817,13,1862,0,3244,1940,0]Al[4595,12,1786,1786,1941,0,0]Ao[4595,13,1863,1863,3245,1941,0]Ai[3817,15,3818,0,3031,0,0]Ah[3817,5,3818,1466,0,0,0]Al[4595,15,4596,1466,3032,0,0]Ah[4595,5,4596,1466,0,0,0]Ai[3817,15,3818,0,4361,0,0]Al[4595,15,4596,3594,4362,0,0]Ah[4595,5,4596,2371,0,0,0]Al[4595,15,4596,3594,1302,0,0]Ai[3817,15,3818,0,3994,0,0]Al[4595,15,4596,2371,3995,0,0]Ai[3817,19,3818,0,4695,0,0]Ai[4595,19,4596,0,4696,0,0]Ai[3817,15,1862,0,1297,0,0]Ai[4595,15,1863,0,1298,0,0]32Al[4595,15,4596,3594,3044,0,0]><><><21Ah[3817,5,1862,1863,0,0,0]Ae[4595,5,1863,0,0,0,0]10Ah[3817,5,3818,4726,0,0,0]Ah[4595,5,4596,4726,0,0,0]Al[4595,15,2057,2057,1300,0,0]5Al[4595,15,4596,1846,4374,0,0]Ai[3817,12,3818,0,2353,0,0]Al[3817,13,3818,0,4121,2353,0]Al[3817,13,3818,0,4013,2353,0]Al[4595,12,4596,1501,2354,0,0]Ao[4595,13,4596,4392,4122,2354,0]Ao[4595,13,4596,1501,4014,2354,0]Ae[3886,7,3887,0,0,0,0]Al[3817,13,3818,3593,3206,0,0]Al[4595,13,4596,3594,3207,0,0]Ai[3817,14,3818,0,3212,0,0]Al[4595,14,4596,1491,3213,0,0]32An[1320,13,1321,1321,121,4246,0]Ao[1015,13,1016,1016,3023,4246,0]An[1015,13,1775,1775,122,4246,0]An[1015,13,1868,1868,123,4246,0]An[3837,13,3838,3838,112,4246,0]Ao[3837,13,3838,3838,2316,4246,0]Ab[4595,3,0,0,0,0,0]Ak[4307,13,4308,375,4273,0,0]Ai[3817,13,3818,0,4382,0,0]Al[4595,13,4596,3189,4383,0,0];:Al[3817,13,3818,2370,2325,0,0]Al[4595,13,4596,2371,2326,0,0]?>32Ae[4173,7,4174,0,0,0,0]5Al[1015,14,1016,1016,3023,0,0]60Ae[1320,7,1321,0,0,0,0]Ai[3817,13,3818,0,4029,0,0]Ai[3817,13,3818,0,3982,0,0]Al[4595,13,4596,1783,4031,0,0]Al[4595,13,4596,1783,3984,0,0]Ai[3817,14,3818,0,4029,0,0]Al[4595,14,4596,1783,4031,0,0]53Al[3817,13,3818,3957,3236,0,0]Ai[3817,19,3818,0,4695,0,0]Ai[4595,19,4596,0,4696,0,0]Ai[3817,15,3818,0,4361,0,0]Al[4595,15,4596,3594,4362,0,0]Ai[3817,10,3588,3589,0,0,0]Af[4595,10,3589,0,0,0,0]Ah[3817,5,3588,3589,0,0,0]Ae[4595,5,3589,0,0,0,0]Ai[3817,10,1785,1786,0,0,0]Af[4595,10,1786,0,0,0,0]Ai[3817,15,3818,0,3994,0,0]Al[4595,15,4596,2371,3995,0,0]Ah[4595,6,4596,1846,0,0,0]Ah[3817,6,3818,3593,0,0,0]Ah[4595,6,4596,3076,0,0,0]Ai[3817,15,3818,0,3031,0,0]Al[4595,15,4596,1466,3032,0,0]10Al[3817,15,3818,3593,4637,0,0]Al[4595,15,4596,3076,4638,0,0]Ai[3817,14,3818,0,3048,0,0]Al[4595,14,4596,1491,3049,0,0]Ai[3817,14,3588,0,4629,0,0]Al[4595,14,3589,3589,4630,0,0]Al[3817,13,3818,3593,3206,0,0]Ai[3817,14,3818,0,3212,0,0]Al[4595,14,4596,1491,3213,0,0]10Al[4595,13,4596,3076,3026,0,0]Al[3817,13,3818,2370,2325,0,0]Al[4595,13,4596,2371,2326,0,0]Ab[4656,3,0,0,0,0,0]Ao[1015,13,1016,1016,4730,3056,0]Ai[3817,19,3818,0,4695,0,0]Ai[4595,19,4596,0,4696,0,0]Ah[3817,5,3818,3189,0,0,0]Ah[4595,5,4596,3189,0,0,0]Ai[3817,15,1862,0,1297,0,0]Ai[4595,15,1863,0,1298,0,0]54Ai[4595,10,4596,1846,0,0,0]Al[4595,15,4596,3594,1302,0,0]7600Al[4595,15,4596,1039,4157,0,0]4365Al[4595,15,2057,2057,1300,0,0]Ae[1015,9,1016,0,0,0,0]:9Ai[3817,15,3818,0,3031,0,0]Al[4595,15,4596,1466,3032,0,0]<;<;Ah[4595,6,4596,1476,0,0,0]21Ae[3817,5,3818,0,0,0,0]Ae[4595,5,4596,0,0,0,0]Ai[3817,15,3818,0,3994,0,0]Ah[3817,5,1785,1786,0,0,0]Al[4595,15,4596,2371,3995,0,0]Al[4595,15,4596,1846,4636,0,0]Ae[4595,5,1786,0,0,0,0]An[1320,13,1321,1321,121,4236,0]Ao[1015,13,1016,1016,3023,4236,0]An[1015,13,1775,1775,122,4236,0]An[1015,13,1868,1868,123,4236,0]An[3837,13,3838,3838,112,4236,0]Ai[3817,13,3818,0,4382,0,0]Al[4595,13,4596,3189,4383,0,0]Ai[3817,13,3818,0,1552,0,0]Al[4595,13,4596,1895,1553,0,0]Al[3817,14,3818,2370,4130,0,0]Ai[3817,13,3818,0,4244,0,0]Al[4595,13,4596,1783,4245,0,0]Ai[3817,13,3818,0,4121,0,0]Al[4595,13,4596,4392,4122,0,0]Ai[3817,14,3818,0,3212,0,0]Al[4595,14,4596,1491,3213,0,0]6Al[4595,13,4596,3594,3207,0,0]Al[3817,13,3818,3593,3206,0,0]0176Ao[3886,13,3887,3887,4883,4690,0]Ai[3817,13,3818,0,4375,0,0]Al[4595,13,4596,1035,4376,0,0]Ae[1015,6,1016,0,0,0,0]Ai[3817,15,3818,0,3031,0,0]Al[4595,15,4596,1466,3032,0,0]10Al[4595,15,4596,3594,3044,0,0]Ae[4595,5,4838,0,0,0,0]Ah[3817,5,3818,3952,0,0,0]Ai[3817,15,1862,0,1297,0,0]Ai[4595,15,1863,0,1298,0,0]Al[4595,15,2057,2057,1300,0,0]Al[3817,14,3818,2370,4130,0,0]Ak[3817,13,3818,0,466,3014,0]Ak[3817,13,3818,0,113,3014,0]Al[3817,13,3818,0,2348,3014,0]An[4595,13,4596,4596,467,3014,0]An[4595,13,4596,4596,114,3014,0]Ao[4595,13,4596,4596,2349,3014,0]Ao[3837,13,3838,3838,2316,3240,0]Ai[3817,13,3818,0,4676,0,0]Al[4595,13,4596,3189,4677,0,0]Ai[3817,12,1785,0,1940,0,0]Al[3817,13,1862,0,3244,1940,0]Al[4595,12,1786,1786,1941,0,0]Ao[4595,13,1863,1863,3245,1941,0]Al[3817,13,3818,3593,3206,0,0]Ai[3817,13,3818,0,4382,0,0]Al[4595,13,4596,3189,4383,0,0]Ai[3817,14,3818,0,3048,0,0]Al[4595,14,4596,1491,3049,0,0]Ai[3817,12,3588,0,3208,0,0]Ak[3817,13,3588,0,107,3208,0]Al[3817,13,3588,0,1284,3208,0]Al[3817,13,3588,0,1927,3208,0]Al[4595,12,3589,3589,3209,0,0]Am[4595,13,3589,129,108,3209,0]Ao[4595,13,3589,1461,1285,3209,0]Ao[4595,13,3589,2419,1928,3209,0]Al[3817,14,3818,2370,4130,0,0]Al[4595,13,4596,2086,1947,0,0]Ah[4595,2,4168,3956,0,0,0]Ae[1320,7,1321,0,0,0,0]==Ak[3817,13,3818,1014,459,0,0]Ak[4307,13,4308,375,4273,0,0]032Ai[3817,12,1785,0,1940,0,0]Al[3817,13,1862,0,3244,1940,0]Ae[4595,2,4596,0,0,0,0]Al[4595,12,1786,1786,1941,0,0]Ao[4595,13,1863,1863,3245,1941,0]Ai[3817,10,3818,4170,0,0,0]Ai[4595,10,4596,4170,0,0,0]Ai[3817,15,1862,0,1297,0,0]Ai[4595,15,1863,0,1298,0,0]Ai[3817,15,3818,0,3994,0,0]Al[4595,15,4596,2371,3995,0,0]Ai[3817,19,3818,0,4695,0,0]Ai[4595,19,4596,0,4696,0,0]Ah[3817,5,3818,1783,0,0,0]Ah[4595,5,4596,1783,0,0,0]Ai[3817,12,1785,0,3041,0,0]Al[4595,12,1786,1786,3042,0,0]Al[4595,13,4596,2371,2326,0,0]Al[4595,13,4596,4596,3030,0,0]Al[4595,13,4596,3594,3207,0,0]Ai[3817,13,3588,0,4149,0,0]Al[4595,13,3589,2419,4150,0,0]Al[3817,13,3818,2078,3054,0,0]0Al[3817,13,3818,3593,3206,0,0]4Ai[3817,14,3818,0,3212,0,0]Al[4595,14,4596,1491,3213,0,0]Ai[3817,13,3818,0,4382,0,0]Al[4595,13,4596,3189,4383,0,0]Al[1015,14,1016,1016,3023,0,0]Ai[3817,19,3818,0,4695,0,0]Ai[4595,19,4596,0,4696,0,0]Al[4595,15,2057,2057,1300,0,0]Ai[3817,15,1862,0,1297,0,0]Ai[4595,15,1863,0,1298,0,0]10Al[4595,15,4596,1476,4692,0,0]Al[4595,15,4596,3594,3044,0,0]Ai[3817,15,3818,0,3031,0,0]Al[4595,15,4596,1466,3032,0,0]Ah[4595,5,4596,1846,0,0,0]Ae[3886,5,3887,0,0,0,0]Al[1015,13,1016,1016,3023,0,0]Al[3817,13,3818,3593,3206,0,0]Ai[3817,12,1785,0,1940,0,0]Al[3817,13,1862,0,3244,1940,0]Al[4595,12,1786,1786,1941,0,0]Ao[4595,13,1863,1863,3245,1941,0]Ah[4595,7,4772,4771,0,0,0]0Al[4595,13,4596,3594,3207,0,0]6Ak[3817,13,3818,1014,459,0,0]Ai[3817,14,3818,0,3212,0,0]Al[4595,14,4596,1491,3213,0,0]Ao[3886,13,3887,3887,4883,4690,0]0Ai[3817,13,3818,0,3982,0,1]Al[4595,13,4596,1783,3984,0,1]Ai[3817,13,3818,0,3982,0,0]Al[4595,13,4596,1783,3984,0,0]Ai[3817,15,1862,0,1297,0,0]Ai[4595,15,1863,0,1298,0,0]10Ai[3817,13,3818,0,4661,0,0]Al[4595,13,4596,3189,4662,0,0]Am[1320,13,1321,1321,121,442,0]An[1015,13,1016,1016,3023,442,0]Am[1015,13,1775,1775,122,442,0]Am[1015,13,1868,1868,123,442,0]Am[3837,13,3838,3838,112,442,0]Al[4595,13,4596,3594,3207,0,0]0Ak[3817,13,3818,1014,459,0,0]An[1320,13,1321,1321,121,4246,0]Ao[1015,13,1016,1016,3023,4246,0]An[1015,13,1775,1775,122,4246,0]An[1015,13,1868,1868,123,4246,0]An[3837,13,3838,3838,112,4246,0]Ao[3837,13,3838,3838,2316,4246,0]Ai[3817,13,3818,0,4029,0,0]Al[4595,13,4596,1783,4031,0,0]Ai[3817,19,3818,0,4695,0,0]Ai[4595,19,4596,0,4696,0,0]Ae[1015,5,4088,0,0,0,0]0Ai[3817,13,3818,0,4244,0,0]Al[4595,13,4596,1783,4245,0,0]Ai[3817,13,3818,0,3982,0,0]Al[4595,13,4596,1783,3984,0,0]Al[3817,13,3818,2370,2325,0,0]Al[3817,13,3818,2397,2329,0,0]Al[4595,13,4596,2371,2330,0,0]Al[4595,13,4596,3594,3207,0,0]Ao[3886,13,3887,3887,4883,4690,0];:Ah[3817,5,1785,1786,0,0,0]Ae[4595,5,1786,0,0,0,0]Ai[3817,15,1862,0,1297,0,0]Ai[4595,15,1863,0,1298,0,0]?>?>Al[3817,13,3818,3593,3206,0,0]6Ai[3817,13,3818,0,4382,0,0]Al[4595,13,4596,3189,4383,0,0]Ae[3886,7,3887,0,0,0,0]Ae[4173,7,4174,0,0,0,0]Ai[3817,14,3818,0,3212,0,0]Al[4595,14,4596,1491,3213,0,0]?=Ai[3817,19,3818,0,4695,0,0]Ai[4595,19,4596,0,4696,0,0]2=<==<>=>=>=<;<;Al[4595,14,4838,4838,4737,0,0]?>Ai[4595,18,4596,3189,0,0,0]") \ No newline at end of file diff --git a/pr/299/docs/search.index/function/a8b301585994.js b/pr/299/docs/search.index/function/a8b301585994.js new file mode 100644 index 000000000..f863ed905 --- /dev/null +++ b/pr/299/docs/search.index/function/a8b301585994.js @@ -0,0 +1 @@ +rd_("Bf[\"{{{AHEf{ANAb}}{AHEf{ANAb}}}CKh}\",[]]Bf[\"{{{AHEf{BDCj}}{AHEf{BDCj}}}CKh}\",[]]Bf[\"{{{AHEf{BAEf}}{AHEf{BAEf}}}CKh}\",[]]Bf[\"{{{AHEf{AGMf}}{AHEf{AGMf}}}CKh}\",[]]Bf[\"{{{AHEf{AID`}}{AHEf{AID`}}}CKh}\",[]]Bf[\"{{{AHEf{BAEd}}{AHEf{BAEd}}}CKh}\",[]]Bf[\"{{{AHEf{AIFb}}{AHEf{AIFb}}}CKh}\",[]]Bf[\"{{{AHEf{AOHf}}{AHEf{AOHf}}}CKh}\",[]]Bd[\"{{{AHEf{LB`}}{AHEf{LB`}}}CKh}\",[]]Bf[\"{{{AHEf{BEGh}}{AHEf{BEGh}}}CKh}\",[]]Bd[\"{{{AHEf{J@`}}{AHEf{J@`}}}CKh}\",[]]Bd[\"{{{AHEf{L@`}}{AHEf{L@`}}}CKh}\",[]]Bf[\"{{{AHEf{AGM`}}{AHEf{AGM`}}}CKh}\",[]]Bd[\"{{{AHEf{OD`}}{AHEf{OD`}}}CKh}\",[]]Bf[\"{{{AHEf{AOBh}}{AHEf{AOBh}}}CKh}\",[]]Bf[\"{{{AHEf{BBAb}}{AHEf{BBAb}}}CKh}\",[]]Bf[\"{{{AHEf{ANBl}}{AHEf{ANBl}}}CKh}\",[]]Bf[\"{{{AHEf{ANAh}}{AHEf{ANAh}}}CKh}\",[]]Bf[\"{{{AHEf{AGJn}}{AHEf{AGJn}}}CKh}\",[]]Bf[\"{{{AHEf{BB@l}}{AHEf{BB@l}}}CKh}\",[]]Bf[\"{{{AHEf{BCGh}}{AHEf{BCGh}}}CKh}\",[]]Bf[\"{{{AHEf{AOCd}}{AHEf{AOCd}}}CKh}\",[]]Bf[\"{{{AHEf{AOCj}}{AHEf{AOCj}}}CKh}\",[]]Bf[\"{{{AHEf{AODb}}{AHEf{AODb}}}CKh}\",[]]Bf[\"{{{AHEf{BDJn}}{AHEf{BDJn}}}CKh}\",[]]Bd[\"{{{AHEf{CJd}}{AHEf{CJd}}}CKh}\",[]]Bb[\"{{{AHEf{Nb}}{AHEf{Nb}}}CKh}\",[]]Bf[\"{{{AHEf{ABEh}}{AHEf{ABEh}}}CKh}\",[]]Bf[\"{{{AHEf{BFIn}}{AHEf{BFIn}}}CKh}\",[]]Bd[\"{{{AHEf{JBb}}{AHEf{JBb}}}CKh}\",[]]Bd[\"{{{AHEf{LBb}}{AHEf{LBb}}}CKh}\",[]]Bf[\"{{{AHEf{BEGj}}{AHEf{BEGj}}}CKh}\",[]]Bd[\"{{{AHEf{J@b}}{AHEf{J@b}}}CKh}\",[]]Bf[\"{{{AHEf{BDCl}}{AHEf{BDCl}}}CKh}\",[]]Bd[\"{{{AHEf{L@b}}{AHEf{L@b}}}CKh}\",[]]Bf[\"{{{AHEf{AICd}}{AHEf{AICd}}}CKh}\",[]]Bf[\"{{{AHEf{AGLn}}{AHEf{AGLn}}}CKh}\",[]]Bf[\"{{{AHEf{BDJh}}{AHEf{BDJh}}}CKh}\",[]]Bf[\"{{{AHEf{AI@d}}{AHEf{AI@d}}}CKh}\",[]]Bf[\"{{{AHEf{AGMb}}{AHEf{AGMb}}}CKh}\",[]]Bf[\"{{{AHEf{AGMh}}{AHEf{AGMh}}}CKh}\",[]]Bf[\"{{{AHEf{B@Gj}}{AHEf{B@Gj}}}CKh}\",[]]Bd[\"{{{AHEf{ODb}}{AHEf{ODb}}}CKh}\",[]]Bf[\"{{{AHEf{AIFd}}{AHEf{AIFd}}}CKh}\",[]]Bf[\"{{{AHEf{ANBj}}{AHEf{ANBj}}}CKh}\",[]]Bf[\"{{{AHEf{ANDn}}{AHEf{ANDn}}}CKh}\",[]]Bf[\"{{{AHEf{BAEh}}{AHEf{BAEh}}}CKh}\",[]]Bf[\"{{{AHEf{AOC`}}{AHEf{AOC`}}}CKh}\",[]]Bf[\"{{{AHEf{BBAd}}{AHEf{BBAd}}}CKh}\",[]]Bf[\"{{{AHEf{AGLh}}{AHEf{AGLh}}}CKh}\",[]]Bf[\"{{{AHEf{BAAf}}{AHEf{BAAf}}}CKh}\",[]]Bf[\"{{{AHEf{ANBn}}{AHEf{ANBn}}}CKh}\",[]]Bf[\"{{{AHEf{ANAj}}{AHEf{ANAj}}}CKh}\",[]]Bf[\"{{{AHEf{AGK`}}{AHEf{AGK`}}}CKh}\",[]]Bf[\"{{{AHEf{BBA`}}{AHEf{BBA`}}}CKh}\",[]]Bf[\"{{{AHEf{BCGl}}{AHEf{BCGl}}}CKh}\",[]]Bf[\"{{{AHEf{BCHd}}{AHEf{BCHd}}}CKh}\",[]]Bf[\"{{{AHEf{AOCf}}{AHEf{AOCf}}}CKh}\",[]]Bf[\"{{{AHEf{ABCh}}{AHEf{ABCh}}}CKh}\",[]]Bf[\"{{{AHEf{BDCh}}{AHEf{BDCh}}}CKh}\",[]]Bf[\"{{{AHEf{AOCl}}{AHEf{AOCl}}}CKh}\",[]]Bf[\"{{{AHEf{AODd}}{AHEf{AODd}}}CKh}\",[]]Bf[\"{{{AHEf{BAF`}}{AHEf{BAF`}}}CKh}\",[]]Bf[\"{{{AHEf{BDK`}}{AHEf{BDK`}}}CKh}\",[]]Bd[\"{{{AHEf{CJf}}{AHEf{CJf}}}CKh}\",[]]Bb[\"{{{AHEf{Nd}}{AHEf{Nd}}}CKh}\",[]]Bf[\"{{{AHEf{ABEj}}{AHEf{ABEj}}}CKh}\",[]]Bf[\"{{{AHEf{AGJl}}{AHEf{AGJl}}}CKh}\",[]]Bf[\"{{{AHEf{B@Hf}}{AHEf{B@Hf}}}CKh}\",[]]Bf[\"{{{AHEf{BE@b}}{AHEf{BE@b}}}CKh}\",[]]Bd[\"{{{AHEf{JBf}}{AHEf{JBf}}}CKh}\",[]]Bf[\"{{{AHEf{BFJ`}}{AHEf{BFJ`}}}CKh}\",[]]Bd[\"{{{AHEf{JBd}}{AHEf{JBd}}}CKh}\",[]]Bf[\"{{{AHEf{AODf}}{AHEf{AODf}}}CKh}\",[]]Ai[\"{{{AHEf{CIf}}}L@`}\",[]]Ak[\"{{{AHEf{AICl}}}ABEh}\",[]]Ak[\"{{{AHEf{BE@d}}}ABEh}\",[]]Ak[\"{{{AHEf{AIDh}}}ABEh}\",[]]o[\"{KOdL@b}\",[]]o[\"{KN`L@b}\",[]]A`[\"{AGHfL@b}\",[]]A`[\"{B@EhL@b}\",[]]A`[\"{AOFlL@b}\",[]]A`[\"{AOEnL@b}\",[]]Aa[\"{ABDlABEj}\",[]]Aa[\"{ABGfABEj}\",[]]Aa[\"{ABBnABEj}\",[]]A`[\"{OBlABEj}\",[]]n[\"{{}OBd}\",[]]A`[\"{CI`ABCh}\",[]]Be[\"{{{AHEf{LB`}}{AHEf{LB`}}}ABDj}\",[]]Be[\"{{{AHEf{L@`}}{AHEf{L@`}}}ABDj}\",[]]Bg[\"{{{AHEf{ANBl}}{AHEf{ANBl}}}ABDj}\",[]]Bg[\"{{{AHEf{AOCj}}{AHEf{AOCj}}}ABDj}\",[]]Be[\"{{{AHEf{CJd}}{AHEf{CJd}}}ABDj}\",[]]Bc[\"{{{AHEf{Nb}}{AHEf{Nb}}}ABDj}\",[]]Bg[\"{{{AHEf{ABEh}}{AHEf{ABEh}}}ABDj}\",[]]Be[\"{{{AHEf{LBb}}{AHEf{LBb}}}ABDj}\",[]]Bg[\"{{{AHEf{BDCl}}{AHEf{BDCl}}}ABDj}\",[]]Be[\"{{{AHEf{L@b}}{AHEf{L@b}}}ABDj}\",[]]Bg[\"{{{AHEf{AGMh}}{AHEf{AGMh}}}ABDj}\",[]]Bg[\"{{{AHEf{B@Gj}}{AHEf{B@Gj}}}ABDj}\",[]]Bg[\"{{{AHEf{AIFd}}{AHEf{AIFd}}}ABDj}\",[]]Bg[\"{{{AHEf{ANBn}}{AHEf{ANBn}}}ABDj}\",[]]Bg[\"{{{AHEf{BCHd}}{AHEf{BCHd}}}ABDj}\",[]]Bg[\"{{{AHEf{BDCh}}{AHEf{BDCh}}}ABDj}\",[]]Bg[\"{{{AHEf{AOCl}}{AHEf{AOCl}}}ABDj}\",[]]Be[\"{{{AHEf{CJf}}{AHEf{CJf}}}ABDj}\",[]]Bc[\"{{{AHEf{Nd}}{AHEf{Nd}}}ABDj}\",[]]Bg[\"{{{AHEf{ABEj}}{AHEf{ABEj}}}ABDj}\",[]]o[\"{ABEhNb}\",[]]o[\"{ABEjNd}\",[]]n[\"{KOfNd}\",[]]o[\"{AGIbNd}\",[]]A`[\"{ABFjKOl}\",[]]A`[\"{ABFlKOl}\",[]]A`[\"{ABEdKOl}\",[]]A`[\"{ABFnKOl}\",[]]Bg[\"{{{AHEf{Ob}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{ANAb}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BAEj}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BFBf}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AI@h}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AI@l}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BEGn}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BDCj}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{ABBj}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{B@Dd}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{ABCb}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AIDb}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{BACb}}{AHEf{C@bAGJj}}}L@d}\",[]]Bh[\"{{{AHEf{CIf}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AICl}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BE@d}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BAEf}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{AGMf}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{AID`}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{BAEd}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{AOHh}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{ANDf}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AIDh}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AIFb}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{AOHf}}{AHEf{C@bAGJj}}}L@d}\",[]]0Cd[\"{{{AHEf{LB`}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Ce[\"{{{AHEf{BEGh}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Cd[\"{{{AHEf{J@`}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Cd[\"{{{AHEf{L@`}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Ce[\"{{{AHEf{ABG`}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{AIAh}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{AGM`}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{BBAj}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Cd[\"{{{AHEf{OD`}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{AOBh}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Ce[\"{{{AHEf{BBAb}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Ce[\"{{{AHEf{BBBn}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{BBCl}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{BDFj}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{BDHh}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{ANBl}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{BABh}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{AOGj}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{BENj}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{AOAl}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{ANAh}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Ce[\"{{{AHEf{AGJn}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Ce[\"{{{AHEf{BB@l}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Ce[\"{{{AHEf{BCGh}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Ce[\"{{{AHEf{AOCd}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Ce[\"{{{AHEf{AOCj}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Ce[\"{{{AHEf{AODb}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{BDJn}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Ce[\"{{{AHEf{BCKf}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Cd[\"{{{AHEf{CJd}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Cc[\"{{{AHEf{Nb}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Ce[\"{{{AHEf{ABEh}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Ce[\"{{{AHEf{ABFj}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Cc[\"{{{AHEf{Mf}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{AGIh}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Cd[\"{{{AHEf{J@h}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{ANBb}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Cd[\"{{{AHEf{O@n}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{B@Fj}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{BDBj}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{BFIn}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{ANAn}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{BDKf}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]Ce[\"{{{AHEf{BEG`}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Cd[\"{{{AHEf{JBb}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Bi[\"{{{AHEf{AI@n}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{ABBl}}{AHEf{C@bAGJj}}}L@d}\",[]]Bh[\"{{{AHEf{LBb}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{BEGj}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bh[\"{{{AHEf{J@b}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BDCl}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bh[\"{{{AHEf{L@b}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{AOGb}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AICd}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AGLn}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BDJh}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{AI@d}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{ABGb}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AIAj}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AGMb}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BBAl}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AGMh}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{B@Gj}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bh[\"{{{AHEf{ODb}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AIFd}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{ANBj}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{ANDn}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BAEh}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AOC`}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{BBAd}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{AGLh}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{BAAf}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bh[\"{{{AHEf{JBl}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BBC`}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BBCn}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BDFl}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BDHj}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{ANBn}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BABj}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AOGn}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BENl}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AOB`}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{ANAj}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{AGK`}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{BBA`}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{BCGl}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{ABAn}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BCHd}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{ABCd}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AOCf}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{AN@n}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AIDd}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{B@G`}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BBBl}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BFBd}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{ABCh}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BDCh}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{BBAh}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{B@Db}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AOCl}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{AODd}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BAF`}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{BDK`}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{AIB`}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BFNb}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BCKh}}{AHEf{C@bAGJj}}}L@d}\",[]]Bh[\"{{{AHEf{CJf}}{AHEf{C@bAGJj}}}L@d}\",[]]Bg[\"{{{AHEf{Nd}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{ABEj}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{ABFl}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AGJl}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{B@Hf}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{BE@b}}{AHEf{C@bAGJj}}}L@d}\",[]]Bh[\"{{{AHEf{JBf}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bi[\"{{{AHEf{BFJ`}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{ANB`}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BDKl}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BEGb}}{AHEf{C@bAGJj}}}L@d}\",[]]0Cd[\"{{{AHEf{JBd}}{AHEf{C@bAGJj}}}{{L@f{HEhJAb}}}}\",[]]0Bg[\"{{{AHEf{Mh}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AGIj}}{AHEf{C@bAGJj}}}L@d}\",[]]Bh[\"{{{AHEf{J@j}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{ANBd}}{AHEf{C@bAGJj}}}L@d}\",[]]Bh[\"{{{AHEf{OA`}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{B@Fl}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BDBl}}{AHEf{C@bAGJj}}}L@d}\",[]]Bh[\"{{{AHEf{JBh}}{AHEf{C@bAGJj}}}L@d}\",[]]0Bh[\"{{{AHEf{JBn}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{BAFb}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{ABEd}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{ABFn}}{AHEf{C@bAGJj}}}L@d}\",[]]Bi[\"{{{AHEf{AODf}}{AHEf{C@bAGJj}}}L@d}\",[]]Ak[\"{{{AHEf{AOGb}}}ABBf}\",[]]A`[\"{ABG`CG`}\",[]]A`[\"{ABGbCG`}\",[]]Aj[\"{{{AHEf{B@G`}}}KL`}\",[]]A`[\"{BFBdCG`}\",[]]o[\"{CI`CG`}\",[]]A`[\"{ABEdKL`}\",[]]Ce[\"{{{AHEf{C@bBENj}}{Md{ABFb}}}{{AHEf{C@bBENj}}}}\",[]]Ce[\"{{{AHEf{C@bBENl}}{Md{ABFd}}}{{AHEf{C@bBENl}}}}\",[]]8Af[\"{{ABBfCG`}BAEj}\",[]]Ag[\"{{}{{Md{BFBf}}}}\",[]]Ae[\"{{KL`CFn}B@Cb}\",[]]Cd[\"{{BABh{KOl{AOGj}}AGMn{Md{OBf}}}{{L@h{ABBj}}}}\",[]]Ah[\"{{{Ml{JDn}}}AICf}\",[]]A`[\"{OAnBBBn}\",[]]Ah[\"{{{Md{NOl}}}BABh}\",[]]Bc[\"{{{Md{AOGl}}{Md{BDBn}}}AOGj}\",[]]Ai[\"{{{Md{AOAn}}}AOAl}\",[]]B`[\"{{{AHEf{{KIl{Ll}}}}}AOCj}\",[]]Ae[\"{{CFnKL`}BCKf}\",[]]Ag[\"{{CJdNbCG`}ABEh}\",[]]Aj[\"{{{Md{AGIh}}CFl}Mf}\",[]]An[\"{{{Md{ANBb}}O@bCFl}J@h}\",[]]An[\"{{{Md{B@Fj}}O@bCFl}O@n}\",[]]Ae[\"{{KL`CFn}B@Cd}\",[]]Ah[\"{{{Ml{JDn}}}AICh}\",[]]Cm[\"{{{Md{BABj}}{KOl{{Md{AOGn}}}}CKh{KOl{{JE`{Ll}}}}}ABBl}\",[]]A`[\"{KL`BGAd}\",[]]o[\"{{}BFMh}\",[]]A`[\"{OAnBBC`}\",[]]Ah[\"{{{Md{NOn}}}BABj}\",[]]Bc[\"{{{Md{AOH`}}{Md{BDC`}}}AOGn}\",[]]Ai[\"{{{Md{AOBb}}}AOB`}\",[]]Ek[\"{{ABAd{AHEf{c}}{AHEf{{KIl{CFn}}}}CKh}{{L@f{{KKn{B@G`{KOl{ABAd}}}}BDCh}}}{BDGfJDf}}\",[\"\"]]B`[\"{{{AHEf{{KIl{Ll}}}}}AOCl}\",[]]Af[\"{{BDK`LAl}BAF`}\",[]]A`[\"{KL`BFNb}\",[]]Ae[\"{{CFnKL`}BCKh}\",[]]Ag[\"{{CJfNdCG`}ABEj}\",[]]Aj[\"{{{Md{AGIj}}CFl}Mh}\",[]]An[\"{{{Md{ANBd}}O@bCFl}J@j}\",[]]An[\"{{{Md{B@Fl}}O@bCFl}OA`}\",[]]Aj[\"{AIF`{{L@h{BAFb}}}}\",[]]Ai[\"{{{Md{BCK`}}}BFHb}\",[]]Ai[\"{{{Md{BCKd}}}BFHd}\",[]]o[\"{CJdCJd}\",[]]o[\"{CJfCJf}\",[]]Ak[\"{{{AHEf{AIEd}}}AIEb}\",[]]Ai[\"{{{AHEf{OBf}}}O@b}\",[]]0Aj[\"{{{AHEf{AOHh}}}O@b}\",[]]Ak[\"{{{AHEf{B@Hb}}}AIEb}\",[]]Ak[\"{{{AHEf{AIEf}}}AIEb}\",[]]Ak[\"{{{AHEf{B@Hd}}}AIEb}\",[]]Bg[\"{{{AHEf{AI@l}}L@`}{{KOl{ABBf}}}}\",[]]Aa[\"{AGM`ABBf}\",[]]Bg[\"{{{AHEf{AI@n}}L@b}{{KOl{ABBf}}}}\",[]]Aa[\"{AGMbABBf}\",[]]Ag[\"{Od{{L@j{HEh}}}}\",[]]Ag[\"{Of{{L@j{HEh}}}}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]Aa[\"{AOEnAIDn}\",[]]Aa[\"{ABEdAIDn}\",[]]Aa[\"{BAF`BDK`}\",[]]A`[\"{AGM`CG`}\",[]]A`[\"{AGMbCG`}\",[]]A`[\"{ABAnIOn}\",[]]Am[\"{{{AHEf{C@bABDf}}}HEh}\",[]]Am[\"{{{AHEf{C@bABCb}}}HEh}\",[]]Am[\"{{{AHEf{C@bAICl}}}HEh}\",[]]Am[\"{{{AHEf{C@bAIDh}}}HEh}\",[]]Al[\"{{{AHEf{C@bKMh}}}HEh}\",[]]A`[\"{cc{}}\",[\"T\"]]00000000000000000000Aa[\"{BBAbBDCj}\",[]]111111111Aa[\"{AGMfBAEf}\",[]]22Aa[\"{AID`AGMf}\",[]]Aa[\"{BBAbAGMf}\",[]]4Aa[\"{BBAbAID`}\",[]]5Aa[\"{AGMfBAEd}\",[]]666Aa[\"{BBAbAIFb}\",[]]Aa[\"{AOHfAIFb}\",[]]Aa[\"{AOBhAIFb}\",[]]9Aa[\"{BBAbAOHf}\",[]]:::::::o[\"{CFnLB`}\",[]]n[\"{LlLB`}\",[]]o[\"{CFlLB`}\",[]]A`[\"{ABEhLB`}\",[]]A`[\"{BDJnLB`}\",[]]??Al[\"{cL@`{{CHj{CFn}}}}\",[\"T\"]]A`[\"{cc{}}\",[\"T\"]]000000Aa[\"{ANAfBBAb}\",[]]1A`[\"{J@fBBAb}\",[]]A`[\"{JAlBBAb}\",[]]33333A`[\"{LB`ANBl}\",[]]4444Aa[\"{BBD`ANAh}\",[]]5Aa[\"{BEGhANAh}\",[]]66A`[\"{JAlBB@l}\",[]]77Aa[\"{BDKfAOCd}\",[]]888A`[\"{LB`BDJn}\",[]]999A`[\"{LB`ABEh}\",[]]:::::::::::::Aa[\"{ANEdANAn}\",[]];;;;;;A`[\"{BCI`JBb}\",[]]A`[\"{BFJnJBb}\",[]]A`[\"{BBAnJBb}\",[]]A`[\"{BBCdJBb}\",[]]A`[\"{BFIlJBb}\",[]]A`[\"{BFDnJBb}\",[]]A`[\"{BCGnJBb}\",[]]A`[\"{BCJhJBb}\",[]]A`[\"{BADfJBb}\",[]]A`[\"{BACdJBb}\",[]]A`[\"{cc{}}\",[\"T\"]]A`[\"{AIBjJBb}\",[]]1111111111111111A`[\"{ABEjLBb}\",[]]A`[\"{BDK`LBb}\",[]]o[\"{CFlLBb}\",[]]4o[\"{CFnLBb}\",[]]n[\"{LlLBb}\",[]]6666Al[\"{cL@b{{CHj{CFn}}}}\",[\"T\"]]777777777777777777Aa[\"{BAF`BBAd}\",[]]888A`[\"{AGLnJBl}\",[]]99999A`[\"{LBbANBn}\",[]]::::Aa[\"{BBD`ANAj}\",[]];Aa[\"{BEGjANAj}\",[]]>>>>>>>Aa[\"{B@HfBDCh}\",[]]????A`[\"{JBfBAF`}\",[]]A`[\"{cc{}}\",[\"T\"]]A`[\"{LBbBDK`}\",[]]111111A`[\"{LBbABEj}\",[]]22222A`[\"{B@HfJBf}\",[]]33333Aa[\"{ANEdANB`}\",[]]444444A`[\"{BACdJBd}\",[]]A`[\"{BFDnJBd}\",[]]6A`[\"{BFJnJBd}\",[]]A`[\"{BCI`JBd}\",[]]A`[\"{AIBjJBd}\",[]]A`[\"{BBAnJBd}\",[]]A`[\"{BBCdJBd}\",[]]A`[\"{BFIlJBd}\",[]]A`[\"{BADfJBd}\",[]]A`[\"{BCJhJBd}\",[]]A`[\"{BCGnJBd}\",[]]?????????An[\"{{{AHEf{c}}}AIF`CGf}\",[\"S\"]]A`[\"{cc{}}\",[\"T\"]]000000000000000Bm[\"{{{AHEf{LB`}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bk[\"{{{AHEf{L@`}}{AHEf{C@bc}}}HEhKNn}\",[\"H\"]]Bn[\"{{{AHEf{ANBl}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bn[\"{{{AHEf{AOCj}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bm[\"{{{AHEf{CJd}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bl[\"{{{AHEf{Nb}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bn[\"{{{AHEf{ABEh}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bm[\"{{{AHEf{LBb}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bn[\"{{{AHEf{BDCl}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bk[\"{{{AHEf{L@b}}{AHEf{C@bc}}}HEhKNn}\",[\"H\"]]Bn[\"{{{AHEf{AGMh}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bn[\"{{{AHEf{B@Gj}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bn[\"{{{AHEf{AIFd}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bn[\"{{{AHEf{ANBn}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bn[\"{{{AHEf{BCHd}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bn[\"{{{AHEf{BDCh}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bn[\"{{{AHEf{AOCl}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bm[\"{{{AHEf{CJf}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bl[\"{{{AHEf{Nd}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bn[\"{{{AHEf{ABEj}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Bn[\"{{{AHEf{AGJl}}{AHEf{C@bc}}}HEhKNn}\",[\"__H\"]]Aa[\"{{}c{}}\",[\"U\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n[\"{{}HEh}\",[]]0000Cc[\"{{{AHEf{C@bKMh}}KL`}{{L@f{{KOl{J@b}}AGMh}}}}\",[]]El[\"{{{AHEf{NNd}}{AHEf{C@b{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}}{{L@f{{AHEf{C@b{KIl{Ll}}}}ANAn}}}}\",[]]Bh[\"{{{AHEf{C@bOCf}}Nd}{{KOl{ABEj}}}}\",[]]El[\"{{{AHEf{NNf}}{AHEf{C@b{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}}{{L@f{{AHEf{C@b{KIl{Ll}}}}ANB`}}}}\",[]]Bf[\"{{{AHEf{AI@l}}L@`}{{KOl{CIf}}}}\",[]]Bf[\"{{{AHEf{CIf}}}{{L@f{HEhAHOn}}}}\",[]]A`[\"{AIAhCG`}\",[]]Am[\"{{{AHEf{C@bAI@n}}}HEh}\",[]]A`[\"{AIAjCG`}\",[]]Cj[\"{{{Oj{{AHEf{C@bAHOf}}}}{AHEf{C@bNO`}}}{{CIn{HEh}}}}\",[]]Dj[\"{{{Oj{{AHEf{C@bBBDf}}}}{AHEf{C@bNO`}}}{{CIn{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Oj{{AHEf{C@bABDf}}}}{AHEf{C@bNO`}}}{{CIn{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Oj{{AHEf{C@bABDh}}}}{AHEf{C@bNO`}}}{{CIn{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Oj{{AHEf{C@bAI@h}}}}{AHEf{C@bNO`}}}{{CIn{c}}}{}}\",[\"Future::Output\"]]Di[\"{{{Oj{{AHEf{C@bOAd}}}}{AHEf{C@bNO`}}}{{CIn{c}}}{}}\",[\"Future::Output\"]]Di[\"{{{Oj{{AHEf{C@bKOb}}}}{AHEf{C@bNO`}}}{{CIn{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Oj{{AHEf{C@bAGHj}}}}{AHEf{C@bNO`}}}{{CIn{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Oj{{AHEf{C@bABAb}}}}{AHEf{C@bNO`}}}{{CIn{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Oj{{AHEf{C@bAOFj}}}}{AHEf{C@bNO`}}}{{CIn{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Oj{{AHEf{C@bAOGd}}}}{AHEf{C@bNO`}}}{{CIn{c}}}{}}\",[\"Future::Output\"]]Di[\"{{{Oj{{AHEf{C@bKLh}}}}{AHEf{C@bNO`}}}{{CIn{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Oj{{AHEf{C@bBACb}}}}{AHEf{C@bNO`}}}{{CIn{c}}}{}}\",[\"Future::Output\"]]Bf[\"{{{AHEf{C@bAI@n}}}{{KOl{JBl}}}}\",[]]De[\"{{{AHEf{C@bAICl}}{AHEf{C@b{KIl{Ll}}}}}{{L@f{{KOl{KL`}}AGMf}}}}\",[]]Bm[\"{{{AHEf{C@bAICn}}CKh}{{L@f{KMhB@Gj}}}}\",[]]Bo[\"{{CJf{AHEf{C@bc}}}{{L@f{BE@bJBf}}}Mn}\",[\"R\"]]Bf[\"{{{AHEf{C@bAGJd}}}{{KOl{IOn}}}}\",[]]Ec[\"{{{AHEf{BAFb}}AIF`{AHEf{C@b{KIl{AIBb}}}}{AHEf{C@b{KIl{ABEd}}}}}{{L@h{KL`}}}}\",[]]Dk[\"{{{AHEf{NNd}}{AHEf{C@b{A@`{Ll}}}}{AHEf{{KIl{Ll}}}}}{{L@f{HEhANAn}}}}\",[]]Dk[\"{{{AHEf{NNf}}{AHEf{C@b{A@`{Ll}}}}{AHEf{{KIl{Ll}}}}}{{L@f{HEhANB`}}}}\",[]]C`[\"{{{AHEf{C@bAGJd}}IOnCKh}{{L@f{HEhBDCl}}}}\",[]]Cc[\"{{{AHEf{BAFb}}AIF`{AHEf{ABFn}}}{{L@h{HEh}}}}\",[]]Bn[\"{{{AHEf{C@bBENj}}CFn}{{AHEf{C@bBENj}}}}\",[]]Bn[\"{{{AHEf{C@bBENl}}CFn}{{AHEf{C@bBENl}}}}\",[]]Aj[\"{{{AHEf{AI@l}}}CJd}\",[]]Aj[\"{{{AHEf{AI@n}}}CJf}\",[]]Cn[\"{{{AHEf{NOl}}{AHEf{{KIl{Ll}}}}{AHEf{C@b{KIl{Ll}}}}}HEh}\",[]]Cn[\"{{{AHEf{NOn}}{AHEf{{KIl{Ll}}}}{AHEf{C@b{KIl{Ll}}}}}HEh}\",[]]A`[\"{BAEjCG`}\",[]]A`[\"{ABFjKL`}\",[]]A`[\"{ABFlKL`}\",[]]o[\"{JDbCKh}\",[]]Bm[\"{{{AHEf{C@bAICl}}LB`}{{L@f{HEhAOBh}}}}\",[]]Bm[\"{{{AHEf{C@bBE@d}}LB`}{{L@f{HEhAOBh}}}}\",[]]Bm[\"{{{AHEf{C@bAICn}}LBb}{{L@f{HEhAOC`}}}}\",[]]Bn[\"{{{AHEf{AIEl}}{AHEf{CFj}}}{{KOl{IOn}}}}\",[]]Bn[\"{{{AHEf{BADb}}{AHEf{CFj}}}{{KOl{IOn}}}}\",[]]Bn[\"{{{AHEf{BCKf}}{AHEf{CFj}}}{{KOl{IOn}}}}\",[]]Bn[\"{{{AHEf{AIEn}}{AHEf{CFj}}}{{KOl{IOn}}}}\",[]]Bn[\"{{{AHEf{BADd}}{AHEf{CFj}}}{{KOl{IOn}}}}\",[]]Bn[\"{{{AHEf{BCKh}}{AHEf{CFj}}}{{KOl{IOn}}}}\",[]]Aa[\"{{}c{}}\",[\"V\"]]0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bg[\"{{{Md{BDBf}}O@bCFl}{{Ml{AIA`}}}}\",[]]Bg[\"{{{Md{AGIh}}O@bCFl}{{Ml{AIA`}}}}\",[]]Bg[\"{{{Md{ANBb}}O@bCFl}{{Ml{AIA`}}}}\",[]]Bg[\"{{{Md{B@Fj}}O@bCFl}{{Ml{AIA`}}}}\",[]]Bg[\"{{{Md{BDBh}}O@bCFl}{{Ml{AIAb}}}}\",[]]Bg[\"{{{Md{AGIj}}O@bCFl}{{Ml{AIAb}}}}\",[]]Bg[\"{{{Md{ANBd}}O@bCFl}{{Ml{AIAb}}}}\",[]]Bg[\"{{{Md{B@Fl}}O@bCFl}{{Ml{AIAb}}}}\",[]]Ai[\"{BDOd{{Md{BDBh}}}}\",[]]o[\"{J@`IOn}\",[]]A`[\"{ABG`CG`}\",[]]o[\"{OD`KL`}\",[]]o[\"{J@bIOn}\",[]]A`[\"{ABGbCG`}\",[]]o[\"{ODbKL`}\",[]]Aa[\"{AN@nBBAd}\",[]]Ag[\"{{{AHEf{Ob}}}Ob}\",[]]Ak[\"{{{AHEf{ANAb}}}ANAb}\",[]]Ak[\"{{{AHEf{AI@l}}}AI@l}\",[]]Ak[\"{{{AHEf{BEGn}}}BEGn}\",[]]Ak[\"{{{AHEf{BDCj}}}BDCj}\",[]]Ak[\"{{{AHEf{ABBj}}}ABBj}\",[]]Ak[\"{{{AHEf{B@Dd}}}B@Dd}\",[]]Ak[\"{{{AHEf{BAEf}}}BAEf}\",[]]Ak[\"{{{AHEf{AGMf}}}AGMf}\",[]]Ak[\"{{{AHEf{AID`}}}AID`}\",[]]Ak[\"{{{AHEf{BAEd}}}BAEd}\",[]]Ak[\"{{{AHEf{AIFb}}}AIFb}\",[]]Ak[\"{{{AHEf{AOHf}}}AOHf}\",[]]Ai[\"{{{AHEf{LB`}}}LB`}\",[]]Ak[\"{{{AHEf{BEGh}}}BEGh}\",[]]Ai[\"{{{AHEf{L@`}}}L@`}\",[]]Ak[\"{{{AHEf{ABG`}}}ABG`}\",[]]Ak[\"{{{AHEf{AIAh}}}AIAh}\",[]]Ak[\"{{{AHEf{AGM`}}}AGM`}\",[]]Ak[\"{{{AHEf{BBAj}}}BBAj}\",[]]Ai[\"{{{AHEf{OD`}}}OD`}\",[]]Ak[\"{{{AHEf{AOBh}}}AOBh}\",[]]Ak[\"{{{AHEf{BBAb}}}BBAb}\",[]]Ak[\"{{{AHEf{BBCl}}}BBCl}\",[]]Ak[\"{{{AHEf{BDFj}}}BDFj}\",[]]Ak[\"{{{AHEf{BDHh}}}BDHh}\",[]]Ak[\"{{{AHEf{ANBl}}}ANBl}\",[]]Ak[\"{{{AHEf{BABh}}}BABh}\",[]]Ak[\"{{{AHEf{AOGj}}}AOGj}\",[]]Ak[\"{{{AHEf{BENj}}}BENj}\",[]]Ak[\"{{{AHEf{AOAl}}}AOAl}\",[]]Ak[\"{{{AHEf{ANAh}}}ANAh}\",[]]Ak[\"{{{AHEf{AGJn}}}AGJn}\",[]]Ak[\"{{{AHEf{BB@l}}}BB@l}\",[]]Ak[\"{{{AHEf{BCGh}}}BCGh}\",[]]Ak[\"{{{AHEf{AOCd}}}AOCd}\",[]]Ak[\"{{{AHEf{AOCj}}}AOCj}\",[]]Ak[\"{{{AHEf{AODb}}}AODb}\",[]]Ak[\"{{{AHEf{BDJn}}}BDJn}\",[]]Ai[\"{{{AHEf{CJd}}}CJd}\",[]]Ag[\"{{{AHEf{Nb}}}Nb}\",[]]Ak[\"{{{AHEf{ABEh}}}ABEh}\",[]]Ag[\"{{{AHEf{Mf}}}Mf}\",[]]Ak[\"{{{AHEf{AGIh}}}AGIh}\",[]]Ai[\"{{{AHEf{J@h}}}J@h}\",[]]Ak[\"{{{AHEf{ANBb}}}ANBb}\",[]]Ai[\"{{{AHEf{O@n}}}O@n}\",[]]Ak[\"{{{AHEf{B@Fj}}}B@Fj}\",[]]Ak[\"{{{AHEf{BEG`}}}BEG`}\",[]]Ai[\"{{{AHEf{JBb}}}JBb}\",[]]Ai[\"{{{AHEf{LBb}}}LBb}\",[]]Ak[\"{{{AHEf{BEGj}}}BEGj}\",[]]Ak[\"{{{AHEf{BDCl}}}BDCl}\",[]]Ai[\"{{{AHEf{L@b}}}L@b}\",[]]Ak[\"{{{AHEf{AOGb}}}AOGb}\",[]]Ak[\"{{{AHEf{AICd}}}AICd}\",[]]Ak[\"{{{AHEf{AGLn}}}AGLn}\",[]]Ak[\"{{{AHEf{BDJh}}}BDJh}\",[]]Ak[\"{{{AHEf{AI@d}}}AI@d}\",[]]Ak[\"{{{AHEf{ABGb}}}ABGb}\",[]]Ak[\"{{{AHEf{AIAj}}}AIAj}\",[]]Ak[\"{{{AHEf{AGMb}}}AGMb}\",[]]Ak[\"{{{AHEf{BBAl}}}BBAl}\",[]]Ak[\"{{{AHEf{AGMh}}}AGMh}\",[]]Ak[\"{{{AHEf{B@Gj}}}B@Gj}\",[]]Ai[\"{{{AHEf{ODb}}}ODb}\",[]]Ak[\"{{{AHEf{AIFd}}}AIFd}\",[]]Ak[\"{{{AHEf{ANBj}}}ANBj}\",[]]Ak[\"{{{AHEf{BAEh}}}BAEh}\",[]]Ak[\"{{{AHEf{AOC`}}}AOC`}\",[]]Ak[\"{{{AHEf{BBAd}}}BBAd}\",[]]Ak[\"{{{AHEf{AGLh}}}AGLh}\",[]]Ak[\"{{{AHEf{BAAf}}}BAAf}\",[]]Ak[\"{{{AHEf{BBCn}}}BBCn}\",[]]Ak[\"{{{AHEf{BDFl}}}BDFl}\",[]]Ak[\"{{{AHEf{BDHj}}}BDHj}\",[]]Ak[\"{{{AHEf{ANBn}}}ANBn}\",[]]Ak[\"{{{AHEf{BABj}}}BABj}\",[]]Ak[\"{{{AHEf{AOGn}}}AOGn}\",[]]Ak[\"{{{AHEf{BENl}}}BENl}\",[]]Ak[\"{{{AHEf{AOB`}}}AOB`}\",[]]Ak[\"{{{AHEf{ANAj}}}ANAj}\",[]]Ak[\"{{{AHEf{AGK`}}}AGK`}\",[]]Ak[\"{{{AHEf{BBA`}}}BBA`}\",[]]Ak[\"{{{AHEf{BCGl}}}BCGl}\",[]]Ak[\"{{{AHEf{ABAn}}}ABAn}\",[]]Ak[\"{{{AHEf{BCHd}}}BCHd}\",[]]Ak[\"{{{AHEf{AOCf}}}AOCf}\",[]]Ak[\"{{{AHEf{BBBl}}}BBBl}\",[]]Ak[\"{{{AHEf{BFBd}}}BFBd}\",[]]Ak[\"{{{AHEf{ABCh}}}ABCh}\",[]]Ak[\"{{{AHEf{BDCh}}}BDCh}\",[]]Ak[\"{{{AHEf{AOCl}}}AOCl}\",[]]Ak[\"{{{AHEf{AODd}}}AODd}\",[]]Ak[\"{{{AHEf{BAF`}}}BAF`}\",[]]Ak[\"{{{AHEf{BDK`}}}BDK`}\",[]]Ak[\"{{{AHEf{AIB`}}}AIB`}\",[]]Ak[\"{{{AHEf{BFNb}}}BFNb}\",[]]Ai[\"{{{AHEf{CJf}}}CJf}\",[]]Ag[\"{{{AHEf{Nd}}}Nd}\",[]]Ak[\"{{{AHEf{ABEj}}}ABEj}\",[]]Ak[\"{{{AHEf{AGJl}}}AGJl}\",[]]Ak[\"{{{AHEf{B@Hf}}}B@Hf}\",[]]Ak[\"{{{AHEf{BE@b}}}BE@b}\",[]]Ai[\"{{{AHEf{JBf}}}JBf}\",[]]Ak[\"{{{AHEf{BEGb}}}BEGb}\",[]]Ai[\"{{{AHEf{JBd}}}JBd}\",[]]Ag[\"{{{AHEf{Mh}}}Mh}\",[]]Ak[\"{{{AHEf{AGIj}}}AGIj}\",[]]Ai[\"{{{AHEf{J@j}}}J@j}\",[]]Ak[\"{{{AHEf{ANBd}}}ANBd}\",[]]Ai[\"{{{AHEf{OA`}}}OA`}\",[]]Ak[\"{{{AHEf{B@Fl}}}B@Fl}\",[]]Ai[\"{{{AHEf{JBn}}}JBn}\",[]]Ak[\"{{{AHEf{ABEd}}}ABEd}\",[]]Ak[\"{{{AHEf{ABFn}}}ABFn}\",[]]Ak[\"{{{AHEf{AODf}}}AODf}\",[]]Ak[\"{{{AHEf{BDOd}}}BDOd}\",[]]Ak[\"{{{AHEf{BBBd}}}BBBd}\",[]]Bn[\"{{{AHEf{AI@l}}LB`{AHEf{{KIl{Ll}}}}}HEh}\",[]]Bn[\"{{{AHEf{ABBj}}LB`{AHEf{{KIl{Ll}}}}}HEh}\",[]]Bd[\"{{{AHEf{CIf}}}{{L@f{`BAAd}}}}\",[]]Bf[\"{{{AHEf{C@bAI@n}}O@bLBbIOn}HEh}\",[]]Bj[\"{{{AHEf{AOCj}}}{{AHEf{{KIl{Ll}}}}}}\",[]]Bj[\"{{{AHEf{AOCl}}}{{AHEf{{KIl{Ll}}}}}}\",[]]Aa[\"{B@EhAGLh}\",[]]k[\"{{}}\",[]]A`[\"{ABEhCG`}\",[]]A`[\"{ABEjCG`}\",[]]A`[\"{AODbCKh}\",[]]A`[\"{AODdCKh}\",[]]l[\"{O@d}\",[]]l[\"{O@f}\",[]]Ce[\"{{{AHEf{BDGf}}{AHEf{C@bMn}}}{{L@f{AOClBDCh}}}}\",[]]Ce[\"{{{AHEf{BGAd}}{AHEf{C@bMn}}}{{L@f{AOClBDCh}}}}\",[]]Bc[\"{{{AHEf{AI@n}}}{{A@`{L@b}}}}\",[]]Bd[\"{{{AHEf{JDh}}{AHEf{CFj}}}HEh}\",[]]Am[\"{{{AHEf{ANAd}}KL`}HEh}\",[]]Ai[\"{{{AHEf{JDj}}}HEh}\",[]]Bf[\"{{{Oj{{AHEf{C@bAHOf}}}}O@b}HEh}\",[]]Bm[\"{{{AHEf{C@bAIDh}}LB`}{{L@f{HEhAOBh}}}}\",[]]Bm[\"{{{AHEf{C@bAIDj}}LBb}{{L@f{HEhAOC`}}}}\",[]]Am[\"{ABCb{{L@f{HEhAIDb}}}}\",[]]Dc[\"{{{AHEf{C@bABBl}}ABCd{AHEf{C@b{A@`{Ll}}}}}{{L@f{ABFlAIDd}}}}\",[]]Bh[\"{{{AHEf{OBf}}{Oj{{Ml{KNl}}}}}HEh}\",[]]Bi[\"{{{AHEf{AOHh}}{Oj{{Ml{KNl}}}}}HEh}\",[]]Bi[\"{{{AHEf{ANDf}}{Oj{{Ml{KNl}}}}}HEh}\",[]]n[\"{ObCKh}\",[]]Ak[\"{{{AHEf{AI@l}}}BBAj}\",[]]Ak[\"{{{AHEf{ABBj}}}B@Dd}\",[]]An[\"{{{AHEf{C@bAI@n}}}BBAl}\",[]]Ce[\"{{{AHEf{C@bAICf}}{KOl{LAl}}}{{AHEf{C@bAICf}}}}\",[]]Ce[\"{{{AHEf{C@bAICh}}{KOl{LAl}}}{{AHEf{C@bAICh}}}}\",[]]Ck[\"{{{AHEf{C@bAIDh}}{AHEf{{KIl{Ll}}}}}{{L@f{KL`AIFb}}}}\",[]]Ck[\"{{{AHEf{C@bAIDj}}{AHEf{{KIl{Ll}}}}}{{L@f{KL`AIFd}}}}\",[]]Bl[\"{{{AHEf{BE@b}}{AHEf{C@bc}}}HEhKMf}\",[\"W\"]]Aj[\"{{{AHEf{ABBj}}}KLh}\",[]]An[\"{ABCb{{L@f{AI@hBBAb}}}}\",[]]Bh[\"{{{AHEf{C@bOCf}}Nd}{{KOl{ABEj}}}}\",[]]Fh[\"{{{AHEf{C@bABBl}}ABCdO@b{AHEf{C@b{A@`{Ll}}}}{KOl{{Md{AOGn}}}}}{{L@f{{KKn{BCHdAI@n}}{Ml{AN@n}}}}}}\",[]]Am[\"{AHEf{{AHEf{c}}}{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000o[\"{OD`KL`}\",[]]o[\"{ODbKL`}\",[]]o[\"{ObANAb}\",[]]Bb[\"{{AIDnBAAbOb}{{L@j{ANAd}}}}\",[]]Aj[\"{AIDn{{L@h{ABBj}}}}\",[]]B`[\"{{}{{L@f{HEh{Ml{JAd}}}}}}\",[]]Ak[\"{{{AHEf{AI@l}}}BBAb}\",[]]Be[\"{{{AHEf{ABBl}}}{{AHEf{BABj}}}}\",[]]o[\"{LlBDJn}\",[]]A`[\"{AIAhCG`}\",[]]o[\"{AOGjMd}\",[]]o[\"{LlBDK`}\",[]]A`[\"{AIAjCG`}\",[]]o[\"{AOGnMd}\",[]]A`[\"{BAF`KOl}\",[]]Bl[\"{{{AHEf{C@bc}}}{{L@f{LB`B@Hf}}}Mn}\",[\"B\"]]Bl[\"{{{AHEf{C@bc}}}{{L@f{L@`B@Hf}}}Mn}\",[\"B\"]]Bm[\"{{{AHEf{C@bc}}}{{L@f{AGJnB@Hf}}}Mn}\",[\"B\"]]Bm[\"{{{AHEf{C@bc}}}{{L@f{BDJnB@Hf}}}Mn}\",[\"B\"]]Bm[\"{{{AHEf{C@bc}}}{{L@f{ABEhB@Hf}}}Mn}\",[\"B\"]]Bh[\"{{{AHEf{C@bc}}}{{L@n{LBb}}}Mn}\",[\"B\"]]Bh[\"{{{AHEf{C@bc}}}{{L@n{L@b}}}Mn}\",[\"B\"]]Bi[\"{{{AHEf{C@bc}}}{{L@n{AGK`}}}Mn}\",[\"B\"]]Ek[\"{{{AHEf{C@b{KNj{ABAd}}}}{AHEf{c}}{AHEf{{KIl{CFn}}}}CKh}{{L@f{BBBlBDCh}}}{BDGfJDf}}\",[\"\"]]Bi[\"{{{AHEf{C@bc}}}{{L@n{BDK`}}}Mn}\",[\"B\"]]Bi[\"{{{AHEf{C@bc}}}{{L@n{ABEj}}}Mn}\",[\"B\"]]Bi[\"{{{AHEf{C@bc}}}{{L@n{AGJf}}}Mn}\",[\"B\"]]A`[\"{ABEdKOl}\",[]]Bk[\"{{{AHEf{LB`}}{AHEf{C@bc}}}HEhKMf}\",[\"B\"]]Bk[\"{{{AHEf{L@`}}{AHEf{C@bc}}}HEhKMf}\",[\"B\"]]Bl[\"{{{AHEf{AGJn}}{AHEf{C@bc}}}HEhKMf}\",[\"B\"]]Bl[\"{{{AHEf{BDJn}}{AHEf{C@bc}}}HEhKMf}\",[\"B\"]]Bl[\"{{{AHEf{ABEh}}{AHEf{C@bc}}}HEhKMf}\",[\"B\"]]Bk[\"{{{AHEf{LBb}}{AHEf{C@bc}}}HEhKMf}\",[\"B\"]]Bk[\"{{{AHEf{L@b}}{AHEf{C@bc}}}HEhKMf}\",[\"B\"]]Bl[\"{{{AHEf{AGK`}}{AHEf{C@bc}}}HEhKMf}\",[\"B\"]]Bl[\"{{{AHEf{ABAn}}{AHEf{C@bc}}}HEhKMf}\",[\"B\"]]Bl[\"{{{AHEf{BDK`}}{AHEf{C@bc}}}HEhKMf}\",[\"B\"]]Bl[\"{{{AHEf{ABEj}}{AHEf{C@bc}}}HEhKMf}\",[\"B\"]]Bl[\"{{{AHEf{AGJh}}{AHEf{C@bc}}}HEhKMf}\",[\"B\"]]Bj[\"{{{AHEf{C@bAIDh}}}{{L@f{HEhAOBh}}}}\",[]]Bj[\"{{{AHEf{C@bAIDj}}}{{L@f{HEhANBj}}}}\",[]]An[\"{{{AHEf{ANEb}}ABBf}HEh}\",[]]Ea[\"{{{AHEf{C@bABBl}}O@bAGJl{KOl{AODd}}ABAd{AHEf{C@b{A@`{Ll}}}}}{{KOl{B@D`}}}}\",[]]o[\"{CHlO@d}\",[]]o[\"{CHnO@f}\",[]]A`[\"{ABCbHEh}\",[]]Ba[\"{{{AHEf{C@bABBl}}ABCd}HEh}\",[]]Bh[\"{{{AHEf{AIEl}}{AHEf{CFj}}IOn}HEh}\",[]]Bh[\"{{{AHEf{BADb}}{AHEf{CFj}}IOn}HEh}\",[]]Bh[\"{{{AHEf{BCKf}}{AHEf{CFj}}IOn}HEh}\",[]]Bh[\"{{{AHEf{AIEn}}{AHEf{CFj}}IOn}HEh}\",[]]Bh[\"{{{AHEf{BADd}}{AHEf{CFj}}IOn}HEh}\",[]]Bh[\"{{{AHEf{BCKh}}{AHEf{CFj}}IOn}HEh}\",[]]A`[\"{AGInCKh}\",[]]o[\"{J@`CG`}\",[]]o[\"{J@bCG`}\",[]]Ce[\"{{{AHEf{C@bAIA`}}O@bO@bCG`CKh{AHEf{AOG`}}}HEh}\",[]]Cc[\"{{{AHEf{C@bMf}}O@bO@bCG`CKh{AHEf{AOG`}}}HEh}\",[]]Cd[\"{{{AHEf{C@bJ@h}}O@bO@bCG`CKh{AHEf{AOG`}}}HEh}\",[]]Cd[\"{{{AHEf{C@bO@n}}O@bO@bCG`CKh{AHEf{AOG`}}}HEh}\",[]]Ce[\"{{{AHEf{C@bAIAb}}O@bO@bCG`CKh{AHEf{AOGb}}}HEh}\",[]]Cc[\"{{{AHEf{C@bMh}}O@bO@bCG`CKh{AHEf{AOGb}}}HEh}\",[]]Cd[\"{{{AHEf{C@bJ@j}}O@bO@bCG`CKh{AHEf{AOGb}}}HEh}\",[]]Cd[\"{{{AHEf{C@bOA`}}O@bO@bCG`CKh{AHEf{AOGb}}}HEh}\",[]]o[\"{CHlO@d}\",[]]o[\"{CHnO@f}\",[]]o[\"{BCKnLl}\",[]]A`[\"{BB@lIOn}\",[]]A`[\"{BCGhIOn}\",[]]A`[\"{BBA`IOn}\",[]]A`[\"{BCGlIOn}\",[]]A`[\"{BAF`LAl}\",[]]Aa[\"{BAAjBBAd}\",[]]Bg[\"{{{AHEf{ABBj}}AGMn}{{L@h{HEh}}}}\",[]]A`[\"{ABCbHEh}\",[]]Cf[\"{{{AHEf{C@bABBl}}ABCd{AHEf{C@b{A@`{Ll}}}}}ABFl}\",[]]l[\"{O@d}\",[]]Aa[\"{AGJlAIDn}\",[]]l[\"{O@f}\",[]]Am[\"{{ABBhOb}{{L@j{HEh}}}}\",[]]B`[\"{{AOGjAIDn}{{L@h{ABBj}}}}\",[]]Bb[\"{AOGh{{L@f{HEh{Ml{JAd}}}}}}\",[]]Bk[\"{{{AHEf{BDCj}}}{{KOl{{AHEf{JAd}}}}}}\",[]]Bk[\"{{{AHEf{BAEf}}}{{KOl{{AHEf{JAd}}}}}}\",[]]Bk[\"{{{AHEf{AGMf}}}{{KOl{{AHEf{JAd}}}}}}\",[]]Bk[\"{{{AHEf{AID`}}}{{KOl{{AHEf{JAd}}}}}}\",[]]Bk[\"{{{AHEf{BAEd}}}{{KOl{{AHEf{JAd}}}}}}\",[]]Bk[\"{{{AHEf{AIFb}}}{{KOl{{AHEf{JAd}}}}}}\",[]]Bk[\"{{{AHEf{AOHf}}}{{KOl{{AHEf{JAd}}}}}}\",[]]Bk[\"{{{AHEf{BBAb}}}{{KOl{{AHEf{JAd}}}}}}\",[]]Bk[\"{{{AHEf{BBAd}}}{{KOl{{AHEf{JAd}}}}}}\",[]]A`[\"{ABFjKOl}\",[]]A`[\"{ABFlKOl}\",[]]A`[\"{ABFnKOl}\",[]]Bg[\"{{{AHEf{CIf}}}{{L@f{AICbAHOn}}}}\",[]]Aa[\"{AOFlAICd}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]A`[\"{ABEdKL`}\",[]]Aa[\"{BBAjABG`}\",[]]Aa[\"{BBAlABGb}\",[]]10Dh[\"{{{AHEf{NOl}}{AHEf{{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}}{{L@f{HEhANAn}}}}\",[]]Dh[\"{{{AHEf{NOn}}{AHEf{{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}}{{L@f{HEhANB`}}}}\",[]]Aj[\"{{{AHEf{AIA`}}}CG`}\",[]]Ah[\"{{{AHEf{Mf}}}CG`}\",[]]Ai[\"{{{AHEf{J@h}}}CG`}\",[]]Ai[\"{{{AHEf{O@n}}}CG`}\",[]]Aj[\"{{{AHEf{AIAb}}}CG`}\",[]]Ah[\"{{{AHEf{Mh}}}CG`}\",[]]Ai[\"{{{AHEf{J@j}}}CG`}\",[]]Ai[\"{{{AHEf{OA`}}}CG`}\",[]]Aj[\"{{{AHEf{BEMh}}}KL`}\",[]]Aj[\"{{{AHEf{BEMj}}}KL`}\",[]]Aj[\"{{{AHEf{BFMh}}}KL`}\",[]]Aj[\"{{{AHEf{BFNb}}}KL`}\",[]]n[\"{ObKL`}\",[]]n[\"{{}NNn}\",[]]0000o[\"{N`ABAh}\",[]]Ba[\"{{AHEf{AHEf{c}}}ABDj{}}\",[\"K\"]]0000000000000000000Cg[\"{{{AHEf{ABBj}}AIDn{AHEf{CFj}}}{{L@f{AI@hAOCd}}}}\",[]]Dl[\"{{{AHEf{C@bABBl}}O@bAOB`AIDn{AHEf{CFj}}}{{L@f{{KKn{BCHdAI@n}}AOCf}}}}\",[]]Dm[\"{{{AHEf{AGLb}}L@`CG`{AHEf{{KIl{Ll}}}}{AHEf{C@bABAd}}}{{L@f{HEhANAn}}}}\",[]]Ca[\"{{{AHEf{AGKd}}KL`{AHEf{C@b{KIl{Ll}}}}}HEh}\",[]]Dm[\"{{{AHEf{AGLd}}L@bCG`{AHEf{{KIl{Ll}}}}{AHEf{C@bABAd}}}{{L@f{HEhANB`}}}}\",[]]Ca[\"{{{AHEf{AGKf}}KL`{AHEf{C@b{KIl{Ll}}}}}HEh}\",[]]o[\"{{}ANE`}\",[]]n[\"{{}JDh}\",[]]o[\"{{}ANAd}\",[]]o[\"{{}B@Cb}\",[]]o[\"{{}B@Dd}\",[]]n[\"{{}LB`}\",[]]n[\"{{}L@`}\",[]]o[\"{{}ABG`}\",[]]o[\"{{}AIAh}\",[]]o[\"{{}AGM`}\",[]]o[\"{{}BBAj}\",[]]n[\"{{}OD`}\",[]]o[\"{{}AOBh}\",[]]o[\"{{}BBCl}\",[]]o[\"{{}BDFj}\",[]]o[\"{{}BDHh}\",[]]o[\"{{}ANBl}\",[]]o[\"{{}BABh}\",[]]o[\"{{}BENj}\",[]]o[\"{{}BCKf}\",[]]o[\"{{}AGIh}\",[]]o[\"{{}ANBb}\",[]]o[\"{{}B@Fj}\",[]]o[\"{{}BDBj}\",[]]o[\"{{}B@Cd}\",[]]o[\"{{}BFMh}\",[]]n[\"{{}LBb}\",[]]n[\"{{}L@b}\",[]]o[\"{{}AICd}\",[]]o[\"{{}AI@d}\",[]]o[\"{{}ABGb}\",[]]o[\"{{}AIAj}\",[]]o[\"{{}AGMb}\",[]]o[\"{{}BBAl}\",[]]n[\"{{}ODb}\",[]]o[\"{{}BAEh}\",[]]o[\"{{}AOC`}\",[]]o[\"{{}BBCn}\",[]]o[\"{{}BDFl}\",[]]o[\"{{}BDHj}\",[]]o[\"{{}ANBn}\",[]]o[\"{{}BABj}\",[]]o[\"{{}BENl}\",[]]o[\"{{}BFNb}\",[]]o[\"{{}BCKh}\",[]]o[\"{{}AGIj}\",[]]o[\"{{}ANBd}\",[]]o[\"{{}B@Fl}\",[]]o[\"{{}BDBl}\",[]]o[\"{{}ABEd}\",[]]n[\"{{}JDj}\",[]]o[\"{{}BBBd}\",[]]o[\"{{}B@Db}\",[]]Ak[\"{{{AHEf{B@G`}}}AOCl}\",[]]Ak[\"{{{AHEf{BBBl}}}AOCl}\",[]]Aa[\"{BFBdAOCl}\",[]]Aa[\"{BDKbAOCl}\",[]]A`[\"{CI`AOCl}\",[]]A`[\"{JCnAOCl}\",[]]A`[\"{JDbAOCl}\",[]]Aa[\"{BCKnAOCl}\",[]]Cg[\"{{{AHEf{AGLb}}L@`CG`{AHEf{C@b{KIl{Ll}}}}KL`}HEh}\",[]]Ca[\"{{{AHEf{AGKd}}KL`{AHEf{C@b{KIl{Ll}}}}}HEh}\",[]]Cg[\"{{{AHEf{AGLd}}L@bCG`{AHEf{C@b{KIl{Ll}}}}KL`}HEh}\",[]]Ca[\"{{{AHEf{AGKf}}KL`{AHEf{C@b{KIl{Ll}}}}}HEh}\",[]]Aj[\"{{{AHEf{AICl}}}CKh}\",[]]Aj[\"{{{AHEf{BE@d}}}CKh}\",[]]Ak[\"{{{AHEf{AIA`}}}BDBj}\",[]]Ai[\"{{{AHEf{Mf}}}BDBj}\",[]]Aj[\"{{{AHEf{J@h}}}BDBj}\",[]]Aj[\"{{{AHEf{O@n}}}BDBj}\",[]]Ak[\"{{{AHEf{AIAb}}}BDBl}\",[]]Ai[\"{{{AHEf{Mh}}}BDBl}\",[]]Aj[\"{{{AHEf{J@j}}}BDBl}\",[]]Aj[\"{{{AHEf{OA`}}}BDBl}\",[]]Bn[\"{{{AHEf{C@bBBCl}}CFl}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}CFl}{{AHEf{C@bBBCn}}}}\",[]]Bf[\"{{{AHEf{C@bAIA`}}O@bCG`CG`}HEh}\",[]]Bd[\"{{{AHEf{C@bMf}}O@bCG`CG`}HEh}\",[]]Bf[\"{{{AHEf{C@bAIAb}}O@bCG`CG`}HEh}\",[]]Bd[\"{{{AHEf{C@bMh}}O@bCG`CG`}HEh}\",[]]Aj[\"{{{AHEf{AI@l}}}KOb}\",[]]Bc[\"{{{AHEf{ABDh}}}{{KOl{L@`}}}}\",[]]Aa[\"{BFBdAOCl}\",[]]Aa[\"{BDKbAOCl}\",[]]A`[\"{CI`AOCl}\",[]]A`[\"{JCnAOCl}\",[]]Aa[\"{BCKnAOCl}\",[]]Di[\"{{{AHEf{AIDh}}}{{`{{KNl{}{{KOn{{L@f{{KOl{LB`}}AOHf}}}}}}CJbCJh}}}}\",[]]Bn[\"{{{AHEf{AIDj}}}{{L@f{{KOl{LBb}}AOC`}}}}\",[]]n[\"{ObKL`}\",[]]o[\"{JDhKL`}\",[]]Am[\"{{{AHEf{C@bAI@n}}}OCf}\",[]]Aj[\"{{{AHEf{AGLb}}}KL`}\",[]]Aj[\"{{{AHEf{AGLd}}}KL`}\",[]]A`[\"{AHEfLAn}\",[]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bd[\"{{{AHEf{BEGn}}}{{KOl{AI@l}}}}\",[]]Bn[\"{{{AHEf{C@bAOAl}}CFn}{{AHEf{C@bAOAl}}}}\",[]]Bn[\"{{{AHEf{C@bAOB`}}CFn}{{AHEf{C@bAOB`}}}}\",[]]A`[\"{BFBdCFn}\",[]]A`[\"{BDKbCFn}\",[]]o[\"{CI`CFn}\",[]]o[\"{JCnCFn}\",[]]A`[\"{AGInKOl}\",[]]Aa[\"{ABFnAHEf}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]Aa[\"{BAEjABBf}\",[]]A`[\"{KMhBAEh}\",[]]Aa[\"{BBAjAIAh}\",[]]Aa[\"{BBAlAIAj}\",[]]10Bg[\"{{{AHEf{C@bc}}KL`}AOCj{MnJDf}}\",[\"\"]]Bg[\"{{{AHEf{C@bc}}KL`}AOCl{MnJDf}}\",[\"\"]]o[\"{{}BBBn}\",[]]o[\"{{}BBC`}\",[]]A`[\"{CG`BFMh}\",[]]Cd[\"{{{AHEf{CFj}}}{{L@j{ANAbc}}}{}}\",[\"FromStr::Err\"]]o[\"{CFnLB`}\",[]]o[\"{CFnLBb}\",[]]Al[\"{CG`{{L@f{LB`BEGh}}}}\",[]]Al[\"{CG`{{L@f{LBbBEGj}}}}\",[]]Aa[\"{BEFbANAl}\",[]]Aa[\"{BGAfB@E`}\",[]]Aa[\"{BEFdANAl}\",[]]Aa[\"{BGAhB@E`}\",[]]Af[\"{{}{{KOl{Ab}}}}\",[]]0000Aj[\"{{{AHEf{AI@n}}}CKh}\",[]]Bo[\"{{{AHEf{C@bBDHh}}ABBf}{{AHEf{C@bBDHh}}}}\",[]]Bo[\"{{{AHEf{C@bBDHj}}ABBf}{{AHEf{C@bBDHj}}}}\",[]]Ao[\"{{{Ml{AIA`}}}{{Ml{Mb}}}}\",[]]Am[\"{{{Ml{Mf}}}{{Ml{Mb}}}}\",[]]An[\"{{{Ml{J@h}}}{{Ml{Mb}}}}\",[]]An[\"{{{Ml{O@n}}}{{Ml{Mb}}}}\",[]]Ao[\"{{{Ml{AIAb}}}{{Ml{Mb}}}}\",[]]Am[\"{{{Ml{Mh}}}{{Ml{Mb}}}}\",[]]An[\"{{{Ml{J@j}}}{{Ml{Mb}}}}\",[]]An[\"{{{Ml{OA`}}}{{Ml{Mb}}}}\",[]]Aj[\"{{{AHEf{BEGn}}}CKh}\",[]]Bo[\"{{{AHEf{C@bBENj}}ABBf}{{AHEf{C@bBENj}}}}\",[]]Bo[\"{{{AHEf{C@bBENl}}ABBf}{{AHEf{C@bBENl}}}}\",[]]Bc[\"{{{AHEf{AI@h}}}{{KOl{KO`}}}}\",[]]Bc[\"{{{AHEf{AI@l}}}{{KOl{KO`}}}}\",[]]Bc[\"{{{AHEf{ABCb}}}{{KOl{KO`}}}}\",[]]Bc[\"{{{AHEf{ABCd}}}{{KOl{KO`}}}}\",[]]A`[\"{AGJlKOl}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]Bc[\"{{{AHEf{AGJd}}}{{KOl{KL`}}}}\",[]]Am[\"{{{AHEf{ANEb}}KL`}HEh}\",[]]Aj[\"{{{AHEf{AI@l}}}OAd}\",[]]Bg[\"{{{AHEf{AIDh}}}{{L@f{BNlAOBh}}}}\",[]]Bg[\"{{{AHEf{AIDj}}}{{L@f{BNlAOC`}}}}\",[]]A`[\"{B@DhKOl}\",[]]A`[\"{B@DjKOl}\",[]]A`[\"{AGInKOl}\",[]]A`[\"{AN@nKOl}\",[]]Ck[\"{{{AHEf{C@bBABh}}{KOl{{JE`{Ll}}}}}{{AHEf{C@bBABh}}}}\",[]]Ck[\"{{{AHEf{C@bBABj}}{KOl{{JE`{Ll}}}}}{{AHEf{C@bBABj}}}}\",[]]A`[\"{BDBjKOl}\",[]]A`[\"{BDBlKOl}\",[]]Ac[\"{AHEfc{}}\",[\"T\"]]0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Ao[\"{c{{L@f{e}}}{}{}}\",[\"U\",\"T\"]]00000000000000000000000000000000000000000000Al[\"{KL`{{L@f{LB`BEGh}}}}\",[]]Al[\"{HEf{{L@f{LB`BEGh}}}}\",[]]Al[\"{CG`{{L@f{LB`BEGh}}}}\",[]]333333333333333Aj[\"{ABBf{{L@f{ANBl}}}}\",[]]44444Ai[\"{CG`{{L@f{AGJn}}}}\",[]]555555555555555555555555555Bb[\"{{{Md{AOAh}}}{{L@f{BCJn}}}}\",[]]Aj[\"{AOAh{{L@f{BCJn}}}}\",[]]7Bb[\"{{{Md{AOGf}}}{{L@f{BCKb}}}}\",[]]Aj[\"{AOGf{{L@f{BCKb}}}}\",[]]9999999999999999999Al[\"{KL`{{L@f{LBbBEGj}}}}\",[]]Al[\"{HEf{{L@f{LBbBEGj}}}}\",[]]Al[\"{CG`{{L@f{LBbBEGj}}}}\",[]]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Bm[\"{AOAh{{L@f{BCK`c}}}{}}\",[\"TryFrom::Error\"]]Ce[\"{{{Md{AOAh}}}{{L@f{BCK`c}}}{}}\",[\"TryFrom::Error\"]]Ao[\"{c{{L@f{e}}}{}{}}\",[\"U\",\"T\"]]Bm[\"{AOGf{{L@f{BCKdc}}}{}}\",[\"TryFrom::Error\"]]Ce[\"{{{Md{AOGf}}}{{L@f{BCKdc}}}{}}\",[\"TryFrom::Error\"]]2222222222222222222222222222Aj[\"{{}{{L@f{c}}}{}}\",[\"U\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cc[\"{{{Oj{{AHEf{C@bc}}}}{AHEf{C@bNO`}}}CIn{}}\",[\"F\"]]00000000Cc[\"{{{AHEf{BAFb}}AIF`{AHEf{ABFn}}}{{L@h{HEh}}}}\",[]]Bk[\"{{{AHEf{BEMh}}AOCj}{{L@f{HEhAIAn}}}}\",[]]Bk[\"{{{AHEf{BEMj}}AOCl}{{L@f{HEhAIB`}}}}\",[]]0Bk[\"{{{AHEf{BFMh}}AOCl}{{L@f{HEhAIB`}}}}\",[]]Ak[\"{{{AHEf{AI@l}}}ABAb}\",[]]Bi[\"{{{AHEf{C@bAOHj}}}{{L@f{LB`JAn}}}}\",[]]Bi[\"{{{AHEf{C@bAOHj}}}{{L@f{CJdJAn}}}}\",[]]Bh[\"{{{AHEf{C@bAOHj}}}{{L@f{NbJAn}}}}\",[]]Bj[\"{{{AHEf{C@bAOHj}}}{{L@f{ABEhJAn}}}}\",[]]Bf[\"{{{AHEf{C@bAOHj}}}{{L@l{LBb}}}}\",[]]Bf[\"{{{AHEf{C@bAOHj}}}{{L@l{CJf}}}}\",[]]Be[\"{{{AHEf{C@bAOHj}}}{{L@l{Nd}}}}\",[]]Bg[\"{{{AHEf{C@bAOHj}}}{{L@l{ABEj}}}}\",[]]Aa[\"{ANAbBEGf}\",[]]Bc[\"{{{AHEf{AIA`}}}{{Ml{AIA`}}}}\",[]]Ba[\"{{{AHEf{Mf}}}{{Ml{AIA`}}}}\",[]]Bb[\"{{{AHEf{J@h}}}{{Ml{AIA`}}}}\",[]]Bb[\"{{{AHEf{O@n}}}{{Ml{AIA`}}}}\",[]]Bc[\"{{{AHEf{AIAb}}}{{Ml{AIAb}}}}\",[]]Ba[\"{{{AHEf{Mh}}}{{Ml{AIAb}}}}\",[]]Bb[\"{{{AHEf{J@j}}}{{Ml{AIAb}}}}\",[]]Bb[\"{{{AHEf{OA`}}}{{Ml{AIAb}}}}\",[]]A`[\"{ABG`CG`}\",[]]An[\"{{{AHEf{C@bAI@n}}}AGJd}\",[]]A`[\"{ABGbCG`}\",[]]C`[\"{{{AHEf{C@bAOCj}}}{{AHEf{C@b{KIl{Ll}}}}}}\",[]]C`[\"{{{AHEf{C@bAOCl}}}{{AHEf{C@b{KIl{Ll}}}}}}\",[]]Ah[\"{Ll{{KOl{AODb}}}}\",[]]Ah[\"{Ll{{KOl{AODd}}}}\",[]]Ah[\"{Ll{{KOl{AODf}}}}\",[]]A`[\"{ABEhCJd}\",[]]A`[\"{ABEjCJf}\",[]]Bi[\"{AI@h{{L@f{{KKn{AI@lBBDf}}AI@h}}}}\",[]]o[\"{CJdCKh}\",[]]o[\"{CJfCKh}\",[]]Aj[\"{{{AHEf{AI@n}}}CKh}\",[]]21Aj[\"{{{AHEf{ABCb}}}CKh}\",[]]Aj[\"{{{AHEf{ABCd}}}CKh}\",[]]Bn[\"{{{AHEf{C@bAOGj}}CKh}{{AHEf{C@bAOGj}}}}\",[]]Bn[\"{{{AHEf{C@bAOGn}}CKh}{{AHEf{C@bAOGn}}}}\",[]]Bk[\"{{{AHEf{OBf}}O@b}{{Oj{{Ml{AHOf}}}}}}\",[]]Bl[\"{{{AHEf{AOHh}}O@b}{{Oj{{Ml{AHOf}}}}}}\",[]]Bl[\"{{{AHEf{ANDf}}O@b}{{Oj{{Ml{AHOf}}}}}}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]Ak[\"{{{AHEf{AI@l}}}ABDf}\",[]]Bc[\"{{{AHEf{AI@l}}AIDnAICb}ABDh}\",[]]Ce[\"{{{AHEf{C@bAI@n}}AGJlAICdO@b}{{L@f{L@bAGLh}}}}\",[]]43Bm[\"{{{AHEf{C@bAI@n}}L@b}{{L@f{HEhAI@d}}}}\",[]]Do[\"{{{Oj{{AHEf{C@bAICl}}}}{AHEf{C@bNO`}}{AHEf{C@bOB`}}}{{CIn{{L@h{HEh}}}}}}\",[]]Ee[\"{{{Oj{{AHEf{C@bAICl}}}}{AHEf{C@bNO`}}{AHEf{C@b{KIl{Ll}}}}}{{CIn{{L@h{KL`}}}}}}\",[]]Ec[\"{{{AHEf{C@bAICl}}{AHEf{C@bNO`}}{AHEf{C@b{KIl{Ll}}}}}{{CIn{{L@f{KL`AGMf}}}}}}\",[]]Fg[\"{{{AHEf{C@bBAA`}}{AHEf{C@bNO`}}{AHEf{C@b{KIl{AIBb}}}}{AHEf{C@b{KIl{ABEd}}}}}{{CIn{{L@h{KL`}}}}}}\",[]]Dm[\"{{{Oj{{AHEf{C@bAGMl}}}}{AHEf{ABFn}}{AHEf{C@bNO`}}}{{CIn{{L@h{HEh}}}}}}\",[]]:9Cd[\"{{{AHEf{C@bBABh}}{Md{NOl}}}{{AHEf{C@bBABh}}}}\",[]]Cd[\"{{{AHEf{C@bBABj}}{Md{NOn}}}{{AHEf{C@bBABj}}}}\",[]]Cj[\"{{{AHEf{AOGl}}CFnAOCj{AHEf{{KIl{Ll}}}}}{{JE`{Ll}}}}\",[]]Cj[\"{{{AHEf{BCKb}}CFnAOCj{AHEf{{KIl{Ll}}}}}{{JE`{Ll}}}}\",[]]Cj[\"{{{AHEf{AOH`}}CFnAOCl{AHEf{{KIl{Ll}}}}}{{JE`{Ll}}}}\",[]]Cj[\"{{{AHEf{BCKd}}CFnAOCl{AHEf{{KIl{Ll}}}}}{{JE`{Ll}}}}\",[]]Cj[\"{{{AHEf{BFHd}}CFnAOCh{AHEf{{KIl{Ll}}}}}{{JE`{Ll}}}}\",[]]Bb[\"{KL`{{KKn{KL`{KOl{KL`}}}}}}\",[]]00000Aj[\"{{{AHEf{AI@l}}}KL`}\",[]]A`[\"{AHEfLAl}\",[]]0000000000000000000000000000000000000000000000000000000Ce[\"{{{AHEf{C@bAOGj}}{Md{BDBn}}}{{AHEf{C@bAOGj}}}}\",[]]Ce[\"{{{AHEf{C@bAOGn}}{Md{BDC`}}}{{AHEf{C@bAOGn}}}}\",[]]A`[\"{BFBdJCd}\",[]]o[\"{AOGjMd}\",[]]o[\"{AOGnMd}\",[]]Aj[\"{{{AHEf{ABBj}}}HEh}\",[]]Ck[\"{{{AHEf{C@bAIDh}}{AHEf{{KIl{Ll}}}}}{{L@f{HEhAIFb}}}}\",[]]n[\"{{}KL`}\",[]]Ak[\"{{{AHEf{AI@l}}}AGHj}\",[]]Bn[\"{{{AHEf{C@bBBCl}}CKh}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}CKh}{{AHEf{C@bBBCn}}}}\",[]]Bi[\"{{{AHEf{C@b}}}{{AHEf{C@bc}}}{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bc[\"{{AHEf{AHEf{C@bc}}}HEh{}}\",[\"T\"]]0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cc[\"{{{AHEf{C@bAI@n}}O@bL@bLBb}{{L@f{HEhBAAf}}}}\",[]]A`[\"{AGInCKh}\",[]]B`[\"{{AHEf{AHEf{c}}}CKh{}}\",[\"K\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Aa[\"{BB@lBDJn}\",[]]A`[\"{BCGhLB`}\",[]]Aa[\"{BBA`BDK`}\",[]]A`[\"{BCGlLBb}\",[]]o[\"{KN`LBb}\",[]]o[\"{OBlLBb}\",[]]Aa[\"{BB@lAIBd}\",[]]k[\"{{}}\",[]]o[\"{LB`CG`}\",[]]o[\"{LBbCG`}\",[]]Aj[\"{{{AHEf{AI@n}}}CKh}\",[]]Aj[\"{{{AHEf{B@Db}}}CKh}\",[]]Bd[\"{{{AHEf{BAA`}}}{{L@h{AIDn}}}}\",[]]Bd[\"{{{AHEf{ABBj}}}{{L@h{AIDn}}}}\",[]]A`[\"{AGM`CG`}\",[]]A`[\"{AGMbCG`}\",[]]Cb[\"{{{AHEf{BBBd}}{AHEf{AIDf}}CG`}{{Md{ANEb}}}}\",[]]Bn[\"{{{AHEf{C@bBBCl}}CKh}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}CKh}{{AHEf{C@bBBCn}}}}\",[]]Bg[\"{{{AHEf{AI@l}}L@`}{{KOl{AGM`}}}}\",[]]Bj[\"{{{AHEf{C@bAI@n}}L@b}{{KOl{AGMb}}}}\",[]]Aa[\"{AGHfAGMb}\",[]]Da[\"{{{Oj{{AHEf{C@bAIDh}}}}{AHEf{C@bNO`}}}{{CIn{{L@h{HEh}}}}}}\",[]]00Eb[\"{{{Oj{{AHEf{C@bAIDh}}}}{AHEf{C@bNO`}}{AHEf{{KIl{Ll}}}}}{{CIn{{L@h{KL`}}}}}}\",[]]Ef[\"{{{Oj{{AHEf{C@bAIDh}}}}{AHEf{C@bNO`}}{AHEf{{KIl{Ll}}}}}{{CIn{{L@f{KL`AIFb}}}}}}\",[]]1Bm[\"{{{AHEf{JDj}}{AHEf{CId}}}{{L@h{HEh}}}}\",[]]Cd[\"{{{AHEf{C@bAICl}}KL`}{{L@f{{KOl{J@`}}AGMf}}}}\",[]]Cd[\"{{{AHEf{C@bBE@d}}KL`}{{L@f{{KOl{J@`}}AGMf}}}}\",[]]Cn[\"{{{AHEf{C@bAICl}}{AHEf{C@b{KIl{Ll}}}}}{{L@f{HEhBAEd}}}}\",[]]Bj[\"{{{AHEf{CIf}}AICb}{{L@f{HEhBDJf}}}}\",[]]Bn[\"{{{AHEf{C@bAICf}}O@b}{{AHEf{C@bAICf}}}}\",[]]Bn[\"{{{AHEf{C@bAICh}}O@b}{{AHEf{C@bAICh}}}}\",[]]A`[\"{BAEjACn}\",[]]o[\"{JDhCG`}\",[]]o[\"{{}AIDn}\",[]]0o[\"{{}AHEf}\",[]]Bj[\"{{ABCb{Md{AOGj}}}{{L@f{AI@hBBAb}}}}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]Bh[\"{{{AHEf{AGIn}}AIDn}{{L@j{AGMn}}}}\",[]]A`[\"{AGM`CFl}\",[]]Aj[\"{{{AHEf{AI@n}}}CFl}\",[]]A`[\"{AGMbCFl}\",[]]Ce[\"{{{AHEf{C@bAICf}}{KOl{LAl}}}{{AHEf{C@bAICf}}}}\",[]]Ce[\"{{{AHEf{C@bAICh}}{KOl{LAl}}}{{AHEf{C@bAICh}}}}\",[]]Aa[\"{ABFjAIDn}\",[]]Aa[\"{ABFlAIDn}\",[]]Aa[\"{ABFnAIDn}\",[]]n[\"{ObCFl}\",[]]Bn[\"{{{AHEf{C@bBBCl}}CFl}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}CFl}{{AHEf{C@bBBCn}}}}\",[]]A`[\"{AGInCFl}\",[]]Bo[\"{{{AHEf{C@bBBCl}}ABBf}{{AHEf{C@bBBCl}}}}\",[]]Bo[\"{{{AHEf{C@bBBCn}}ABBf}{{AHEf{C@bBBCn}}}}\",[]]A`[\"{AGInKOl}\",[]]k[\"{{}}\",[]]0000000000Bh[\"{ABCbc{}}\",[\"IntoFuture::IntoFuture\"]]1A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]n[\"{ObKL`}\",[]]Ao[\"{{AHEfc}{{O@h{c}}}{}}\",[\"S\"]]0C`[\"{{{AHEf{C@bAIA`}}O@bCG`CKh{KOl{CG`}}}HEh}\",[]]Bn[\"{{{AHEf{C@bMf}}O@bCG`CKh{KOl{CG`}}}HEh}\",[]]C`[\"{{{AHEf{C@bAIAb}}O@bCG`CKh{KOl{CG`}}}HEh}\",[]]Bn[\"{{{AHEf{C@bMh}}O@bCG`CKh{KOl{CG`}}}HEh}\",[]]Bk[\"{{{AHEf{C@bJDj}}O@b{AHEf{BBBd}}}HEh}\",[]]A`[\"{BDBjKOl}\",[]]A`[\"{BDBlKOl}\",[]]Bn[\"{{{AHEf{LB`}}{AHEf{LB`}}}{{KOl{ABDj}}}}\",[]]Bn[\"{{{AHEf{L@`}}{AHEf{L@`}}}{{KOl{ABDj}}}}\",[]]C`[\"{{{AHEf{ANBl}}{AHEf{ANBl}}}{{KOl{ABDj}}}}\",[]]C`[\"{{{AHEf{AOCj}}{AHEf{AOCj}}}{{KOl{ABDj}}}}\",[]]Bn[\"{{{AHEf{CJd}}{AHEf{CJd}}}{{KOl{ABDj}}}}\",[]]Bl[\"{{{AHEf{Nb}}{AHEf{Nb}}}{{KOl{ABDj}}}}\",[]]C`[\"{{{AHEf{ABEh}}{AHEf{ABEh}}}{{KOl{ABDj}}}}\",[]]Bn[\"{{{AHEf{LBb}}{AHEf{LBb}}}{{KOl{ABDj}}}}\",[]]C`[\"{{{AHEf{BDCl}}{AHEf{BDCl}}}{{KOl{ABDj}}}}\",[]]Bn[\"{{{AHEf{L@b}}{AHEf{L@b}}}{{KOl{ABDj}}}}\",[]]C`[\"{{{AHEf{AGMh}}{AHEf{AGMh}}}{{KOl{ABDj}}}}\",[]]C`[\"{{{AHEf{B@Gj}}{AHEf{B@Gj}}}{{KOl{ABDj}}}}\",[]]C`[\"{{{AHEf{AIFd}}{AHEf{AIFd}}}{{KOl{ABDj}}}}\",[]]C`[\"{{{AHEf{ANBn}}{AHEf{ANBn}}}{{KOl{ABDj}}}}\",[]]C`[\"{{{AHEf{BCHd}}{AHEf{BCHd}}}{{KOl{ABDj}}}}\",[]]C`[\"{{{AHEf{BDCh}}{AHEf{BDCh}}}{{KOl{ABDj}}}}\",[]]C`[\"{{{AHEf{AOCl}}{AHEf{AOCl}}}{{KOl{ABDj}}}}\",[]]Bn[\"{{{AHEf{CJf}}{AHEf{CJf}}}{{KOl{ABDj}}}}\",[]]Bl[\"{{{AHEf{Nd}}{AHEf{Nd}}}{{KOl{ABDj}}}}\",[]]C`[\"{{{AHEf{ABEj}}{AHEf{ABEj}}}{{KOl{ABDj}}}}\",[]]Be[\"{{{AHEf{AI@l}}}{{ABE`{AGLl}}}}\",[]]Bk[\"{{{AHEf{AI@n}}L@b}{{L@f{AICdAI@d}}}}\",[]]Df[\"{{{AHEf{C@bAICl}}{AHEf{C@b{KIl{IOn}}}}}{{L@f{{KOl{KL`}}AGMf}}}}\",[]]Cc[\"{{{AHEf{C@bAICl}}KL`}{{L@f{{A@`{Ll}}BAEf}}}}\",[]]Bb[\"{{{AHEf{C@bAI@n}}ABEj}AICn}\",[]]Aj[\"{{{AHEf{AGKd}}}KL`}\",[]]Aj[\"{{{AHEf{AGKf}}}KL`}\",[]]Bb[\"{{{AHEf{C@bAI@n}}ABEj}AIDj}\",[]]Bn[\"{{{AHEf{C@bBBCl}}CG`}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}CG`}{{AHEf{C@bBBCn}}}}\",[]]A`[\"{AGInKOl}\",[]]A`[\"{B@DhKOl}\",[]]A`[\"{B@DjKOl}\",[]]Ce[\"{{{AHEf{C@bAOGj}}{Md{AIEd}}}{{AHEf{C@bAOGj}}}}\",[]]Ce[\"{{{AHEf{C@bAOGn}}{Md{AIEf}}}{{AHEf{C@bAOGn}}}}\",[]]Ce[\"{{{AHEf{C@bAOAl}}{Md{AIEl}}}{{AHEf{C@bAOAl}}}}\",[]]Ce[\"{{{AHEf{C@bAOB`}}{Md{AIEn}}}{{AHEf{C@bAOB`}}}}\",[]]n[\"{ObCG`}\",[]]Bn[\"{{{AHEf{C@bBDHh}}CFl}{{AHEf{C@bBDHh}}}}\",[]]Bn[\"{{{AHEf{C@bBDHj}}CFl}{{AHEf{C@bBDHj}}}}\",[]]Ak[\"{{{AHEf{AI@l}}}BEGn}\",[]]Ai[\"{{{Md{AOGl}}}AOGj}\",[]]Ai[\"{{{Md{AOH`}}}AOGn}\",[]]Aj[\"{{BBBnc}BBBnABF`}\",[\"\"]]Aj[\"{{BBC`c}BBC`ABF`}\",[\"\"]]Bm[\"{{{AHEf{C@bAIDh}}IOn}{{L@f{HEhAIFb}}}}\",[]]n[\"{{}KO`}\",[]]o[\"{NNnNNn}\",[]]0000Bd[\"{{{AHEf{BEMh}}}{{KOl{ABBf}}}}\",[]]Bd[\"{{{AHEf{BEMj}}}{{KOl{ABBf}}}}\",[]]Bd[\"{{{AHEf{BFMh}}}{{KOl{ABBf}}}}\",[]]Bd[\"{{{AHEf{BFNb}}}{{KOl{ABBf}}}}\",[]]Bd[\"{{{AHEf{AI@l}}}{{KOl{BBAb}}}}\",[]]Ck[\"{{{AHEf{ABBj}}AOAlAIDn{AHEf{CFj}}}{{L@f{AI@hAOCd}}}}\",[]]Ak[\"{{{AHEf{AOGb}}}ABBf}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]An[\"{{AICjCKh}{{L@j{KL`}}}}\",[]]Cj[\"{{{AHEf{OBh}}}{{KOl{{KKn{{Ml{AGKd}}{Ml{AGLb}}}}}}}}\",[]]Ck[\"{{{AHEf{AIEh}}}{{KOl{{KKn{{Ml{AGKd}}{Ml{AGLb}}}}}}}}\",[]]Cj[\"{{{AHEf{OBj}}}{{KOl{{KKn{{Ml{AGKf}}{Ml{AGLd}}}}}}}}\",[]]Ck[\"{{{AHEf{AIEj}}}{{KOl{{KKn{{Ml{AGKf}}{Ml{AGLd}}}}}}}}\",[]]A`[\"{BEFbA@`}\",[]]A`[\"{BGAfA@`}\",[]]A`[\"{BEFdA@`}\",[]]A`[\"{BGAhA@`}\",[]]An[\"{{{AHEf{C@bBEMh}}}AOCj}\",[]]An[\"{{{AHEf{C@bBEMj}}}AOCl}\",[]]An[\"{{{AHEf{C@bBFMh}}}AOCl}\",[]]An[\"{{{AHEf{C@bBFNb}}}AOCl}\",[]]Ak[\"{{{AHEf{BAFb}}}AOEh}\",[]]Ba[\"{{{AHEf{C@bAI@n}}BBAh}HEh}\",[]]Bo[\"{{{AHEf{C@bABBl}}BCHdB@Db}{{KOl{BBAh}}}}\",[]]B`[\"{{{AHEf{OBh}}AOCjCJd}CHl}\",[]]Bn[\"{{{AHEf{AOGl}}CFnAOCj}{{L@f{CHlBDKf}}}}\",[]]Ba[\"{{{AHEf{AIEh}}AOCjCJd}CHl}\",[]]Bn[\"{{{AHEf{BCKb}}CFnAOCj}{{L@f{CHlBDKf}}}}\",[]]B`[\"{{{AHEf{OBj}}AOClCJf}CHn}\",[]]Bn[\"{{{AHEf{AOH`}}CFnAOCl}{{L@f{CHnBDKl}}}}\",[]]Ba[\"{{{AHEf{AIEj}}AOClCJf}CHn}\",[]]Bn[\"{{{AHEf{BCKd}}CFnAOCl}{{L@f{CHnBDKl}}}}\",[]]Bn[\"{{{AHEf{BFHd}}CFnAOCh}{{L@f{CHnBDKl}}}}\",[]]Aj[\"{{}{{OBb{c}}}{}}\",[\"T\"]]0A`[\"{AGM`CG`}\",[]]A`[\"{AGMbCG`}\",[]]Bn[\"{{{AHEf{C@bAOGj}}KL`}{{AHEf{C@bAOGj}}}}\",[]]Bn[\"{{{AHEf{C@bAOGn}}KL`}{{AHEf{C@bAOGn}}}}\",[]]Aj[\"{{{AHEf{BAA`}}}CKh}\",[]]0Aj[\"{{{AHEf{BAFb}}}CKh}\",[]]Bk[\"{{{AHEf{AI@n}}L@b}{{L@f{AGJlAI@d}}}}\",[]]Cb[\"{{{AHEf{BBBd}}{AHEf{AICj}}CG`}{{Md{ANEb}}}}\",[]]Ak[\"{{{AHEf{ABCb}}}AOCj}\",[]]Ak[\"{{{AHEf{ABCd}}}AOCl}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]Bf[\"{{{AHEf{C@bAI@n}}}{{KOl{O@b}}}}\",[]]Ce[\"{{{AHEf{C@bBBCl}}{Md{ANCj}}}{{AHEf{C@bBBCl}}}}\",[]]Ce[\"{{{AHEf{C@bBBCn}}{Md{ANCl}}}{{AHEf{C@bBBCn}}}}\",[]]43A`[\"{ABFjKOl}\",[]]A`[\"{ABFlKOl}\",[]]A`[\"{ABFnKOl}\",[]]Ai[\"{{{AHEf{OCf}}}KL`}\",[]]A`[\"{AGM`CG`}\",[]]A`[\"{AGMbCG`}\",[]]Bo[\"{{{AHEf{C@bBFMh}}ABBf}{{AHEf{C@bBFMh}}}}\",[]]Bo[\"{{{AHEf{C@bBFNb}}ABBf}{{AHEf{C@bBFNb}}}}\",[]]Bj[\"{{{AHEf{AIDh}}BNl}{{L@f{HEhAOBh}}}}\",[]]Bm[\"{{{AHEf{C@bAIDj}}BNl}{{L@f{HEhAOC`}}}}\",[]]>=A`[\"{JDhANE`}\",[]]A`[\"{ANAdJDh}\",[]]Bi[\"{{{Md{AOAh}}JDl}{{L@f{BCJnBEG`}}}}\",[]]Bi[\"{{{Md{AOGf}}JDl}{{L@f{BCKbBEG`}}}}\",[]]Bi[\"{{{Md{AOAh}}JDl}{{L@f{BCK`BEGb}}}}\",[]]Bi[\"{{{Md{AOGf}}JDl}{{L@f{BCKdBEGb}}}}\",[]]Co[\"{{{AHEf{C@bAIDh}}{AHEf{C@b{KIl{IOn}}}}}{{L@f{OD`AIFb}}}}\",[]]Co[\"{{{AHEf{C@bAIDj}}{AHEf{C@b{KIl{IOn}}}}}{{L@f{ODbAIFd}}}}\",[]]Aj[\"{{{AHEf{AI@n}}}CKh}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]A`[\"{AGInCKh}\",[]]E`[\"{{{AHEf{C@bBABh}}c}{{AHEf{C@bBABh}}}{{A`{}{{KOn{{Ml{BEMh}}}}}}CJbCJh}}\",[\"F\"]]E`[\"{{{AHEf{C@bBABj}}c}{{AHEf{C@bBABj}}}{{A`{}{{KOn{{Ml{BEMj}}}}}}CJbCJh}}\",[\"F\"]]Bi[\"{{{AHEf{BAA`}}}{{Oj{{Ml{AGMl}}}}}}\",[]]n[\"{ObCG`}\",[]]Aa[\"{ANE`AGKh}\",[]]76Aa[\"{AIDbABCb}\",[]]Aa[\"{AIDdABCd}\",[]]Cf[\"{{{AHEf{C@bBDFj}}{KOl{ABBf}}}{{AHEf{C@bBDFj}}}}\",[]]Cf[\"{{{AHEf{C@bBDFl}}{KOl{ABBf}}}{{AHEf{C@bBDFl}}}}\",[]]9;:An[\"{{{AHEf{ANEb}}ABBf}HEh}\",[]]B`[\"{{{AHEf{C@bAIA`}}CFl}HEh}\",[]]An[\"{{{AHEf{C@bMf}}CFl}HEh}\",[]]Ao[\"{{{AHEf{C@bJ@h}}CFl}HEh}\",[]]Ao[\"{{{AHEf{C@bO@n}}CFl}HEh}\",[]]B`[\"{{{AHEf{C@bAIAb}}CFl}HEh}\",[]]An[\"{{{AHEf{C@bMh}}CFl}HEh}\",[]]Ao[\"{{{AHEf{C@bJ@j}}CFl}HEh}\",[]]Ao[\"{{{AHEf{C@bOA`}}CFl}HEh}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]10Bh[\"{{{AHEf{AI@l}}}{{KOl{{Ml{Mb}}}}}}\",[]]Bg[\"{{{AHEf{OBh}}}{{KOl{{Ml{Mb}}}}}}\",[]]Bh[\"{{{AHEf{AIEh}}}{{KOl{{Ml{Mb}}}}}}\",[]]Bg[\"{{{AHEf{OBj}}}{{KOl{{Ml{Mb}}}}}}\",[]]Bh[\"{{{AHEf{AIEj}}}{{KOl{{Ml{Mb}}}}}}\",[]]Dm[\"{{{AHEf{C@bAICl}}{AHEf{C@bNO`}}{AHEf{C@bOB`}}}{{CIn{{L@f{HEhAGMf}}}}}}\",[]]Da[\"{{{Oj{{AHEf{C@bAIDh}}}}{AHEf{C@bNO`}}}{{CIn{{L@h{HEh}}}}}}\",[]]Db[\"{{{AHEf{C@bAI@n}}O@bAOEh{AHEf{C@b{A@`{Ll}}}}}{{KOl{ABFl}}}}\",[]]Cf[\"{{{AHEf{C@bBBCl}}{AHEf{CFj}}}{{AHEf{C@bBBCl}}}}\",[]]Cf[\"{{{AHEf{C@bBBCn}}{AHEf{CFj}}}{{AHEf{C@bBBCn}}}}\",[]]Ak[\"{{{AHEf{AI@l}}}AOFj}\",[]]Bj[\"{{{AHEf{AI@l}}IOn}{{L@f{HEhBDCj}}}}\",[]]Bn[\"{{{AHEf{C@bBBCl}}CKh}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}CKh}{{AHEf{C@bBBCn}}}}\",[]]Ai[\"{{{AHEf{NOl}}}KL`}\",[]]Ai[\"{{{AHEf{NOn}}}KL`}\",[]]De[\"{{{Md{AOAn}}CFn{AHEf{CFj}}{AHEf{BE@b}}}{{L@f{{Ml{OBh}}AOCd}}}}\",[]]Bo[\"{{{Md{AOGl}}CFn{AHEf{BE@b}}}{{Ml{OBh}}}}\",[]]De[\"{{{Md{BCJn}}CFn{AHEf{CFj}}{AHEf{BE@b}}}{{L@f{{Ml{OBh}}AOCd}}}}\",[]]Bo[\"{{{Md{BCKb}}CFn{AHEf{BE@b}}}{{Ml{OBh}}}}\",[]]De[\"{{{Md{AOBb}}CFn{AHEf{CFj}}{AHEf{BE@b}}}{{L@f{{Ml{OBj}}AOCf}}}}\",[]]Bo[\"{{{Md{AOH`}}CFn{AHEf{BE@b}}}{{Ml{OBj}}}}\",[]]De[\"{{{Md{BCK`}}CFn{AHEf{CFj}}{AHEf{BE@b}}}{{L@f{{Ml{OBj}}AOCf}}}}\",[]]Bo[\"{{{Md{BCKd}}CFn{AHEf{BE@b}}}{{Ml{OBj}}}}\",[]]De[\"{{{Md{BFHb}}CFn{AHEf{CFj}}{AHEf{BE@b}}}{{L@f{{Ml{OBj}}AOCb}}}}\",[]]Bo[\"{{{Md{BFHd}}CFn{AHEf{BE@b}}}{{Ml{OBj}}}}\",[]]Bm[\"{KL`{{L@f{{KKn{KL`{KOl{KL`}}}}BDOf}}}}\",[]]0Cc[\"{{{AHEf{BDBn}}{AHEf{{KIl{Ll}}}}}{{Ml{NNd}}}}\",[]]Cc[\"{{{AHEf{BDC`}}{AHEf{{KIl{Ll}}}}}{{Ml{NNf}}}}\",[]]Bm[\"{{AIDnBAAbOb}{{L@j{{KKn{ABBhAI@j}}}}}}\",[]]Bd[\"{{{AHEf{AI@n}}}{{AHEf{OBj}}}}\",[]]A`[\"{ANAdJDh}\",[]]Cb[\"{{{AHEf{ANCj}}CJdAIDnAOCjO@b}{{KOl{AICf}}}}\",[]]Cb[\"{{{AHEf{BBBn}}CJdAIDnAOCjO@b}{{KOl{AICf}}}}\",[]]Cb[\"{{{AHEf{ANCl}}CJfAIDnAOClO@b}{{KOl{AICh}}}}\",[]]Cb[\"{{{AHEf{BBC`}}CJfAIDnAOClO@b}{{KOl{AICh}}}}\",[]]B`[\"{{{AHEf{C@bAI@n}}O@b}HEh}\",[]]Bo[\"{{{AHEf{C@bAI@h}}}{{L@f{{Ml{Mb}}BBAb}}}}\",[]]Bh[\"{{{AHEf{AI@l}}}{{KOl{{Ml{Mb}}}}}}\",[]]Bg[\"{{{AHEf{OBh}}}{{KOl{{Ml{Mb}}}}}}\",[]]Bh[\"{{{AHEf{AIEh}}}{{KOl{{Ml{Mb}}}}}}\",[]]Bg[\"{{{AHEf{OBj}}}{{KOl{{Ml{Mb}}}}}}\",[]]Bh[\"{{{AHEf{AIEj}}}{{KOl{{Ml{Mb}}}}}}\",[]]o[\"{AIAhLl}\",[]]o[\"{AIAjLl}\",[]]Ai[\"{{{AHEf{CFj}}}CKh}\",[]]Aj[\"{{{AHEf{AIA`}}}CG`}\",[]]Ah[\"{{{AHEf{Mf}}}CG`}\",[]]Bn[\"{{{AHEf{C@bAGIh}}CG`}{{AHEf{C@bAGIh}}}}\",[]]Ai[\"{{{AHEf{J@h}}}CG`}\",[]]Bn[\"{{{AHEf{C@bANBb}}CG`}{{AHEf{C@bANBb}}}}\",[]]Ai[\"{{{AHEf{O@n}}}CG`}\",[]]Bn[\"{{{AHEf{C@bB@Fj}}CG`}{{AHEf{C@bB@Fj}}}}\",[]]Aj[\"{{{AHEf{AIAb}}}CG`}\",[]]Ah[\"{{{AHEf{Mh}}}CG`}\",[]]Bn[\"{{{AHEf{C@bAGIj}}CG`}{{AHEf{C@bAGIj}}}}\",[]]Ai[\"{{{AHEf{J@j}}}CG`}\",[]]Bn[\"{{{AHEf{C@bANBd}}CG`}{{AHEf{C@bANBd}}}}\",[]]Ai[\"{{{AHEf{OA`}}}CG`}\",[]]Bn[\"{{{AHEf{C@bB@Fl}}CG`}{{AHEf{C@bB@Fl}}}}\",[]]Aa[\"{AIClBE@d}\",[]]Ai[\"{{{AHEf{OBh}}}CKh}\",[]]Aj[\"{{{AHEf{AIEh}}}CKh}\",[]]Aj[\"{{{AHEf{AI@n}}}CKh}\",[]]Ai[\"{{{AHEf{OBj}}}CKh}\",[]]Aj[\"{{{AHEf{AIEj}}}CKh}\",[]]Co[\"{{{AHEf{OBh}}AOCj{AHEf{{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}}CKh}\",[]]D`[\"{{{AHEf{AIEh}}AOCj{AHEf{{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}}CKh}\",[]]Co[\"{{{AHEf{OBj}}AOCl{AHEf{{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}}CKh}\",[]]D`[\"{{{AHEf{AIEj}}AOCl{AHEf{{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}}CKh}\",[]]Bn[\"{{{AHEf{C@bBDHh}}CFl}{{AHEf{C@bBDHh}}}}\",[]]Bn[\"{{{AHEf{C@bBDHj}}CFl}{{AHEf{C@bBDHj}}}}\",[]]Cc[\"{{{AHEf{C@bOBh}}}{{KOl{{O@d{{Ml{AGLb}}}}}}}}\",[]]Cd[\"{{{AHEf{C@bAIEh}}}{{KOl{{O@d{{Ml{AGLb}}}}}}}}\",[]]Cc[\"{{{AHEf{C@bOBj}}}{{KOl{{O@f{{Ml{AGLd}}}}}}}}\",[]]Cd[\"{{{AHEf{C@bAIEj}}}{{KOl{{O@f{{Ml{AGLd}}}}}}}}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]De[\"{{{AHEf{C@bBBCl}}c{AHEf{CFj}}}{{AHEf{C@bBBCl}}}{{IOl{CId}}}}\",[\"\"]]De[\"{{{AHEf{C@bBBCn}}c{AHEf{CFj}}}{{AHEf{C@bBBCn}}}{{IOl{CId}}}}\",[\"\"]]Ci[\"{{{AHEf{C@bOBh}}{AHEf{{KIl{Ll}}}}}{{L@f{CKhJAl}}}}\",[]]Cj[\"{{{AHEf{C@bAIEh}}{AHEf{{KIl{Ll}}}}}{{L@f{CKhJAl}}}}\",[]]Cj[\"{{{AHEf{C@bOBj}}{AHEf{{KIl{Ll}}}}}{{L@f{CKhBAF`}}}}\",[]]Ck[\"{{{AHEf{C@bAIEj}}{AHEf{{KIl{Ll}}}}}{{L@f{CKhBAF`}}}}\",[]]n[\"{ObCKh}\",[]]Bn[\"{{{AHEf{C@bBBCl}}LB`}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}LBb}{{AHEf{C@bBBCn}}}}\",[]]A`[\"{AGInKOl}\",[]]Ca[\"{{{AHEf{C@bAICl}}}{{L@f{{KOl{LB`}}AID`}}}}\",[]]Ca[\"{{{AHEf{C@bBE@d}}}{{L@f{{KOl{LB`}}AID`}}}}\",[]]Ca[\"{{{AHEf{C@bAICn}}}{{L@f{{KOl{LBb}}AOC`}}}}\",[]]Ak[\"{{{AHEf{AI@h}}}AIDn}\",[]]Ak[\"{{{AHEf{AI@l}}}AIDn}\",[]]Ak[\"{{{AHEf{ABCb}}}AIDn}\",[]]Bg[\"{{{AHEf{CIf}}}{{L@f{AIDnAHOn}}}}\",[]]Ak[\"{{{AHEf{ABCd}}}AIDn}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]B`[\"{{LB`c}LB`{{CHj{LB`}}}}\",[\"\"]]B`[\"{{L@`c}L@`{{CHj{L@`}}}}\",[\"\"]]B`[\"{{LBbc}LBb{{CHj{LBb}}}}\",[\"\"]]B`[\"{{L@bc}L@b{{CHj{L@b}}}}\",[\"\"]]20Ae[\"{{ABBfCG`}ACn}\",[]]Bn[\"{{{AHEf{C@bBBCl}}ACl}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}ACl}{{AHEf{C@bBBCn}}}}\",[]]A`[\"{JDhABBf}\",[]]B`[\"{{}{{AHEf{{KIl{BDOd}}}}}}\",[]]Ao[\"{{AHEf{A@Cj{C@bLl}}}HEh}\",[]]0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Am[\"{{}{{KOl{{Md{OBf}}}}}}\",[]]Bn[\"{{{AHEf{C@bBABh}}CKh}{{AHEf{C@bBABh}}}}\",[]]Bn[\"{{{AHEf{C@bBABj}}CKh}{{AHEf{C@bBABj}}}}\",[]]Aj[\"{{{AHEf{AGLb}}}CG`}\",[]]Aj[\"{{{AHEf{AGLd}}}CG`}\",[]]A`[\"{ABEdKOl}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]10Bf[\"{{{AHEf{CFj}}}{{L@j{CG`B@Fn}}}}\",[]]Bm[\"{{{AHEf{ABBj}}{Ml{BAA`}}}{{L@h{HEh}}}}\",[]]Ai[\"{AI@j{{L@j{HEh}}}}\",[]]Ci[\"{{{AHEf{OBd}}BAAbB@Gh{AHEf{Ob}}}{{KKn{AIDnABBh}}}}\",[]]Cb[\"{{{AHEf{C@bAI@n}}L@bAICd}{{L@f{AICdBDJh}}}}\",[]]Am[\"{{{AHEf{AI@l}}CG`}HEh}\",[]]B`[\"{{{AHEf{C@bAI@n}}CG`}HEh}\",[]]A`[\"{BAEhCKh}\",[]]B`[\"{{{AHEf{C@bJDh}}BAEj}HEh}\",[]]Aa[\"{ANE`AGKh}\",[]]Bm[\"{{{AHEf{OBf}}AGMn}{{L@h{{Ml{BAA`}}}}}}\",[]]Bn[\"{{{AHEf{AOHh}}AGMn}{{L@h{{Ml{BAA`}}}}}}\",[]]Bn[\"{{{AHEf{ANDf}}AGMn}{{L@h{{Ml{BAA`}}}}}}\",[]]Ci[\"{{{AHEf{C@bOBh}}{AHEf{C@b{A@`{Ll}}}}}{{KOl{CHl}}}}\",[]]Cj[\"{{{AHEf{C@bAIEh}}{AHEf{C@b{A@`{Ll}}}}}{{KOl{CHl}}}}\",[]]Ci[\"{{{AHEf{C@bOBj}}{AHEf{C@b{A@`{Ll}}}}}{{KOl{CHn}}}}\",[]]Cj[\"{{{AHEf{C@bAIEj}}{AHEf{C@b{A@`{Ll}}}}}{{KOl{CHn}}}}\",[]]Cb[\"{{{AHEf{ABFb}}HEfAIEbABBf}{{L@f{HEhBBCf}}}}\",[]]Cb[\"{{{AHEf{B@Cb}}HEfAIEbABBf}{{L@f{HEhBBCf}}}}\",[]]Cb[\"{{{AHEf{AOEj}}HEfAIEbABBf}{{L@f{HEhBBCf}}}}\",[]]Cb[\"{{{AHEf{ABFd}}HEfAIEbABBf}{{L@f{HEhBBCh}}}}\",[]]Cb[\"{{{AHEf{B@Cd}}HEfAIEbABBf}{{L@f{HEhBBCh}}}}\",[]]Cb[\"{{{AHEf{AOEl}}HEfAIEbABBf}{{L@f{HEhBBCh}}}}\",[]]Al[\"{{}{{L@f{AOAjBEGb}}}}\",[]]Bm[\"{{{AHEf{AI@l}}L@`}{{KOl{{Ml{AIA`}}}}}}\",[]]Bo[\"{{{AHEf{AI@n}}L@b}{{KOl{{AHEf{AIAb}}}}}}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]Aj[\"{{{AHEf{AI@l}}}HEh}\",[]]Am[\"{{{AHEf{C@bAI@n}}}HEh}\",[]]Be[\"{{{AHEf{AHOd}}}{{L@f{ObJB`}}}}\",[]]Bg[\"{{{AHEf{AHOd}}}{{L@f{AGInJB`}}}}\",[]]Be[\"{{{AHEf{AHOd}}}{{L@f{OdJB`}}}}\",[]]Be[\"{{{AHEf{AHOd}}}{{L@f{OfJB`}}}}\",[]]Be[\"{{{AHEf{AHOd}}}{{L@f{N`JB`}}}}\",[]]Bg[\"{{{AHEf{AHOd}}}{{L@f{ABAhJB`}}}}\",[]]Ak[\"{{{AHEf{BAFb}}}AOEh}\",[]]Cf[\"{{{AHEf{C@bBBCl}}{KOl{ANBl}}}{{AHEf{C@bBBCl}}}}\",[]]Cf[\"{{{AHEf{C@bBBCn}}{KOl{ANBn}}}{{AHEf{C@bBBCn}}}}\",[]]<;Aj[\"{{{AHEf{ABBj}}}KL`}\",[]]Aj[\"{{{AHEf{ABBl}}}KL`}\",[]]Bc[\"{{{AHEf{AI@l}}AIDnAICb}ABDh}\",[]]Co[\"{{{AHEf{C@bAI@n}}AGJlAICdO@b}{{L@f{{KKn{L@bCKh}}AGLh}}}}\",[]]Bn[\"{{{AHEf{C@bBBCl}}CFn}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}CFn}{{AHEf{C@bBBCn}}}}\",[]]Ai[\"{AI@j{{L@j{HEh}}}}\",[]]Bg[\"{{{AHEf{BAFb}}AIF`}{{L@h{KL`}}}}\",[]]A`[\"{AGInCG`}\",[]]10Ai[\"{{{AHEf{Ob}}}BBCj}\",[]]Ce[\"{{{AHEf{C@bAOGj}}{Md{BBCl}}}{{AHEf{C@bAOGj}}}}\",[]]Ce[\"{{{AHEf{C@bAOAl}}{Md{BBCl}}}{{AHEf{C@bAOAl}}}}\",[]]Ce[\"{{{AHEf{C@bAOGn}}{Md{BBCn}}}{{AHEf{C@bAOGn}}}}\",[]]Ce[\"{{{AHEf{C@bAOB`}}{Md{BBCn}}}{{AHEf{C@bAOB`}}}}\",[]]Aa[\"{AOGjBENj}\",[]]Aa[\"{AOGnBENl}\",[]]Bj[\"{{{A@`{BAAb}}B@Gh}{{L@f{AOGjJBb}}}}\",[]]Bj[\"{{{A@`{BAAb}}B@Gh}{{L@f{AOGnJBd}}}}\",[]]Co[\"{{{AHEf{C@bAIDh}}{AHEf{C@b{KIl{IOn}}}}}{{L@f{HEhAIFb}}}}\",[]]A`[\"{AGM`CG`}\",[]]A`[\"{AGMbCG`}\",[]]A`[\"{BDBjCG`}\",[]]A`[\"{BDBlCG`}\",[]]Bc[\"{{{AHEf{AI@l}}}{{KOl{KL`}}}}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]10Aj[\"{{{AHEf{AGJd}}}KL`}\",[]]Be[\"{{{AHEf{ABBj}}{KOl{AOGj}}}HEh}\",[]]Bn[\"{{{AHEf{C@bABBl}}{KOl{{Md{AOGn}}}}}HEh}\",[]]Bd[\"{{{AHEf{BDOd}}}{{KOl{B@Gf}}}}\",[]]o[\"{{}AHEf}\",[]]n[\"{{}NNn}\",[]]0000Bn[\"{{{AHEf{C@bBBCl}}KL`}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}KL`}{{AHEf{C@bBBCn}}}}\",[]]o[\"{CG`LB`}\",[]]o[\"{CG`LBb}\",[]]A`[\"{B@DdCG`}\",[]]Bo[\"{{{AHEf{C@bBABh}}ABBf}{{AHEf{C@bBABh}}}}\",[]]Bo[\"{{{AHEf{C@bBABj}}ABBf}{{AHEf{C@bBABj}}}}\",[]]Ae[\"{{KL`CG`}B@Cb}\",[]]Ae[\"{{KL`CG`}B@Cd}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]6Bg[\"{{{AHEf{AI@n}}L@b}{{KOl{AICd}}}}\",[]]An[\"{{{AHEf{AI@l}}IOn}AOGd}\",[]]Am[\"{{{AHEf{AI@l}}LB`}HEh}\",[]]B`[\"{{{AHEf{C@bAI@n}}LBb}HEh}\",[]]Ce[\"{{{AHEf{C@bBABh}}{A@`{CFn}}}{{AHEf{C@bBABh}}}}\",[]]Ce[\"{{{AHEf{C@bBABj}}{A@`{CFn}}}{{AHEf{C@bBABj}}}}\",[]]Fg[\"{{{AHEf{BFBf}}{AHEf{BAAb}}{AHEf{{KIl{BAAb}}}}{AHEf{AIDl}}{AHEf{{KIl{Ll}}}}ABGd}{{L@f{BDJbJAf}}}}\",[]]Af[\"{{BBBnO@b}BBBn}\",[]]Af[\"{{BBC`O@b}BBC`}\",[]]?Al[\"{AOHj{{L@f{CJdJAn}}}}\",[]]Ak[\"{AOHj{{L@f{NbJAn}}}}\",[]]Am[\"{AOHj{{L@f{ABEhJAn}}}}\",[]]Ai[\"{AOHj{{L@l{CJf}}}}\",[]]Ah[\"{AOHj{{L@l{Nd}}}}\",[]]Aj[\"{AOHj{{L@l{ABEj}}}}\",[]]o[\"{NNnNNn}\",[]]Bo[\"{{{AHEf{C@bBDHh}}ABBf}{{AHEf{C@bBDHh}}}}\",[]]Bo[\"{{{AHEf{C@bBDHj}}ABBf}{{AHEf{C@bBDHj}}}}\",[]]Bb[\"{{{AHEf{OBh}}}{{KOl{CKh}}}}\",[]]Bc[\"{{{AHEf{AIEh}}}{{KOl{CKh}}}}\",[]]Bb[\"{{{AHEf{OBj}}}{{KOl{CKh}}}}\",[]]Bc[\"{{{AHEf{AIEj}}}{{KOl{CKh}}}}\",[]]Bg[\"{{{AHEf{AI@l}}}{{L@f{HEhBBAb}}}}\",[]]Cf[\"{{{AHEf{C@bBBCl}}{KOl{ABBf}}}{{AHEf{C@bBBCl}}}}\",[]]Cf[\"{{{AHEf{C@bBBCn}}{KOl{ABBf}}}{{AHEf{C@bBBCn}}}}\",[]]A`[\"{AGM`CG`}\",[]]A`[\"{AGMbCG`}\",[]]Bl[\"{{{AHEf{C@bAIA`}}O@bO@bCKhCKhCG`}HEh}\",[]]Bj[\"{{{AHEf{C@bMf}}O@bO@bCKhCKhCG`}HEh}\",[]]Bk[\"{{{AHEf{C@bJ@h}}O@bO@bCKhCKhCG`}HEh}\",[]]Bk[\"{{{AHEf{C@bO@n}}O@bO@bCKhCKhCG`}HEh}\",[]]Bl[\"{{{AHEf{C@bAIAb}}O@bO@bCKhCKhCG`}HEh}\",[]]Bj[\"{{{AHEf{C@bMh}}O@bO@bCKhCKhCG`}HEh}\",[]]Bk[\"{{{AHEf{C@bJ@j}}O@bO@bCKhCKhCG`}HEh}\",[]]Bk[\"{{{AHEf{C@bOA`}}O@bO@bCKhCKhCG`}HEh}\",[]]A`[\"{B@DdCG`}\",[]]Ak[\"{{{AHEf{OCf}}Nd}CG`}\",[]]Bi[\"{{{AHEf{C@bAIDf}}CG`}{{L@j{HEh}}}}\",[]]<;A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]10Cf[\"{{{AHEf{C@bBBCl}}{KOl{BDFj}}}{{AHEf{C@bBBCl}}}}\",[]]Cf[\"{{{AHEf{C@bBBCn}}{KOl{BDFl}}}{{AHEf{C@bBBCn}}}}\",[]]A`[\"{AGM`CG`}\",[]]A`[\"{AGMbCG`}\",[]]Bh[\"{{{AHEf{C@bAHOd}}}{{L@f{ObJB`}}}}\",[]]Bj[\"{{{AHEf{C@bAHOd}}}{{L@f{AGInJB`}}}}\",[]]Bh[\"{{{AHEf{C@bAHOd}}}{{L@f{OdJB`}}}}\",[]]Bh[\"{{{AHEf{C@bAHOd}}}{{L@f{OfJB`}}}}\",[]]Bh[\"{{{AHEf{C@bAHOd}}}{{L@f{N`JB`}}}}\",[]]Bj[\"{{{AHEf{C@bAHOd}}}{{L@f{ABAhJB`}}}}\",[]]Cc[\"{{{Md{AI@j}}CG`CKh}{{L@j{{KKn{BAEjBAEj}}}}}}\",[]]Bn[\"{{{AHEf{C@bAOGj}}CG`}{{AHEf{C@bAOGj}}}}\",[]]Bn[\"{{{AHEf{C@bAOGn}}CG`}{{AHEf{C@bAOGn}}}}\",[]]Aj[\"{{{AHEf{AI@l}}}CKh}\",[]]Ak[\"{{{AHEf{BAA`}}}AOEh}\",[]]0Ci[\"{{{AHEf{C@bBABh}}CFl}{{L@f{{AHEf{C@bBABh}}ANAh}}}}\",[]]Ci[\"{{{AHEf{C@bBABj}}CFl}{{L@f{{AHEf{C@bBABj}}ANAj}}}}\",[]]A`[\"{AGInCFl}\",[]]Cf[\"{{{AHEf{C@bBBCl}}{KOl{BDHh}}}{{AHEf{C@bBBCl}}}}\",[]]Cf[\"{{{AHEf{C@bBBCn}}{KOl{BDHj}}}{{AHEf{C@bBBCn}}}}\",[]]Bg[\"{{{AHEf{C@bAI@n}}}{{KOl{B@Db}}}}\",[]]Cf[\"{{{AHEf{C@bAOGj}}{KOl{AOHb}}}{{AHEf{C@bAOGj}}}}\",[]]Cf[\"{{{AHEf{C@bAOGn}}{KOl{AOHb}}}{{AHEf{C@bAOGn}}}}\",[]]Cf[\"{{{AHEf{C@bAOGj}}{KOl{AOHd}}}{{AHEf{C@bAOGj}}}}\",[]]Cf[\"{{{AHEf{C@bAOGn}}{KOl{AOHd}}}{{AHEf{C@bAOGn}}}}\",[]]Cc[\"{{}{{L@f{{KKn{{A@`{BAAb}}B@Gh}}{Ml{JAd}}}}}}\",[]]Bn[\"{{{AHEf{C@bBDFj}}LB`}{{AHEf{C@bBDFj}}}}\",[]]Bn[\"{{{AHEf{C@bBDFl}}LBb}{{AHEf{C@bBDFl}}}}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]Bo[\"{{{AHEf{C@bAOGj}}ABBf}{{AHEf{C@bAOGj}}}}\",[]]Bo[\"{{{AHEf{C@bAOGn}}ABBf}{{AHEf{C@bAOGn}}}}\",[]]Ci[\"{{{AHEf{CIf}}{KOl{ABBf}}}{{L@f{{KOl{ABBf}}AHOn}}}}\",[]]Bj[\"{{{AHEf{BAFb}}AIF`KL`}{{L@h{HEh}}}}\",[]]054Bm[\"{{{AHEf{OBh}}}{{L@f{{KOl{BE@b}}JAl}}}}\",[]]Bn[\"{{{AHEf{AIEh}}}{{L@f{{KOl{BE@b}}JAl}}}}\",[]]Bn[\"{{{AHEf{OBj}}}{{L@f{{KOl{BE@b}}BAF`}}}}\",[]]Bo[\"{{{AHEf{AIEj}}}{{L@f{{KOl{BE@b}}BAF`}}}}\",[]]Aj[\"{{{AHEf{AGLb}}}CG`}\",[]]Aj[\"{{{AHEf{AGLd}}}CG`}\",[]]Aj[\"{{{AHEf{ABBl}}}CG`}\",[]]Bf[\"{{{AHEf{AGJl}}{AHEf{AGJl}}}CKh}\",[]]Am[\"{{{AHEf{C@bAI@n}}}HEh}\",[]]Bn[\"{{{AHEf{C@bB@Fj}}ACl}{{AHEf{C@bB@Fj}}}}\",[]]Bn[\"{{{AHEf{C@bB@Fl}}ACl}{{AHEf{C@bB@Fl}}}}\",[]]Ak[\"{{{AHEf{AGMl}}}AOEh}\",[]]0Bd[\"{{{AHEf{AI@l}}}{{ABE`{JBn}}}}\",[]]Cb[\"{{{AHEf{AI@n}}L@b}{{L@f{{KOl{AIDn}}AI@d}}}}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]Bn[\"{{{AHEf{C@bBBCl}}LB`}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}LBb}{{AHEf{C@bBBCn}}}}\",[]]A`[\"{AGInKOl}\",[]]Bo[\"{{{AHEf{AGIn}}{AHEf{CFj}}}{{L@h{BBCj}}}}\",[]]Em[\"{{{AHEf{AI@l}}{AHEf{C@b{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}}{{L@f{HEhBFIn}}}}\",[]]El[\"{{{AHEf{OBh}}{AHEf{C@b{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}}{{L@f{HEhBFIn}}}}\",[]]Em[\"{{{AHEf{AIEh}}{AHEf{C@b{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}}{{L@f{HEhBFIn}}}}\",[]]El[\"{{{AHEf{OBj}}{AHEf{C@b{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}}{{L@f{HEhBFJ`}}}}\",[]]Em[\"{{{AHEf{AIEj}}{AHEf{C@b{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}{AHEf{{KIl{Ll}}}}}{{L@f{HEhBFJ`}}}}\",[]]Am[\"{{{AHEf{C@bAGJb}}}HEh}\",[]]0Al[\"{{{AHEf{AI@n}}Nd}CG`}\",[]]Bl[\"{{{AHEf{AI@l}}}{{ABEb{{KOl{AIDn}}}}}}\",[]]Bd[\"{{{AHEf{CIf}}}{{L@f{`AHOn}}}}\",[]]>=E`[\"{{{AHEf{BFBf}}{AHEf{{KIl{Ll}}}}{AHEf{BAAb}}{AHEf{BEMl}}}{{L@f{BFE`JAf}}}}\",[]]0o[\"{{}AOAl}\",[]]o[\"{{}AOB`}\",[]]Bf[\"{{{Md{B@H`}}}{{L@f{AOAlBEGl}}}}\",[]]Bf[\"{{{Md{B@H`}}}{{L@f{AOB`BEGl}}}}\",[]]Bn[\"{{{AHEf{C@bBDFj}}LB`}{{AHEf{C@bBDFj}}}}\",[]]Bn[\"{{{AHEf{C@bBDFl}}LBb}{{AHEf{C@bBDFl}}}}\",[]]o[\"{NNnNNn}\",[]]0000Aj[\"{{{AHEf{AI@n}}}CKh}\",[]]0Ci[\"{{{AHEf{CIf}}{KOl{ABBf}}}{{L@f{{KOl{ABBf}}AHOn}}}}\",[]]Cc[\"{{{AHEf{C@bOb}}{AHEf{AHOd}}}{{L@f{HEhJB`}}}}\",[]]Ce[\"{{{AHEf{C@bAGIn}}{AHEf{AHOd}}}{{L@f{HEhJB`}}}}\",[]]Cc[\"{{{AHEf{C@bOd}}{AHEf{AHOd}}}{{L@f{HEhJB`}}}}\",[]]Cc[\"{{{AHEf{C@bOf}}{AHEf{AHOd}}}{{L@f{HEhJB`}}}}\",[]]Cc[\"{{{AHEf{C@bN`}}{AHEf{AHOd}}}{{L@f{HEhJB`}}}}\",[]]Ce[\"{{{AHEf{C@bABAh}}{AHEf{AHOd}}}{{L@f{HEhJB`}}}}\",[]]Bo[\"{{{AHEf{C@bAOGj}}BENj}{{AHEf{C@bAOGj}}}}\",[]]Bo[\"{{{AHEf{C@bAOGn}}BENl}{{AHEf{C@bAOGn}}}}\",[]]Aj[\"{{{AHEf{BABh}}}CG`}\",[]]Aj[\"{{{AHEf{BABj}}}CG`}\",[]]Cc[\"{{{AHEf{C@bAOAl}}{Md{A`}}}{{AHEf{C@bAOAl}}}}\",[]]Cc[\"{{{AHEf{C@bAOB`}}{Md{A`}}}{{AHEf{C@bAOB`}}}}\",[]]Cj[\"{{BABh{KOl{AOGj}}{Ml{BAA`}}{Md{OBf}}}{{L@h{ABBj}}}}\",[]]Aj[\"{{{AHEf{ABCb}}}CKh}\",[]]Aj[\"{{{AHEf{ABCd}}}CKh}\",[]]Bn[\"{{{AHEf{C@bAI@n}}O@bAIBh}{{L@f{HEh`}}}}\",[]]Bd[\"{{{AHEf{BFBf}}}{{A@`{BBCb}}}}\",[]]Bj[\"{{{AHEf{AI@l}}AIDn}{{L@f{HEhJBh}}}}\",[]]Bm[\"{{{AHEf{C@bAI@n}}AIDn}{{L@f{HEhJBh}}}}\",[]]Bn[\"{{{AHEf{C@bBBCl}}KL`}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}KL`}{{AHEf{C@bBBCn}}}}\",[]]Bl[\"{{}{{L@f{{KKn{BAAbBDId}}{Ml{JAd}}}}}}\",[]]Ai[\"{AI@j{{L@j{HEh}}}}\",[]]A`[\"{AIAhCG`}\",[]]A`[\"{AIAjCG`}\",[]]Ba[\"{{{AHEf{C@bABBj}}AOAl}HEh}\",[]]D`[\"{{{AHEf{C@bAI@n}}L@b{KOl{ABBf}}}{{L@f{{KOl{ABBf}}AI@d}}}}\",[]]o[\"{{}AHEf}\",[]]Aj[\"{{{AHEf{AI@l}}}KL`}\",[]]Bn[\"{{{AHEf{C@bAOGj}}CG`}{{AHEf{C@bAOGj}}}}\",[]]Bn[\"{{{AHEf{C@bAOGn}}CG`}{{AHEf{C@bAOGn}}}}\",[]]Bn[\"{{{AHEf{C@bBBCl}}LB`}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}LBb}{{AHEf{C@bBBCn}}}}\",[]]:Bb[\"{{{AHEf{C@bAI@n}}NdLBb}HEh}\",[]]Ak[\"{{}{{L@f{AOAlJBb}}}}\",[]]Ak[\"{{}{{L@f{AOB`JBd}}}}\",[]]Bn[\"{{{AHEf{C@bBBCl}}CKh}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}CKh}{{AHEf{C@bBBCn}}}}\",[]]65Cf[\"{{{AHEf{C@bOb}}{AHEf{C@bAHOd}}}{{L@f{HEhJB`}}}}\",[]]Ch[\"{{{AHEf{C@bAGIn}}{AHEf{C@bAHOd}}}{{L@f{HEhJB`}}}}\",[]]Cf[\"{{{AHEf{C@bOd}}{AHEf{C@bAHOd}}}{{L@f{HEhJB`}}}}\",[]]Cf[\"{{{AHEf{C@bOf}}{AHEf{C@bAHOd}}}{{L@f{HEhJB`}}}}\",[]]Cf[\"{{{AHEf{C@bN`}}{AHEf{C@bAHOd}}}{{L@f{HEhJB`}}}}\",[]]Ch[\"{{{AHEf{C@bABAh}}{AHEf{C@bAHOd}}}{{L@f{HEhJB`}}}}\",[]]Bi[\"{{{AHEf{C@bAGJl}}{AHEf{AGJl}}}CKh}\",[]]n[\"{{}HEh}\",[]]Ce[\"{{{AHEf{C@bBBCl}}{KOl{KL`}}}{{AHEf{C@bBBCl}}}}\",[]]Ce[\"{{{AHEf{C@bBBCn}}{KOl{KL`}}}{{AHEf{C@bBBCn}}}}\",[]]Bn[\"{{{AHEf{AI@l}}}{{L@f{{A@`{AIDn}}JBh}}}}\",[]]Cd[\"{{{AHEf{C@bAI@n}}O@b}{{L@f{{A@`{AIDn}}JBh}}}}\",[]]Am[\"{{{AHEf{C@bAIA`}}}HEh}\",[]]Al[\"{{{AHEf{C@bJ@h}}}HEh}\",[]]Am[\"{{{AHEf{C@bAIAb}}}HEh}\",[]]Al[\"{{{AHEf{C@bJ@j}}}HEh}\",[]]Ai[\"{AI@j{{L@j{HEh}}}}\",[]]Bj[\"{{{AHEf{AI@l}}AIDn}{{L@f{HEhJBh}}}}\",[]]Bm[\"{{{AHEf{C@bAI@n}}AIDn}{{L@f{HEhJBh}}}}\",[]]D`[\"{{{AHEf{C@bAI@n}}L@b{KOl{ABBf}}}{{L@f{{KOl{ABBf}}AI@d}}}}\",[]]Ce[\"{{{AHEf{C@bBBCl}}{Md{BDBf}}}{{AHEf{C@bBBCl}}}}\",[]]Ce[\"{{{AHEf{C@bBBCn}}{Md{BDBh}}}{{AHEf{C@bBBCn}}}}\",[]]Cf[\"{{{AHEf{C@bBBCl}}{KOl{ABBf}}}{{AHEf{C@bBBCl}}}}\",[]]Cf[\"{{{AHEf{C@bBBCn}}{KOl{ABBf}}}{{AHEf{C@bBBCn}}}}\",[]]Aa[\"{B@DhAIBf}\",[]]Aa[\"{B@DjAIBf}\",[]]9Bn[\"{{{AHEf{C@bBBCl}}CKh}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}CKh}{{AHEf{C@bBBCn}}}}\",[]]Am[\"{{{AHEf{AI@l}}LB`}HEh}\",[]]o[\"{NNnNNn}\",[]]Bn[\"{{{AHEf{C@bBBCl}}CFn}{{AHEf{C@bBBCl}}}}\",[]]Bn[\"{{{AHEf{C@bBBCn}}CFn}{{AHEf{C@bBBCn}}}}\",[]]3109854Bn[\"{{{AHEf{AI@l}}}{{L@f{{A@`{AIDn}}JBh}}}}\",[]]Bn[\"{{{AHEf{AI@n}}}{{L@f{{A@`{AIDn}}JBh}}}}\",[]]10A`[\"{BE@bKOl}\",[]]Bm[\"{{{AHEf{C@bBBCl}}Ll}{{AHEf{C@bBBCl}}}}\",[]]Bm[\"{{{AHEf{C@bBBCn}}Ll}{{AHEf{C@bBBCn}}}}\",[]]o[\"{{}AIBh}\",[]]") \ No newline at end of file diff --git a/pr/299/docs/search.index/generic_inverted_index/770510825e02.js b/pr/299/docs/search.index/generic_inverted_index/770510825e02.js new file mode 100644 index 000000000..ec0c0e131 --- /dev/null +++ b/pr/299/docs/search.index/generic_inverted_index/770510825e02.js @@ -0,0 +1 @@ +rb_("QU1mADswAAABAACmAQIAFgPVACwE0AA7MAAAAQAAJQItAOYBFAD8AQgABgIBAAoCAAAMAgAADgICABQCAAAWAgYAIgIBACUCBgAtAgAAMAIEADYCAwA7AgAAPQIBAEACAQBDAgIARwICAEsCDABZAgUAaQIAAGsCDwB+AgAAgQIDAIYCEQCZAgIAnQIEAKMCAwCoAgAAqgIAAKwCBACyAgcAuwIDAMACAADCAgUAyQIEAM8CBADVAgUA3QIAAOcCCADxAg8Afwl2AN4K1QBJDwAA7A8BADswAAABAACrAQwAFQbVAPYJLAAmCg4ANgoEADwKGgBZCgAAXAoSAHIKHQCRCgUAmAokAL8KAADCChsAOzAAAAEAAPMACwAkAgAAhQIAAPACAACtBxMAkAoAAJcKAAC9CgAAwAoAAAoO1ABODwEAhQ8BADswAAABAABVAQUATQkAAL4KAADBCgAAtQxMAfkQBQA7MAAAAQAABQACAAgHAgAMBwIAOzAAAAEAACgABQABAxQA4gUAAAMHBAAQBwsASAkBADswAAABAAAJAAIADQQAALQLCAA7MAAAAQAACwABAP0DCwAAAuUQAADmEAAAAAJKEAAASxAAAAACmQEAAAsHAABDagAAADswAAABAADVAAsA9gksACYKDgA2CgQAPAoaAFkKAABcChIAcgodAJEKBQCYCiQAvwoAAMIKGwA=") \ No newline at end of file diff --git a/pr/299/docs/search.index/name/8263a00c4c8a.js b/pr/299/docs/search.index/name/8263a00c4c8a.js new file mode 100644 index 000000000..495d3832d --- /dev/null +++ b/pr/299/docs/search.index/name/8263a00c4c8a.js @@ -0,0 +1 @@ +rd_("b()b->bBi0bCe00bFnbIdbOkb[]beq0000000000000000000000000000000000000000000000000000000000000000000000000bid0000000000000brtbtybu8cAck0cAnycArccBbr00cBoxcBufcClicDir0cErrcMAX000cNotcOpt00cOrdcPincUni0cVeccbbrccmp0000000000000000000cdir000cecn000cf32cf64cfmt00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cgetci32cimpcios0clen000clog0cmincmutcnew00000000000000000000000000000000000cnot0cnow000000crtt000crun0cstrcu16cu32cu64cudpdArgsdAsFddDropdEct000dEct100dFromdHashdIntodKeys0dLong0dPath00dPing0dPolldReaddSenddSide0dSyncdVZipdZERO0dacks0daddr0dbooldbulkdcodedcwnd0ddataddrop0000dfrom000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dhash00000000000000000000dinto000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dmain0000dnextdopen00dpath0dperfdping000dpoll0000000000000dqlogdread00drecv00dseal0dsend00dsent0dside0dsign0dsize00dsmoldspindstop00dtake00000du128dunitdvzip0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eAsRefeByteseChunk0eCloneeCloseeCubic00eDebugeDerefeError00000000000eEvent00eOther0eRangeeReset000eRetry0eShort0eSizedeStats0eSuiteeWriteearrayebenchebuild00000000ebytes00000ecauseeclone0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eclose000ecubicederef0eerror0eframe0eindex0eis_ce0elocal0eparse0epaths0eprint00ereset00eretry0esliceespawn00estats00000000etitle0etokenetokioetupleeusizeewrite00fAcceptfAckEcn0fAes128fAes256fBackupfBorrowfBufMutfChunksfClient00fClosed00fCrypto0fCursorfFuturefHasherfIpAddrfOpenBifOpened000fOptionfOutputfPathId0fResult00000fServer00fStream00fStringfTypeIdfVarInt0faccept000fborrow000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fchunks0fcipherfclient0000fclosedfcoding0fconfig0fcrypto0000000000fdecode00000000000fdst_ipfencode00000000000ffinish00fhandlefheader0fignore0finsert00000fkeylogfoffset0fon_ack0000000fpacket00frandomfreason00000frebindfrefuse0fremote00frustls00000fserver0000fsharedfsource00000000fsrc_ip00fstatus0fstream0fstridefudp_rx0fudp_tx0fvarintfverify0fwindow0000000gAeadKey0gBlocked00gCommandgContextgDefaultgDisplaygFromStrgGeneral0gHmacKey0gInitialgInstantgKeyPair0gLayeredgMaxData0gNewReno00gOpenUnigPadding0gPathAck0gPathBufgReadBufgRequestgRuntime0gSession0gStopped0000gStreamsgToOwnedgTooLonggTryFromgTryIntogWritten0gZeroRttgcid_len000gclientsgcommand00000gcompare0000000000000000000gconnect0gdecrypt000gdefault000000000000000000000000000000000000000000000000000gdrainedgdst_cid0000000gencrypt000giroh_hp0gis_0rtt0gmetrics0000000gmin_mtu0gon_sent000gopen_bigpath_idgpointergruntimegsrc_cid0000gstopped0gstreams000gtag_len0gtype_id000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000gupgradegversion00000hAcceptBihBytesMuthChacha20hCommandshDatagram00hDerefMuthDisabled0hDurationhEndpoint00hFinished0hIncoming0hLayerExthLongTypehMAX_SIZE0hNO_ERROR0hNewToken0hOnClosedhOpenPathhOrderinghReadable0hReceiver0hRecvMetahResponsehStreamId0hTimedOut0hToStringhTokenLog0hTooLarge0hTransmit00hUdpStats0hUnixTimehWritable0hcong_alghcontentshdatagram0hdurationhendpoint0hfinalizehframe_rx0hframe_tx0hfrom_buf0hfrom_env0hfrom_keyhfrom_strhfrom_u320hfrom_u640hgot_type000hgroup_id0000hhas_0rtthincominghinterval0hinto_any0000000his_alivehlifetime0hlocal_ip0000hmax_data0hmax_sizehnew_renohon_byteshopen_unihpriority0hprotocol0hqlog_dirhresponsehrng_seed0hssthresh0hto_owned0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_from000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_into000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_poll00000000htry_sendhvalidate000iAbandoned0iAcceptUniiArbitraryiAsyncRead0iAvailable00iBbrConfig0iBorrowMutiCommonOptiConnectediCriterioniDatagramsiDecodableiEncodableiFormatteriFourTupleiFrameType0iHandshakeiHeaderKey0iHistogramiMalformediMaxPathId0iMultipathiPacketKey0iPartialEqiPathErroriPathEvent00iPathStats0iReadError00iTryFutureiUdpSenderiUdpSocketiValueEnumiaccept_biiarbitrary0000000ias_rustlsiassemblericlone_box0000000idatagrams000ideref_mut0ifrom_bits00iinitiator0iinto_0rttiis_client0iis_closediis_server0imay_retry0imigration0inew_timer00inew_token0ion_closediopen_path0ipath_acks0iping_pathipoll_read00ipoll_recvipoll_sendireach_out0ireferenceireset_key0iretry_tag0000isize_hint00000istable_idito_string0000000000000000000000000000000000000000000000000000000itoken_key0itoken_positransport00iwait_idleiwrite_alljArgMatchesjAsyncTimerjAsyncWrite0jBATCH_SIZEjClosedPath000jComparablejConnectingjConnection00jController0jEquivalent0jFrameStats0jIntoFuturejInvalidCid0jIoSliceMutjMaybeFramejNamedGroupjNonZeroU32jOtherErrorjPartialOrdjPathAckEcn0jPathStatus0jQlogConfig0jRecvStream00jResetErrorjRetryError0jSendStream00jServerNamejSocketAddrjSubcommandjSystemTimejTimeSource0jTlsSession0jTokenStore0jUdpSockRefjWriteError0jaccept_unijallow_spin0jborrow_mut000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jclone_into0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jclose_pathjcongestion000jconn_statsjconnection0jequivalent00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jerror_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_future000000000000kmax_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_chunklAckFrequency0lAddressAddedlClientConfig00000lClosedStream000000lConnectError00lConnectionId00lDecryptError0lEcnCodepoint00lEncryptError0lIllegalValuelImmediateAck0lLOCALHOST_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_streamslsent_packets0lset_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_session000000000mtry_size_hint0nAddIpv4Address0nAddIpv6Address0nAddressRemovednAsyncUdpSocketnCertificateDernClosePathError0nCommandFactorynConnectionLost000000nEndpointConfig0nFromArgMatchesnINTERNAL_ERROR0nIncomingFuturenInvalidMessage00nMaxStreamsBidi0nNO_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_uninit0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000odefault_runtimeogrease_quic_bit0ointegrity_limit0ointerface_indexomax_stream_data0omax_streams_uni0oparse_byte_sizeorebind_abstractosend_unreliableoserver_endpointoset_path_statusoset_send_window0oshould_transmitostream_finishedothroughput_histowrap_udp_socket00owrite_handshake000A`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_instant0AcCongestionAlgorithmAcMaxRecursionReachedAcPathNewConnectionId0AcPathStatusAvailable0AcRemoteCidsExhaustedAcTransportParametersAcUnorderedRecvStreamAcUnsupportedNameType0AcWrongConnectionSideAcaccepted_handshakesAcarbitrary_take_rest00000Acaugment_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_EXCEEDED0AfExtensionNotNegotiatedAfFailedToGetCurrentTime0AfFailedToGetRandomBytes0AfMultipathNotNegotiated0AfPathRetireConnectionId0AfProtectedInitialHeaderAfSkipServerVerificationAfbuild_transport_configAfexport_keying_material0000Afget_into_many_segmentsAfget_loop_many_segmentsAfmax_concurrent_streamsAfobserved_external_addr0Afpath_new_connection_id0Afverify_tls12_signatureAfverify_tls13_signatureAfwith_platform_verifier0Afwith_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/299/docs/search.index/normalizedName/479d9533fdd1.js b/pr/299/docs/search.index/normalizedName/479d9533fdd1.js new file mode 100644 index 000000000..bb1d7331c --- /dev/null +++ b/pr/299/docs/search.index/normalizedName/479d9533fdd1.js @@ -0,0 +1 @@ +rd_("b()b->bbi0bce00bfnbidbokb[]beq000000000000000000000000000000000000000000000000000000000000000000000000033333333333333brtbtybu8cack0canycarccbbr00cboxcbufcclicdir0cerrcmax000cnotcopt00cordcpincuni0cvecA`[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\"]3Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]Be[6,\"iroh_quinn_udp\",\"iroh_quinn_udp\"]Bm[8,\"core::num::nonzero\",\"core::num::nonzero\"]9C`[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\"]Bc[6,\"rustls::error\",\"rustls::error\"]A`[5,\"iroh_quinn\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]A`[6,\"iroh_quinn\"]=Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]3Cl[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\"]8Cg[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\"]=Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::transport\"]Bi[5,\"core::num::error\",\"core::num::error\"]Bi[5,\"iroh_quinn\",\"iroh_quinn::connection\"]9A`[5,\"iroh_quinn\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]Bc[6,\"rustls::error\",\"rustls::error\"]Cc[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]11B`[5,\"iroh_quinn::crypto::rustls\"]Di[5,\"iroh_quinn_proto::crypto::rustls\",\"iroh_quinn_proto::crypto::rustls\"]105Cm[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token_memory_cache\"]Bh[15,\"iroh_quinn_proto::ProtectedHeader\"]f[3,\"\"]Am[10,\"iroh_quinn::congestion\"]Db[10,\"iroh_quinn_proto::congestion\",\"iroh_quinn_proto::congestion\"]Al[5,\"iroh_quinn::congestion\"]Da[5,\"iroh_quinn_proto::congestion\",\"iroh_quinn_proto::congestion\"]Ai[10,\"iroh_quinn::crypto\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]Af[2,\"iroh_quinn_proto\"]Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]Bi[6,\"iroh_quinn\",\"iroh_quinn::connection\"]D`[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::datagrams\"]:A`[5,\"iroh_quinn\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::transport\"]Cb[10,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]21Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]Be[5,\"rustls::verify\",\"rustls::verify\"]Ae[10,\"rustls::verify\"]Do[6,\"iroh_quinn_proto::connection::paths\",\"iroh_quinn_proto::connection::paths\"]Cl[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::paths\"]7Af[5,\"iroh_quinn_proto\"]Bj[15,\"iroh_quinn_proto::PacketDecodeError\"]Ah[5,\"iroh_quinn::crypto\"]Ci[5,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]?Aa[6,\"perf\",\"perf\"]Ak[5,\"arbitrary\",\"arbitrary\"]Ee[5,\"iroh_quinn_proto::transport_parameters\",\"iroh_quinn_proto::transport_parameters\"]Bj[5,\"iroh_quinn\",\"iroh_quinn::recv_stream\"]Bo[2,\"iroh_quinn_proto::connection::send_buffer\"]Be[2,\"iroh_quinn_proto::bench_exports\"]Bh[15,\"iroh_quinn::crypto::rustls::Error\"]Bn[15,\"iroh_quinn_proto::crypto::rustls::Error\"]B`[5,\"iroh_quinn::crypto::rustls\"]Di[5,\"iroh_quinn_proto::crypto::rustls\",\"iroh_quinn_proto::crypto::rustls\"]Be[6,\"rustls::suites\",\"rustls::suites\"]A`[5,\"iroh_quinn\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::varint\"]Be[6,\"rustls::webpki\",\"rustls::webpki\"]Bi[5,\"iroh_quinn\",\"iroh_quinn::connection\"]Af[2,\"iroh_quinn_proto\"]Aa[10,\"iroh_quinn\"]Ci[10,\"iroh_quinn_proto\",\"iroh_quinn_proto::cid_generator\"]Be[5,\"rustls::verify\",\"rustls::verify\"]7Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]Ao[5,\"certificate\",\"certificate\"]Bc[6,\"rustls::error\",\"rustls::error\"]4Bm[5,\"perf::noprotection\",\"perf::noprotection\"]01Ah[5,\"iroh_quinn::crypto\"]Ci[5,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]Ce[5,\"rustls::crypto::cipher\",\"rustls::crypto::cipher\"]Ch[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::cid_generator\"]07Bh[15,\"iroh_quinn::crypto::rustls::Error\"]Bn[15,\"iroh_quinn_proto::crypto::rustls::Error\"]") \ No newline at end of file diff --git a/pr/299/docs/search.index/root.js b/pr/299/docs/search.index/root.js new file mode 100644 index 000000000..2c773c13b --- /dev/null +++ b/pr/299/docs/search.index/root.js @@ -0,0 +1 @@ +rr_('{"normalizedName":{"I":"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","N":"ACJj","E":"OjAAAAAAAAA=","H":"R52VM/3R"},"crateNames":{"N":"j","E":"OjAAAAAAAAA=","H":"ijgfzyvU"},"name":{"N":"ACJj","E":"OjAAAAAAAAA=","H":"gmOgDEyK"},"path":{"N":"B@j","E":"OzAAAAEAAJ8RsAACAAQACQAAAAsAWQBmAAEAbAAAAHIABAB9AAEAgQAbAJ8A1QB3AQgAgQEyALgBAAC8AQUAyAEAAMsBAgDPAQAA1QEFAN0BFwL2AwAA+AMRAAsEAgAPBAMAFAQKACAECQAsBNAABQUAABcFAQAaBQMAHwUAACEFAAApBYoAtQUBALgFAAC6BQgAxAUIAM4FAgDUBQcA4AUCAOQFBADsBQIA8AUDAPsFAQAHBgUAEQbcAO8GBAD1BgAA9wYAAPoGOgA2Bw4ARwcDAEwHAwBRBxQAZwcJAHMHAgB7BwEAfwcAAIQHAQCIBwAAigcDAJYHAwCcBwAAoQdmAAkIEgAeCAYAJgjhAAoJAAAMCQEAEAkBABcJAAAcCQUAJgkAACoJAAAtCQEAMgkBADsJBQBDCRgAXQkUAHMJTgLDCwAAyQsBAM8LAADYCwAA3AsDAOkLAADwCwkA+wsHAAQMJQArDEgAdQwDAH0MAAB/DAEAlgwBALIMUAEFDgIACg7eAOoOEgD+DgcABw8AABIPAQAYDwMAHg8DACMPAQAqDwMALw8/AHAPAgB1DxUAkQ8BAJQPAgCeDwEAow8HAK8PBAC2DwAAxQ8xAPgPHwAaEAMAIhAAACUQAQAoEAIALBAHADgQAQA9EAEAQxADAEgQAABKEAIAThA4AI0QBQCWEAEAmhAFAKMQBQCtEAEAsRAhANQQMAAGEQAAChEAAA8RAQASEQEAIBEBACMRAwAoEZIAvBEAAL8RAQDCEQQAyBEKANQRAQDcEQEA3xETAPQRHQAYEgEAHhIRADESAgA2EgMAOxIHAEUSAwBKEgUAVBIBAFkSAABbEgEAXhIWAHYSAgB7EgQAghIfAKQSCwCyEgQAuRIAAL8SJQDmEgQA7hIFAPYSGgATExIAKBMXAEITCgBQEwUAVxMTAGwTAwBxExcAjBMdAA==","H":"GaiDujQT"},"entry":{"N":"ABJf","E":"OjAAAAEAAAAAAAMBEAAAAAAAAQAHAAgACgBlAGgAaQBtAG4AdwB7AHwAfwCAAJ0AngB1AXYBgAG0AbUBtgG3AbkBugG7AcIBwwHEAccByQHOAdAB0wHUAdsB9QMKBA4EEwQfBCoEKwT9BP4EAQUCBQYFBwUIBQkFCgULBQwFDQUOBQ8FFAUZBR4FIAUiBSUFJgUnBbQFtwW5BcMFzQXRBdIF0wXcBd0F3gXfBekF6gXvBfQF9QX2BfcF+QX6Bf0F/gUBBgIGAwYEBgUGDQYOBu4G9Ab2BvgG+QY1B0UHRgdLB1AHZgd2B3cHeAd5B3oHgAeDB44HjweQB5EHlQebB50HngcICBwIHQglCAkJDwkSCRMJFgkaCSQJJQknCSgJLwk5CToJQQlCCVwJcgnCC8ULxgvHC8gLzQvQC9QL2wviC+QL5QvsC+4L7wv6CwMMKgx0DHkMewx8DH4MggyEDIkMigyNDI4MkAyRDJIMkwyUDJUMmAycDKIMpQymDKcMqAwDDgQOCA4JDgoPDQ8XDyUPKQ9vD3MPdA+LD4wPmA+bD6sPrg+1D7cPug+7D8APwQ/ED/cPHhAnECsQNhA6EDsQPxBHEEkQiBCJEIsQjBCVEJkQohAOERQRGBEZERwRHxEnEb4RxxHTEd4RGhJJElASURJSElgSdRJ6EqISoxKwErESuhK9EuUS7RImEycTTRNWE4oTixM=","H":"UQ/Achj8"},"desc":{"N":"FIk","E":"OzAAAAEAAA4NfQEAAAEABwABAAoASgBjAAAAZQAEAGwAAwB3AAEAegACAH8AFQCdANYAdQEBAIABAgCiAQQAqgEBAK0BAACyAQkAwgECAMcBAADJAQAAzAECANABAADTAQEA1wEBANsBAQDhAQQA+wEAAAUCAAAIAgEACwIAAA0CAAARAgIAFQIAAB0CBAAkAgAALAIAAC4CAQA1AgAAOgIAADwCAAA/AgAAQgIAAEYCAABKAgAAWAIAAF8CCQBqAgAAewICAH8CAQCFAgAAmAIAAJwCAACiAgAApwIAAKkCAACrAgAAsQIAALoCAAC/AgAAwQIAAMgCAADOAgAA1AIAANsCAQDeAggA8AIAAAEDFADsAwQA9QMAAPcDAAD5AwAA+wMAAP0DCwAKBAAADgQAABAEAAATBAAAHAQAAB8EAAAlBAEAKATWAAEFAQAFBQoAFAUAABkFAAAeBQAAIAUAACIFBgAqBQIALgUDADkFdgC0BQMAuQUAAMIFAQDFBQIAzQUAAM8FBQDZBQAA3AUDAOQFAwDpBQEA7wUAAPIFBQD5BQEA/QUBAAEGBAAKBgAADAYCABUG1QDuBgEA8QYBAPQGAAD2BgAA+AYBAPwGAAAABwAAAwcHAAwHAQAQBwoAHgcAACUHAQAoBwEALgcCADIHAwBFBwIASQcAAEsHAQBOBwsAXwcBAGYHAABqBwIAbgcCAHYHBACABwAAgwcCAIgHAACKBwcAlQcAAJsHAACdBwEApAcAAKcHGQDHBzAA+QcBAAgIAAANCAIAEQgCABcIAAAZCAAAHAgBACUIAAAnCAAAKwjVAAkJBAAPCQAAEgkBABYJAAAaCQAAIAkBACQJAQAnCQEALwkAADkJAQA+CQQATQkAAFYJBABcCQAAYAkCAGQJAgBvCQEAcgkBAH8JowAmCkgAcgpKAcELAQDFCwMAzQsBANALAADUCwAA1gsBANsLAADdCwEA4gsAAOQLAQDsCwAA7gsBAPELCQD8CwIAAAwDAAcMAQAZDAMAIAwBACMMAQAoDAIALgwAADAMBwA5DDcAdAwAAHkMAAB7DAEAfgwAAIIMAACEDAAAiQwBAI0MAQCQDAgAnAwAAKIMAAClDAMAtQxMAQMOAQAIDtYA8A4AAPYOAwD7DgIABA8BAAcPAwANDwAAEg8BABcPAAAaDwEAHg8DACMPAgAnDwIAKw8DAD4PDgBODwEAUQ8AAFMPAQBXDxMAbw8AAHMPAQCIDwEAiw8BAJgPAACbDwAApg8CAKsPAACuDwAAsA8DALUPAAC3DwAAug8BAMAPAQDEDwUAzA8BANEPAgDVDwAA1w8AAN4PAQDlDwEA6Q8EAPYPAgD7DwEAABABAAwQAwAeEAAAJRACACsQAAAvEAMANhAAADgQAwA9EAIARBABAEcQAABJEAAATRAAAE8QAgBXEAIAWxACAF8QBgBsEAAAeBABAHwQCQCIEAEAixABAJUQAACZEAAAnBABAKIQAAClEAMArBAAALUQAAC3EAAAuRAAAL4QAADAEAMAxRAAAMcQAADJEAAAzBAAAM4QAADQEAAA1RAAANgQAADaEAAA3BAAAOAQAADiEAIA6BAAAOoQAAD3EAEA/xAAAAIRAgAGEQAADhECABIRAgAYEQEAHBEAAB8RAAAnEXcApREEAKsRAACxEQEAtBEBALcRAAC5EQEAvBEAAL4RAADHEQAAzREGAN4RAADhEQEA5BECAOkRAQDtEQUA9xEBAP8RAAAEEgAAGhIAAB4SAwApEgMAMBIBAEYSAABJEgIAUBICAFQSAQBYEgAAXhIEAGwSAQB1EgEAeRIFAIYSBgCQEgAAkhIAAJkSAgCdEgIAohIBAKUSAACoEgMAsBIBALoSAAC9EgAAwxIGANkSAADcEgEA4xICAOcSAADqEgAA7RIAAAETAQAPEwEAEhMBABYTAAAYEwAAHhMDACYTAQAuEwQANhMFAEATAQBKEwAATRMAAFYTAABbEwMAZxMAAHUTBQB8EwAAghMAAIQTAQCKEwEAlBMAAJgTAAA=","H":"7igw/4ee"},"function":{"N":"A@Df","E":"OzAAAAEAAGMDVwAAAAoAZQAbAJ0AAQB1AQEAgAEAALQBIgDbAQEA9QMAAPcDAAAKBAAADgQAABMEAAAfBAAAKgQBAP0EKwC0BQAAtwUAALkFAADDBQAAzQUAANEFAwDZBQAA3AUDAOMFLQDuBgAA8QYAAPQGAgD4BgIA/gYAADUHAABFBwYATgcAAFAHAABmBwAAcQcwAAgIAQAcCAEAJQgAAAgJMwBBCQEAXAkAAHIJAADCCy0A+gsAAAMMAAAqDAAAdAwAAHkMAwB+DDMAAw4DAAgOAQDpDgAA/Q4AAAYPAAAIDxcAIg8HAC4PAABvDwAAcw8BAIgPHwCpDxsA9w8BABYQLABHEAIATRAAAIIQLgDTEAAABBEjALoRJQDzEQAADhIVADASAAAyEhMARxIWAHUSAAB5EgsAohICAKwSEgDlEgAA6BIAAOsSCgAGEwwAJhMFAD4TAwBLEwsAaxMFAIkTBAA=","H":"qLMBWFmU"},"type":{"N":"AIn","E":"OzAAAAEAAAsSkgACAAQACQAAAAsAWQBmAAEAbAAAAHIABQB7AAAAfQABAIAAHACfANUAdgEJAIEBMgC4AQEAuwEIAMgBAADLAQIAzwEAANQBBgDcAU0CLATQAAEFAAAFBQIACwUBABQFAAAXBQEAGgUDAB8FAAAhBQAAKAWkAM4FDwDgBQIA5AUFAOwFAgDwBQMA+wUBAAcGBQARBl8BcwcCAHgHBAB/BwAAhAcBAIgHAACKBwMAlgcDAJsHAwChB3oAHQjqAAoJAAAMCQEADwkCABcJAAAaCQAAHAkFACYJAAAqCQAALQkBADIJAQA7CYYCwwsAAMULAgDJCwEAzQsAAM8LAADYCwAA3AsDAOILAADkCwAA6QsAAOwLAADvCzoAKwxNAHsMAgB/DAEAggwAAIkMAQCNDAAAlQwCAKcMAACyDFMCBw8AABIPAQAXDwQAHg8DACMPAgAqD2AAkQ8BAJQPAgCeDwEAow8HAK8PBAC2DwAAxQ9SABoQBAAiEAAAJRABACgQAgAsEAcAOBABAD0QAQBDEEMAixAAAI0QBQCVEAIAmhAFAKMQBQCtEAEAsRBTAAYRAAAKEQAADxEBABIRAgAgEQEAIxGXALwRAAC/EQEAwhEEAMgRCgDUEQEA3BEBAN8RMgAYEgIAHhIVADYSAwA7EgcARRIDAEoSBQBREgAAVBIBAFkSAABbEgEAXhIaAHsSBACCEi0AshIEALkSAAC/EisA7hIFAPYSGgATExIAKBMXAEITCgBQEwUAVxMTAGwTAwBxExcAjBMdAA==","H":"pmzv8ehF"},"alias":{"N":"`","E":"OzAAAAEAAKkTAQAAAKkT","H":"p2IVDFMs"},"generic_inverted_index":{"N":"b","E":"OjAAAAAAAAA=","H":"dwUQgl4C"}}') \ No newline at end of file diff --git a/pr/299/docs/search.index/type/a66ceff1e845.js b/pr/299/docs/search.index/type/a66ceff1e845.js new file mode 100644 index 000000000..20cb0d7b0 --- /dev/null +++ b/pr/299/docs/search.index/type/a66ceff1e845.js @@ -0,0 +1 @@ +rd_("CAo[\"\",\"ADswAAABAAAFAAIA7AMEAHwTAAACIgcAAEAHAAA6MAAAAQAAAAAACgAQAAAAsgGzAccFdwyrEesR/xFcE2cThROUEzowAAABAAAAAAAYABAAAADhAeIB4wHkAeUB+gPGBcsF8gYeB0wHcwlZEK4R7BFxEvoSGRMaE4ETghODE4QTlxObEzswAAABAAAIAREA+AMAAMUFAAAjBwAAPwcAAE8HAAAyDAUAiw12AOEPAABaEAcAuhAAACgRdgCvEQAAsREAAC4SAAByEgAAkxIAAF8TAAA6MAAAAQAAAAAAHAAQAAAAyAXOBc8F0AUcBx0HJAclByYHJwcoBykHvgu/C8ALwQsODPwOAQ8KEHEQqhGlEuES4hJXE1sTaBOGEzswAAABAAA6ABQAAQMUABUEAAAhBAIAsAUBALMFAADJBQEA4gUAABAHCwAWCAMAvQsAACIMAABUDwAAhw8AAAsQAACAEAEAsxAAAC8SAADZEgAAWBMAAIcTAAA7MAAAAQAAVQAMAMAAPABkAQEAFAQAAMQHAADGBwAABQgAAAcIAADUDwMA4BEFAMkSAAA2EwUASRMAADswAAABAAAVAAcA/AMAAHgMAAACDgAAUA8DAKwRAACYEgcAdRMFADswAAABAAARAAgAGgQBAC0HBwAECAAABggAAPYOAgAADwAAbBAAAA0SAAADZwcAAGgHAAD8EQAAAxEEAAASBAAAJwwAAAXCBwAAwwcAAMUHAAAjDAAAaxAAAAA7MAAAAQAABQACAJkBAAAUEwQAAAABFAYAAA==\"]Bo[\"AAAAAAAAAAACRBMAAEUTAAAAAAAAAkoQAABLEAAA\",\"\"]0Bg[\"\",\"AAAFVgkAAFcJAABYCQAAWQkAAFoJAAA=\"]AIg[\"AAAAAAKKAQAAmgEAAAAABLAFAACxBQAAsRAAALIQAAAExAcAAMYHAAAFCAAABwgAADswAAABAAANAAUA4AUBAHsJAQAtDAQAeAwAAOcQAwA6MAAAAQAAAAAADAAQAAAAkgEaBBsEBAgGCAAPFBAVENkQ2hDbENwQDRIECwQAAGcHAABoBwAAbQ8AAAQRBAAAEgQAACATAAAhEwAABRAEAADDBwAAxQcAAPkOAAD7DgAAAyQMAAAlDAAA+g4AADowAAABAAAAAAAIABAAAACZAfID9AMLBxQTFRMWExcTGBMBdhIAAAEmDAAA\",\"AAAAAQMRAAAAArUFAAC2BQAAAAIHDAAACAwAAAAFLQwAAC4MAAAvDAAAMAwAADEMAAAAAAAAAALyAwAA9AMAAA==\"]ANk[\"AAAEHgIAAIACAAD7BgAA/wYAAAMJDAAACgwAAAsMAAACigEAAJoBAAA7MAAAAQAAdgABACgRdgAABrAFAACxBQAAsRAAALIQAACnEwAAqBMAAATEBwAAxgcAAAUIAAAHCAAAOzAAAAEAABIABwDgBQEAQQcAAHsJAQAtDAQAeAwAAOcQAwC2EQMAOjAAAAEAAAAAAAkAEAAAAJIBGgQbBAQIBggAD9kQ2hDbENwQBQsEAADMBQAAZwcAAGgHAABtEAAABBEEAAASBAAAIBMAACETAAAEwwcAAMUHAAD5DgAA+w4AAAQfBwAAJAwAACUMAAD6DgAAOzAAAAEAAAYAAwDyAwAA9AMAABQTBAABdhIAAAABFAYAAA==\",\"AAAAAAACtQUAALYFAAAAAgcMAAAIDAAAAW4PAAAFLQwAAC4MAAAvDAAAMAwAADEMAAAAAAAAAALyAwAA9AMAAAAAAzgHAADBEAAAwhAAAA==\"]Dc[\"\",\"AAAAADswAAABAAAHAAEAXwkHADswAAABAAAJAAIAZhAEALwQBAAAAbsQAAA=\"]AHi[\"AAAAOjAAAAEAAAAAAAcAEAAAAIcBiQGWAZgBowGkAYMPhA8EjQEAAJ8BAABXCgAAWgoAADowAAABAAAAAAAJABAAAACIAY4BjwGXAaABoQG+CsEKJBMlEzswAAABAAAMAAMAKQUHACoPAAAQEAMABncQAAB5EAAAexAAAH0QAAB/EAAALxIAADswAAABAAAVAAoAfQEBAIQBAAArDAEAcQwBAHsPAwD+DwEABRIDAMkSAABEEwEAjhMBAAFGEwAAAZIBAAAFdhAAAHgQAAB6EAAAfBAAAH4QAAAAAAAAAAABFAYAAA==\",\"AAABggEAAAIxBQAAnxEAAAAAAALwDgAA9g8AAAAAAAAAAAAAAAAE/QYAAAEHAAB1DAAAdgwAAA==\",1]Fk[\"AAAABGMFAABqBwAADQgAAMUQAAACYAkAAPwLAAABWxAAAAABFwgAAAHvAAAAAlEPAACZEgAAAS4HAAA=\",\"AAAAAWMFAAABjQEAAA==\"]Fk[\"AAAABKQFAABuBwAAEQgAAMwQAAACZAkAAAAMAAABXxAAAAFmAQAAARkIAAAAAlMPAACdEgAAATIHAAA=\",\"AAAAAaQFAAABnwEAAA==\"]AEa[\"AAAAAoUBAACRAQAAOzAAAAEAAAcAAQBfCQcAAATOBQAAzwUAANAFAACqEQAAAAABRhMAAAAAAAJKEAAASxAAAA==\",\"AAAAADswAAABAAAQAAMA8gYAAF8JBwD7CwcAOzAAAAEAAAsABABPBwAATBAAAGYQBAC8EAQAOzAAAAEAAA8ABQApBQcAGAwCALMRAgDnEQAAWxMAADowAAABAAAAAAAMABAAAAB3EHkQexB9EH8QsRCyELsQ3xDgEOEQ4hDZEgTUDwAA1Q8AANYPAADXDwAAAAAFdhAAAHgQAAB6EAAAfBAAAH4QAAAAAAAAAAABFAYAAA==\",1]Ec[\"AAAAAAAAOzAAAAEAAAUAAgAIBwIADAcCADswAAABAAAGAAIAAwcEAEgJAQADDQQAAMEFAADCBQAA\",\"\"]Dg[\"AAAAAAAAAAABOhMAAAF5EwAAAAAAAAAAAAABrAcAAA==\",\"AAAAAAAB8REAAAHHEgAA\"]Eo[\"AAAAAWEFAAABjAEAAAIlAAAAhgAAAAFcDwAAAQYDAAAC6gAAAOsAAAA=\",\"AAABlQAAAAFhBQAAAYcSAAAAAfMLAAA=\"]Ic[\"AAAAAZwFAAADngEAAKESAAAbEwAAAkwAAACTAAAABlMBAABUAQAA8wMAABMGAABpDwAAaBMAAAETAwAA\",\"AAABlgAAAAKcBQAAihIAAAAB9wsAAAAAAAAAAAAAAAAAAAKXAAAAmAAAAA==\"]Ik[\"AAAAAjkFAAAEEgAAAUwHAAAB7wYAAAGfAAAAAbMQAAABNhMAAAKsEQAAdRMAAAAAAAAAAAAAOjAAAAEAAAAAAAgAEAAAANUF7QamByYINw9ND38PThDrEA==\",\"AAAAATkFAAAAAe0RAAABwxIAAA==\"]Ck[\"AAAAAbIBAAAAAAAAATgTAAABdxMAAA==\",\"AAAAAAAB7xEAAAHFEgAA\"]Ck[\"AAAAAbMBAAAAAAAAATkTAAABeBMAAA==\",\"AAAAAAAB8BEAAAHGEgAA\"]Jk[\"AAAAAAAABMgFAADOBQAAzwUAANAFAAAAOzAAAAEAAAgAAQC0CwgAOzAAAAEAAAwAAQD8AwwABPYOAAD3DgAA+A4AAGwQAAAAAScMAAADIwwAAPkOAAD7DgAAAiQMAAD6DgAA\",\"AAAAAAABTBAAAAMYDAAAGQwAABoMAAA=\"]Mg[\"AAAAAAAAAgsSAAAMEgAAAAJzEgAAdBIAAAVBBwAAthEAALcRAAC4EQAAuREAAAACzAUAAG0QAAACEQQAABIEAAAAAR8HAAAAAAABFAYAAA==\",\"AAAAAALEBQAAShMAAAAFfxMAAKITAACjEwAApBMAAKUTAAAB2RIAAAJuDwAAgBMAAAAAAAAAAAAAAATYDwAA2Q8AANoPAADbDwAA\"]Bg[\"AAAAAAAAAAQAEQAAAREAAPsSAAD8EgAA\",\"\"]Bk[\"\",\"AAAAAf8QAAAAAAAAAAAAAAAAAAAAAAEEDwAA\"]Cc[\"AAAAAAAAAQoQAAABCxAAAA==\",\"AAAAAAACdQkAAHYJAAA=\"]FHk[\"AAAAADswAAABAAAUAAoA4QEEAPoDAADYBQAAKAgAAAUMAADcDwMA7BEAAPoSAAAZEwEAgRMDADswAAABAADcAREACQQAAA8EAAAjBwAA9QsDALUMTAFwDwAAdQ8AAOEPAAD9DwAAWhAHALoQAAAoEXYArxEAALERAAByEgAA1BIAAF8TAAA7MAAAAQAArgAPAJ8AIAD9AGYAZgENAPMDAADIBQAAEwYAAAgHAgAMBwIAHAcBAPELAwD0DgAApRIAAMMSBQBoEwAAexMAADswAAABAACdAEAAAQMUAAwEAAAWBAEAIQQCALMFAADJBQEA4gUAAAMHBAAQBwsAFAgFAAIJAQBICQEAXQkBAGgJAQAHDAEAFgwBACIMAACzDAEA8Q4BAAIPAQA4DwEAOw8BAFQPAAB2DwEAgA8BAIcPAADiDwAA8A8BAAgQAQALEAAAchABALsQAADGEAAAyBAAAMoQAADNEAAAzxAAANEQAADdEAUA7BABAO8QAgAAEQEAoBEBAP0RAQAvEgAAYxIBAGgSAQCNEgEAyhIBANoSAQDeEgEA+xIBAAMTAQAsEwEAPBMBAFgTAgBjEwMAcRMDAIcTAACVEwEAmRMBAJwTAQCgEwEApxMBADswAAABAACMACUAwAA8AGQBAQB9AQEA8QMAAA0EAAAUBAAAwQUBANoFAQDEBwAAxgcAAAUIAAAHCAAAtAsIACsMAQBxDAEA/g4BADIPAQBuDwAAew8DAP4PAQBUEAEAbhABAK0RAAD1EQEABRIDAHMSAQCUEgEAvxIBANISAQDVEgMANhMFAEQTAQBJEwAAfRMBAIATAACOEwMAnhMBADswAAABAAAwAA0A/AMMAOAFAQBBBwAAewkBAB8MAAB4DAAAAg4AAFAPAwDnEAMAthEDAJgSBwDPEgEAdRMFADowAAABAAAAAAAVABAAAAAaBBsELQcuBy8HMAcxBzIHMwc0BwQIBgj2DvcO+A4ADxQQFRBsEA0SYBOIEwcLBAAAzAUAAG0PAABtEAAA5RAAAOYQAAD8EQAAAxEEAAASBAAAJwwAADowAAABAAAAAAAJABAAAAAQBMIHwwfFByMM+Q77DkoQSxBrEAQfBwAAJAwAACUMAAD6DgAAOjAAAAEAAAAAAAcAEAAAAPID9AMLBxQTFRMWExcTGBMAASYMAAABFAYAAA==\",\"AAAAAAA7MAAAAQAA1QABALUM1QAAOjAAAAEAAAAAAFkAEAAAABYEFwQUCBUIAgkDCV0JXgloCWkJBwwIDBYMFwyzDLQM8Q7yDgIPAw84DzkPOw88D3YPdw+AD4EP8A/xDwgQCRByEHMQxhDIEMoQzRDPENEQ3RDeEOwQ7RAAEQERoBGhEf0R/hFjEmQSaBJpEo0SjhLKEssS2hLbEt4S3xL7EvwSAxMEEywTLRM8Ez0TWRNaE2MTZBNlE2YTcRNyE3MTdBOVE5YTmROaE5wTnROgE6ETpxOoEzswAAABAAAvABQAfQEBANoFAQArDAEAcQwBADIPAQB7DwMA/g8BAFQQAQBuEAEA9REBAAUSAwBzEgEAlBIBAL8SAQDSEgEA1RIDAEQTAQB9EwEAjhMDAJ4TAQAEewkAAHwJAADPEgAA0BIAAAAC5RAAAOYQAAAAAkoQAABLEAAAAALyAwAA9AMAAA==\"]Ho[\"AAAAAcMQAAAAA8UFAABNCQAAqREAADswAAABAAAMAAMAJAQFACQHBQATEwAAAAPBBwAAbhAAAG8QAAABzw8AAAAHdhAAAHgQAAB6EAAAfBAAAH4QAADlEAAA5hAAAAABwgcAAA==\",\"\"]Lk[\"AAACHwIAAH0CAAAAAo0BAACfAQAAOzAAAAEAAAsAAwCOAQEAoAEBAFoQBwA7MAAAAQAABwABACkFBwA6MAAAAQAAAAAABwAQAAAAFAgVCDgPOQ+AD4EP3RDeEAACzxIAANASAAA=\",\"AAAAATAPAAAAAAAAAAAAAAAAAAAAAAUvDwAAMQ8AADcPAAA6DwAA0RIAAA==\"]AAg[\"AAAEHQIAAH8CAABOCQAATwkAAASDAQAAiwEAAJABAACdAQAAAiQCAACFAgAAAAA6MAAAAQAAAAAAEwAQAAAAFgQXBAIJAwnkD+YP6A/qD+sPdxB5EHsQfRB/EP0R/hGZE5oTnBOdEwJzEgAAdBIAAAUtDAAALgwAAC8MAAAwDAAAMQwAAAAFdhAAAHgQAAB6EAAAfBAAAH4QAAAAAAACmQEAAAsHAAA=\",\"AAAAAAAAAAAAAAAAAAAAAAAABAQJAAAFCQAABgkAAAcJAAA=\"]BKo[\"AAADTAkAAGUSAABmEgAABYEBAAA7CgAA/xAAAGoSAABrEgAAOjAAAAEAAAAAAAcAEAAAAIwBngFQCVEJJQpxCpcKrhEBrxEAAAGlEgAAOjAAAAEAAAAAABEAEAAAABYIFwgYCBkI8A52D3cP9g/GEMgQyhDNEM8Q0RDKEssSYxNkEwHJEgAAOzAAAAEAAAsAAgBQDwMAmBIHADswAAABAAAJAAMALQcHAAQIAAAGCAAAAAACwwcAAMUHAAA=\",\"AAAEuwUAALwFAADnDgAA6A4AADswAAABAAAWAAoAaQcHAMQQAQDHEAAAyRAAAMsQAQDOEAAA0BAAAKIRAQD2EgIAQhMBAAKhEgAAGxMAAAGpEQAAAAAAAAAAAAAAAAAAOjAAAAEAAAAAAG0AEAAAAHcBeAF6AXsB1wHYAd4B3wH5A/sDHAQzBTYF/AYABysHLAdfB2AHPgk/CW8JcAkEDAYMGwwcDCAMIQwoDCkM7g7vDgUPKw8sD0sPTA9/D9EP0g/uD+8P+w/8DwAQARAGEAcQDBANEEQQRRBOEFAQURBXEFgQYhBjEGQQZRDjEOQQ9xD4EKURphGnEagR6RHqEfcR+BEBEgMSJBIlEiYSJxIpEioSKxIsEmcSbBJtEm4ShRKWEpcSoBKmEqcSqBKpEqoSqxLBEsIS3BLdEuMS5BIBEwITHhMfE10TXhM=\"]Ac[\"AAAAAAHwAgAA\",\"\"]Ca[\"AAAAAAIkAgAAhQIAADswAAABAAAFAAEA+RAFAA==\",\"\",1]Ek[\"AAAAAAAAAAAAAAAAAAAAAAAAAiAHAAA2BwAA\",\"AAAAAAAC4w8AAOUPAAAAAuQPAADmDwAAAAK2EQAAtxEAAA==\"]Eo[\"AAAAAAAAAAAAAAAAAAAAAAAAAiEHAAA3BwAA\",\"AAAAAAAC5w8AAOkPAAAAA+gPAADqDwAA6w8AAAACuBEAALkRAAA=\"]Ck[\"AAAAAAAAAAAAAAAAAAAAAAAABWQAAAB7AQAAAAgAACAIAAAGCQAA\",\"\"]Bg[\"AAAAAAAAAfwOAAAAAAAAAuUQAADmEAAA\",\"\"]Ek[\"AAAAAVUAAAAABfgDAACyBQAAXQcAAPUQAAAdEwAAAq4AAAABDwAAAALgEgAANRMAAA==\",\"AAAAAAAB9gMAAA==\"]Hk[\"\",\"AAAAAAAAAAA7MAAAAQAACAABALQLCAA7MAAAAQAADAABAPwDDAAE9g4AAPcOAAD4DgAAbBAAAAABJwwAAAQjDAAA+Q4AAPsOAABrEAAAAyQMAAAlDAAA+g4AAAAAASYMAAA=\"]Co[\"AAAAAAAAAAAAAAAAAAJKEAAASxAAAA==\",\"AAAAAAAAAAAAAAABIwgAAA==\"]Hk[\"AAADpQEAAA8MAAASDAAAAWAFAAABjAEAAAQkAAAAhQAAAOMPAADlDwAAAVsPAAABBQMAAAPpAAAAthAAALcQAAA=\",\"AAACpQEAAAwMAAACGAQAAGAFAAABhhIAAAAB8gsAAA==\"]Hk[\"AAADpgEAABAMAAATDAAAAZsFAAABngEAAARLAAAAkgAAAOcPAADpDwAAAlIBAABoDwAAARIDAAADDQQAALgQAAC5EAAA\",\"AAACpgEAAA0MAAADGQQAAJsFAACJEgAAAAH2CwAA\"]2ALk[\"AAAAAAHTDwAAAAA6MAAAAQAAAAAAEAAQAAAADAQWDBcMswy0DPEO8g5yEHMQoBGhEXETchOVE5YToBOhEwIUBAAAyRIAADswAAABAAALAAIAUA8DAJgSBwA7MAAAAQAACAACAJIBAAAtBwcAAAAAAAKZAQAACwcAAA==\",\"AAAHvQUAAL4FAAAPDAAAEAwAABIMAAATDAAAsBEAADowAAABAAAAAAAXABAAAAAKCAsIWwlnCREMFAwVDOoO6w7yD/MP9A9DEMMQ1BDVENYQ1xDYEMwSMxM0E0cTSBM7MAAAAQAA2AACAAoO1ACPEgMAOzAAAAEAAEoAAgALAEkA+RIAAAF7EwAAAAAE5xAAAOgQAADpEAAA6hAAAATZEAAA2hAAANsQAADcEAAAAfwRAAAAAAAAAAAHIAQAANUFAAAqBwAABw4AAEYQAABWEAAA6xAAAA==\"]Cc[\"AAAAAAIkCgAAcAoAAAAAADswAAABAAAFAAEA4BEFAA==\",\"\"]BNc[\"\",\"ADswAAABAAAFAAIA7AMEAHwTAAACIgcAAEAHAAA6MAAAAQAAAAAACgAQAAAAsgGzAccFdwyrEesR/xFcE2cThROUEzowAAABAAAAAAAYABAAAADhAeIB4wHkAeUB+gPGBcsF8gYeB0wHcwlZEK4R7BFxEvoSGRMaE4ETghODE4QTlxObEzswAAABAAACARAA+AMAAMUFAAAjBwAAPwcAAE8HAACLDXYA4Q8AAFoQBwC6EAAAKBF2AK8RAACxEQAALhIAAHISAACTEgAAXxMAADowAAABAAAAAAAaABAAAADIBc4FzwXQBRwHHQckByUHJgcnBygHKQe+C78LwAvBC/wOAQ8KEHEQqhGlEuES4hJXE2gThhM7MAAAAQAANgARAAEDFAAVBAAAIQQCALAFAQCzBQAAyQUBAOIFAAAQBwsAFggDAL0LAAAiDAAAVA8AAIcPAAALEAAALxIAAFgTAACHEwAAOzAAAAEAAFAACgDAADwAZAEBABQEAADEBwAAxgcAAAUIAAAHCAAA4BEFADYTBQBJEwAAOzAAAAEAABQABgD8AwAAeAwAAAIOAABQDwMAmBIHAHUTBQA7MAAAAQAAEQAIABoEAQAtBwcABAgAAAYIAAD2DgIAAA8AAGwQAAANEgAAAmcHAABoBwAAAxEEAAASBAAAJwwAAATDBwAAxQcAACMMAABrEAAAAAUUEwAAFRMAABYTAAAXEwAAGBMAAA==\"]Bc[\"AAAAAAAAAAAAAAAC5RAAAOYQAAA=\",\"\"]Kc[\"AAAAAAFwEgAAADswAAABAAAGAAIAJAcFAHEQAAACswUAAIcPAAABFAQAAAADFBAAABUQAAANEgAAAW0PAAA=\",\"AAAAAAABDwQAADswAAABAAAFAAEAJAQFAAAAAAAAAAAAAAAAB+ABAAAyBQAANQUAADkHAAA6BwAAOwcAADwHAAA=\"]Do[\"AAAAAAABFQAAAAAAAcQAAAAAAAAAAAAAAAACMgUAACsHAAA=\",\"AAAAAAAAAAAC/g4AAP8OAAA=\"]Dk[\"AAAAAAABKwAAAAEDAQAAAAAAAAAAAAAAAAACNQUAACwHAAA=\",\"AAAAAAAAAAAB8QMAAA==\"]Ac[\"AAABLgIAAA==\",\"\"]Fg[\"AAAABGUFAABrBwAADggAAMcQAAADYQkAAP0LAACCEwAAAVwQAAAAAAHxAAAAAZoSAAABLwcAAA==\",\"AAAAAWUFAAAAAY4BAAA=\"]Fg[\"AAAABKYFAABvBwAAEggAAM4QAAADZQkAAAEMAACEEwAAAWAQAAABaAEAAAAAAZ4SAAABMwcAAA==\",\"AAAAAaYFAAAAAaABAAA=\"]Bk[\"\",\"AAAAAAAAAAA7MAAAAQAAPgACAMAAPABkAQEA\"]Ck[\"\",\"AAAAAAHyBgAAOzAAAAEAAAkAAgBPBwAAUQcIAAFbEwAAAdkSAAA=\"]Bo[\"\",\"AAAAAAAAAAAAAAAAAiATAAAhEwAAAAAAAXYSAAA=\"]Ck[\"AAACLwIAAD8CAAA=\",\"AAAAAAAAAuYSAADnEgAAAAAC5xAAAOgQAAA=\"]Cg[\"\",\"AAAAAAOGEgAAhxIAAIgSAAAABPELAADyCwAA8wsAAPQLAAA=\"]Eo[\"\",\"AAAAAAA7MAAAAQAABQABAO0RBQA7MAAAAQAABQABAMMSBQAAOzAAAAEAAAUAAQA2EwUAOzAAAAEAAAUAAQB1EwUA\"]Eo[\"AAAAAWoFAAAAASgAAAAAAAL7AAAA/AAAAA==\",\"AAA7MAAAAQAACgACAF8CCQBqAgAAAmoFAABpEwAAAAABCxIAAA==\"]Eo[\"AAAAAaMFAAAAAVMAAAAAAAJkAQAAZQEAAA==\",\"AAA7MAAAAQAACgACANsCAQDeAggAAqMFAABqEwAAAAABDBIAAA==\"]Dg[\"AAABvwIAAAGhBQAAAAFRAAAAAl0BAABeAQAA\",\"AAABzgIAAAGhBQAAAAAAAAENBAAA\"]Eo[\"AAAAAAAAAm0BAABuAQAA\",\"AAAAAAAAB1cTAAB/EwAAhhMAAKITAACjEwAApBMAAKUTAAACWBMAAIcTAAABgBMAAA==\"]Bk[\"AAAAAAAAASYBAAA=\",\"AAABnAIAAAAAAQkEAAA=\"]Bo[\"AAAAAaoFAAAAAAFvAQAA\",\"AAAAAaoFAAAB/xIAAA==\"]Bg[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAXMMAAA=\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAAwEIAAAhCAAABwkAAA==\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAiAEAAACCAAA\",\"\"]Cc[\"AAAAAAAAAAJICQAASQkAAAAAAAAAAAACmQEAAAsHAAA=\",\"\"]Fo[\"AAAAAAACxQUAALERAAAAAAAAAAAAAAAAAAAEJwgAAAUPAAAOEAAAAhEAAA==\",\"AAHIBwAAAAAAAAAAAAAAAAAAAAAAAAIPEAAAtRAAAA==\"]Bk[\"AAAAAccFAAAAAAH8DgAAAVQPAAA=\",\"AAH5BwAA\"]Bg[\"AAAAAAAABBAQAAAREAAAEhAAABMQAAA=\",\"\"]Ag[\"AAAAAoUBAACRAQAA\",\"\"]Do[\"AAAAAAAAAAAAAnsJAAB8CQAAAZIBAAA=\",\"AAAAAAAAAAAABS0MAAAuDAAALwwAADAMAAAxDAAA\"]AGc[\"AAAAAAKKAQAAmgEAAAAABLAFAACxBQAAsRAAALIQAAAExAcAAMYHAAAFCAAABwgAADswAAABAAALAAQA4AUBAC0MBAB4DAAA5xADADowAAABAAAAAAALABAAAAAaBBsEBAgGCAAPFBAVENkQ2hDbENwQDRIECwQAAGcHAABoBwAAbQ8AAAQRBAAAEgQAACATAAAhEwAABRAEAADDBwAAxQcAAPkOAAD7DgAAAyQMAAAlDAAA+g4AADowAAABAAAAAAAIABAAAACZAfID9AMLBxQTFRMWExcTGBMBdhIAAAEmDAAA\",\"AAAAAQMRAAAAArUFAAC2BQAAAAIHDAAACAwAAAAAAAAAAAAC8gMAAPQDAAA=\"]Hm[\"\",\"AAAAAAA7MAAAAQAABQABADIMBQACDgwAAFsTAAAEgBAAAIEQAACzEAAA2RIAAAXUDwAA1Q8AANYPAADXDwAAyRIAAAGsEQAAAAH8EQAAAAHCBwAAAAGZAQAAAAABFAYAAA==\",1]ANc[\"AAACkwEAAJwBAAAGgwEAAIsBAACQAQAAnQEAAGoSAABrEgAABMYFAABzCQAAIwoAAG8KAAA7MAAAAQAABQABADIMBQAC4RIAAOISAAA6MAAAAQAAAAAACQAQAAAASAlJCfAP8Q+AEIEQYxJkElkTWhM6MAAAAQAAAAAACQAQAAAA8QPEB8YHBQgHCP4O/w5uD30TfhMAAgQIAAAGCAAA\",\"AAAAOjAAAAEAAAAAABAAEAAAAHkBogejB6QHpQcpCCoIOAxxD3IPBRB0EHUQ+RH6ES0SYhMDcQkAANMPAAAoEgAAOzAAAAEAAAcAAwAyDAUAABIAAAISAAAAAoAQAACBEAAAAALgBQAA4QUAAAACCwQAAG0PAAAAAxAEAAD5DgAA+w4AAAMkDAAAJQwAAPoOAAAAAAImDAAAfQwAADowAAABAAAAAAALABAAAAB8AR0EHgQ0BTcF6wbsBmEHpgcmCCcITQ8=\"]Bg[\"AAAAAAAAAAAAAQcEAAA=\",\"AAAAAREGAAA=\"]Bk[\"AAAAAAAAADswAAABAAAMAAIA4gUAABAHCwA=\",\"\"]Cc[\"AAABQwkAAAAB5QEAAAAAAAHxAwAA\",\"AAAAAAAAAAEMBAAA\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAloAAADjDgAA\",\"\"]Bc[\"AAAAAAAAAAAAAAAAAAAAAQsHAAA=\",\"\"]Cm[\"AAAAAAAAA84FAADPBQAA0AUAAA==\",\"AAAAAAAAAAAAAAABIwgAAA==\",1]Bg[\"AAAAAAAAADswAAABAAAUAAEAAQMUAA==\",\"\"]Ck[\"\",\"AAAAAARqCQAAawkAAGwJAABtCQAAAAAAAAAAAAAAAAAAAagPAAA=\"]Bg[\"AAAAAAAAAAAAAQIEAAA=\",\"AAAAARoIAAA=\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAVkAAAA=\",\"\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAZcAAAA=\",\"\"]BMa[\"AAAAAAABLhIAAAABLxIAADowAAABAAAAAAAaABAAAACEAdoF2wUyDzMPVBBVEPUR9hGUEpUSvxLAEtIS0xLVEtYS1xLYEuASNRN9E34TkBORE54TnxM7MAAAAQAABgADAHsJAQBQDwMARhMAAAOSAQAAYBMAAIgTAAAAAAABHwcAAAAAAAEUBgAA\",\"AAAFVgkAAFcJAABYCQAAWQkAAFoJAAAECQwAAAoMAAALDAAAnxEAADswAAABAAARAAgAGwgAAAEJAABqCQMAcQkAAMoPBAAoEgAAMRIAAI8SAwA7MAAAAQAAIgAOAK4BAACwAQAA9gMAAAkEAAAPBAAAUQcIADIMBQDzDgAA/Q8AAGYQBAC8EAQAbxIAANQSAAAcEwAAOzAAAAEAACMACQDzAwAAJAQFABMGAAAkCAAA9A4AAFcPEwDnEQEA5hIBAOkSAQA7MAAAAQAACgAFAOIPAACAEAEA3xADAO8QAgAAEwAAOzAAAAEAAAwABgDxAwAA/g4BANQPAwC2EAMA4BIAADUTAAAEthEAALcRAAC4EQAAuREAAAJgEwAAiBMAAAQLBAAAIwgAAG0PAABtEAAAAAABHwcAAAGZAQAAAAA6MAAAAQAAAAAAHQAQAAAAmQCaAJsAnAACBw8HWgdbB1wHPAluCXcJeAl5CXoJfQl+CT0PVQ9WD3gPeQ96DwIQAxAEEO4QpBEFE6YT\",1]Co[\"AAAAAAAAAAAAAAAAAAJKEAAASxAAAA==\",\"AAAAAAAAAAAAAAABIwgAAA==\"]Jg[\"AAAAAUgFAAAABxYAAACCAAAArgEAAPYDAADzDgAA+hAAAP0QAAACWA8AAOcRAAACAgMAABEHAAACxQAAAMYAAAAAAQQIAAAAAAHDBwAA\",\"AAHNBwAAAAJVAAAASAUAAAIkAgAAGwgAAAL6EAAA/RAAAAABBAcAAA==\"]Og[\"AAAAAW4FAAAABi0AAACKAAAAsAEAAPwQAAD+EAAAbxIAAAcGAQAABwEAAPQOAABgDwAAbA8AAPUPAADoEQAABAoDAAAWBwAAIgwAAAATAAABrREAAAECDgAAAwYIAABgEwAAiBMAAAAAAcUHAAA=\",\"AAHiBwAAAAFuBQAAAoUCAADEBQAAAvwQAAD+EAAAAQkHAAAAAAEfDAAAAAH8EQAAAAAAAAAAOzAAAAEAAAUAAQBZAAUA\"]Bo[\"\",\"AAAAAAAAOzAAAAEAAJUAAwCfACAA/QBmAGYBDQA=\"]CDi[\"\",\"AAA7MAAAAQAA1QABAN4K1QA7MAAAAQAA3AALAPYJLAAmCjAAWAoBAFsKEwByCh0AkQoFAJgKJAC/CgAAwgobAOYRAABpEwEAOjAAAAEAAAAAABMAEAAAAMsFEgbyBlAJUQkjCiQKJQpXCloKbwpwCnEKkAqXCr0KwAqGEocSiBI6MAAAAQAAAAAAEgAQAAAA+AOyBU8HXQd1CXYJvgrBCvUQ7RHuEe8R8BHxEfIRkxIdEyQTJRM6MAAAAQAAAAAAKQAQAAAAHAcdByQIvgu/C8ALwQvxC/IL8wv0Cw4MAQ8qD2wP9Q8KEBAQERASEBMQcRALEgwSwxLEEsUSxhLHEsgS5hLnEukS6hJXE1sTfxOGE6IToxOkE6UTOjAAAAEAAAAAABwAEAAAAAwEIQQiBCMEyQXKBQMHBAcFBwYHBwciDIcP5A/mD+gP6g/rDwsQgBCBELsQ7xDwEPEQ2RIAE1gThxM7MAAAAQAAWAAPAMAAPABkAQEA8QMAAA0EAAAUBAAAwQUBAMEHAAD+DgEAbg8AAK0RAADgEQUA4BIAADUTBgBJEwAAgBMAADswAAABAAARAAgA4AUBAB8MAAB4DAAAAg4AAM8PAADnEAMAzxIBAHUTBQAGAA8AABQQAAAVEAAADRIAAGATAACIEwAAOjAAAAEAAAAAAAsAEAAAAAsEzAVnB2gHIwhtD3YQeBB6EHwQfhD8EQQRBAAAEgQAACATAAAhEwAABMIHAADDBwAAxQcAAGsQAAACJQwAAPoOAAA6MAAAAQAAAAAACAAQAAAAmQHyA/QDCwcUExUTFhMXExgTAXYSAAAAARQGAAA=\",1]Le[\"\",\"AAAAAqIBAADwBgAAA00HAADsDgAA7Q4AAAM/BwAAABIAAAISAAA6MAAAAQAAAAAABwAQAAAA/A6qEbMRtBG1EeES4hITEwIVBAAAvQsAAAGEAQAAAUYTAAAE9g4AAPcOAAD4DgAAbBAAAAABJwwAAAQQBAAAIwwAAPkOAAD7DgAAASQMAAAAAAEmDAAA\",1]Fo[\"\",\"AAAAOjAAAAEAAAAAAAcAEAAAALIBswGrEf8RXBNnE4UTlBMCTAcAANMPAAAE7wYAAE0JAAAtDwAAqREAAAGlEgAAAbMQAAAByRIAAA==\"]Cg[\"\",\"AAAAA4kSAACKEgAAixIAAAAE9QsAAPYLAAD3CwAA+AsAAA==\"]Bk[\"\",\"AAAAAAAAOzAAAAEAAAUAAgAIBwIADAcCAA==\"]Fc[\"AAAAAZsBAAAAAAAABNoFAADbBQAAMg8AADMPAAA=\",\"AAA7MAAAAQAANwABADkMNwAAAAAAAAAAAAAAAAAAAAABPQcAAA==\"]Ao[\"\",\"AAA7MAAAAQAA1QABACsI1QA=\"]AAc[\"AAAENQIAAEYCAABKAgAA5w4AAAFGBQAAA3ESAACXEwAAmxMAAAMTAAAAgQAAAPkQAAABVw8AADowAAABAAAAAAAMABAAAAABAyEEIgSwBbEFyQUQB+wQ2hIDEywTZRNzEwLAAAAAwQAAAA==\",\"AAHMBwAAOzAAAAEAAAYAAwAdAgQATgkAAGUSAAABRgUAAARQCQAAIwoAACQKAAAlCgAAAfkQAAAB8QsAAAMDBwAA7xAAAPAQAAAAAAABIwgAAAAAAAAAAAHgDgAA\"]AAo[\"AAAEogIAAMECAADIAgAA6A4AAAFrBQAAAAQpAAAAiAAAAPsQAAByEgAABP8AAAAAAQAAXg8AAGgTAAA6MAAAAQAAAAAACgAQAAAACAMjBLMFygUVB+0Q2xIEEy0TZhN0EwABAg4AAA==\",\"AAHhBwAAB3sCAAB8AgAAfQIAAH8CAACAAgAATwkAAGYSAAABawUAAARRCQAAbwoAAHAKAABxCgAAAvULAAD7EAAAAggHAAAkCAAAAfEQAAAAAAAAAAAAAAAAA+IOAADjDgAA5A4AAA==\"]Cg[\"AAAAAAAAAAAAAAAAAREEAAAAAAHyAwAA\",\"AAAAAAAAAAGxEAAA\"]Cg[\"AAAAAAAAAAAAAAAAARIEAAAAAAH0AwAA\",\"AAAAAAAAAAGyEAAA\"]Fo[\"AAA7MAAAAQAACwAEAMUPBACMEgAALhMEAJgTAAA=\",\"ADswAAABAAAJAAIApwcEAF4SBAA7MAAAAQAACwAEAMUPBACMEgAALhMEAJgTAAA=\"]Hk[\"AAAAAAAAAAA7MAAAAQAACAABALQLCAA7MAAAAQAADAABAPwDDAAE9g4AAPcOAAD4DgAAbBAAAAABJwwAAAQjDAAA+Q4AAPsOAABrEAAAAyQMAAAlDAAA+g4AAAAAASYMAAA=\",\"\"]Cc[\"AAAAAocBAAB0EAAAAAAAAAErDAAAAAEaBAAAAWcHAAA=\",\"\"]Cc[\"AAAAApYBAAB1EAAAAAAAAAEsDAAAAAEbBAAAAWgHAAA=\",\"\"]AAo[\"AAAAAncSAAB4EgAAAAWOAQAAjwEAAKABAAChAQAAuhAAADswAAABAAALAAMAKQUHAMgFAAAYDAIAOjAAAAEAAAAAAAcAEAAAALMFFggXCBgIGQgCDwMPVA8GthAAALcQAAC4EAAAuRAAAEkTAACAEwAAOzAAAAEAAA0ABAAfDAAAAg4AAFAPAwCYEgcAOzAAAAEAAAcAAQAtBwcAAm0QAAD8EQAAAAHCBwAAAR8HAAAAAAABFAYAAA==\",\"AAAAA6gBAACpAQAAqgEAAAAB/Q8AAA==\"]Ek[\"AAAAAAAAAAAAAAAAAAAAAAACvwUAAEIHAAA=\",\"AAAAAAAAAALfEAAA4BAAAAAAAAAAAAAAAAACIAcAADYHAAA=\"]Ek[\"AAAAAAAAAAAAAAAAAAAAAAACwAUAAEQHAAA=\",\"AAAAAAAAAALhEAAA4hAAAAAAAAAAAAAAAAACIQcAADcHAAA=\"]Ak[\"\",\"AAAAAAJODwAATw8AAA==\"]Eo[\"AAAABGcFAABsBwAADwgAAMkQAAACYgkAAP4LAAABXRAAAAAAAfMAAAABmxIAAAEwBwAA\",\"AAAAAWcFAAAAAY8BAAA=\"]Eo[\"AAAABKgFAABwBwAAEwgAANAQAAACZgkAAAIMAAABYRAAAAFqAQAAAAABnxIAAAE0BwAA\",\"AAAAAagFAAAAAaEBAAA=\"]Bg[\"AAAAAAAAAAAAAQEEAAA=\",\"AAAAAXQJAAA=\"]Ag[\"AAAChgEAAJUBAAA=\",\"\"]Bg[\"AAAAAAAAAAAAAAAAAAIjDAAAaxAAAA==\",\"\"]Ag[\"\",\"AAAC7A8AAO0PAAA=\"]Bc[\"AAAAAAAAAAAAAawRAAA=\",\"AAFjAAAA\"]Dg[\"AAAAAqgBAACpAQAAAAADzgUAABgMAACzEQAAAAGEAQAAAUYTAAA=\",\"AAAAAZ8RAAA=\"]Hg[\"AAAAAdQQAAABjxIAAAPjDwAAZxAAAL0QAAAB5hIAAAHfEAAAAdQPAAAC5xAAALYRAAAB2RAAAAAAAAABFRMAAA==\",\"AAAAAAAAAAJ3EAAAeRAAAAAAAAJ2EAAAeBAAAA==\"]Ig[\"AAAAAdcQAAABkRIAAAPnDwAAaRAAAL8QAAAB6RIAAAHhEAAAAdYPAAAC6RAAALgRAAAB2xAAAAAAAAABFxMAAA==\",\"AAAAAAG0EAAAAAADexAAAH0QAAB/EAAAAAAAA3oQAAB8EAAAfhAAAA==\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAmIAAADkDgAA\",\"\"]Cc[\"AAAAAQUQAAABoRIAAAAC8wMAABMGAAA=\",\"AAAAAAEoCAAA\"]Ek[\"AAAAAU0FAAAAARgAAAAAAAHLAAAAAAAAAAAAAAAAAjQFAADrBgAA\",\"AAHSBwAAAAFNBQAAAAAAAAAAARQQAAA=\"]Ek[\"AAAAAXoFAAAAATUAAAABFwEAAAAAAAAAAAAAAAAAAjcFAADsBgAA\",\"AAHpBwAAAAF6BQAAAAAAAAAAARUQAAA=\"]Be[\"AAAAAAA7MAAAAQAAdgABACgRdgA=\",\"\",1]Bg[\"AAAAAAAAAAAAAQQEAAA=\",\"AAAAAfALAAA=\"]Ec[\"AAAAAAAAAAAAAAAAAALDBwAAxQcAAAEfBwAAApkBAAALBwAA\",\"AAAAAAAAAAAAAAAAAAAAAZkBAAA=\"]Dk[\"AAAAAAAAAAABOxMAAAF6EwAA\",\"AAAAAAAB8hEAAAHIEgAAAAAAAAAAAAAAAAABrAcAAA==\"]Dc[\"AAAAAY4FAAAAAAE4AQAAARgHAAAAAAAAAAAAAAAAAeABAAA=\",\"AAAAAY4FAAA=\"]ABc[\"AAAAA4EBAAA1CgAA/xAAAAMeBwAAkAoAAFkQAAAAADowAAABAAAAAAANABAAAABdCV4JaAlpCTsPPA8IEAkQaBJpEo0SjhLeEt8SOzAAAAEAAA8ABwBUEAEA4BEFAJQSAQDgEgAANRMAAJATAQCeEwEAAAJgEwAAiBMAAA==\",\"AAAAA3QBAAB/AQAA0A8AAATKDwAAyw8AAMwPAADNDwAAAq4BAACwAQAAAAAC4BIAADUTAAAAAmATAACIEwAAAAAAAAAAAASvAQAAsQEAAEAJAAACEQAA\"]Bk[\"AAAAAAFMBwAA\",\"AAAAAAAAAAGzEAAAAAGsEQAA\"]Hg[\"AAAABT4FAADXBQAAEQYAAHcMAAD5EQAAAe0OAAADPwcAAC4SAABfEwAAAqgAAACqEQAAAbEFAAABwQcAAAHPDwAA\",\"AAAAAj4FAADwBgAAAU0HAAAAAAABhAEAAAFGEwAA\"]Gg[\"AAAAAvoRAAD4EgAAAfcGAAABIwcAAAH+AAAAAuIPAAAvEgAAAAFBBwAAAAHMBQAAAAHCBwAAAR8HAAAAAAABFAYAAA==\",\"AAAAAAAAAAAAAAGSAQAA\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAWEAAAA=\",\"\"]Fc[\"AAADIgcAAEAHAABJDwAABBQMAAD5DwAA9BAAAEcTAAAE4gEAAMsFAAASBgAAbAkAAAACqgAAACoPAAA=\",\"AAABUhAAAA==\"]Fc[\"AAAABBUMAAD6DwAA9hAAAEgTAAABbQkAAAEjBwAAAToBAAAAAAFBBwAAAAHMBQAAAAAAAAAAARQGAAA=\",\"AAABUxAAAA==\"]Dg[\"AAAAAZMFAAAAAUUAAAABQwEAAA==\",\"AAAAAZMFAAAAAAAAAAAAAAAAAAAAAAFkAAAA\"]Bk[\"AAAAAAHhAQAAAAAAAAH+AwAA\",\"AAAAAR0MAAA=\"]Cc[\"AAAAAAEbCAAAAAAAAAH/AwAA\",\"AAAAAAACHgwAAPsRAAA=\"]Dk[\"\",\"AAAAADswAAABAAATAAEArQcTADswAAABAAATAAEAgQATADswAAABAAATAAEAVw8TAA==\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAV8AAAA=\",\"\"]Ak[\"\",\"AAAAAAJrDwAA/xIAAA==\"]Ag[\"\",\"AAAAAAABHBMAAA==\"]Fg[\"AAAAAasFAAAAAAFxAQAAAAAAAAAAARAEAAAAAAABJgwAAAabAAAAfAEAANoBAAAPBwAAYQcAAKQRAAA=\",\"AAH4BwAAAAGrBQAA\"]Ho[\"AAAElQAAACACAAC7BQAADAwAAAFiBQAAAAImAAAAhwAAAAFdDwAAAgcDAAAUBwAAAuwAAADtAAAA\",\"AAABSgIAAARWAAAAVwAAAFgAAABiBQAAAowBAACIEgAAAAH0CwAAAQcHAAA=\"]Kk[\"AAAElgAAAHsCAAC8BQAADQwAAAGdBQAAAARNAAAAlAAAAHAPAAB1DwAAA1UBAABWAQAAag8AAAIUAwAAGgcAAA==\",\"AAAByAIAAAKdBQAAixIAAAGeAQAAAfgLAAAD8wMAABMGAAANBwAAAAAAAAAAAAAAAAAEXwAAAGAAAABhAAAAYgAAAA==\"]Ak[\"AAAAAAKFDwAAhg8AAA==\",\"\"]Ao[\"AAAAAAAAAAACfQEAAOARAAA=\",\"\"]Ao[\"AAAAAAAAAAACfgEAAOMRAAA=\",\"\"]Dc[\"AAAAAAAAAAAB7gAAAAAAAAAAAAAAAAWZAAAAHQQAAFoHAAA0DwAAAhAAAA==\",\"\"]Fc[\"AAAAAAAAAVcBAAAAAAAAAAAAAAAAAAWaAAAAHgQAAFsHAAA1DwAAAxAAAA==\",\"AAAAAAAAAAAAAUEHAAAAAswFAABtEAAA\"]Fc[\"AAAAAawFAAAAAAFyAQAAAhUEAAC9CwAAAAAAAAEnDAAAAAAAAAAFnAAAAFwHAAA9CQAANg8AAAQQAAA=\",\"AAAAAawFAAA=\"]Fk[\"AAAAAUkFAAAAAAAAAccAAAAAAAAAAAAAAAADdwEAADMFAAAEDAAA\",\"AAHOBwAAAAFJBQAAAAAAAAAAAAAAAAAAAAACYgcAAGQHAAA=\"]Fk[\"AAAAAXQFAAAAAAEPAQAAAAAAAAAAAAAAAAADeAEAADYFAAAGDAAA\",\"AAHlBwAAAAF0BQAAAAAAAAAAAAAAAAAAAAACYwcAAGUHAAA=\"]Bc[\"AAAAAAAAAAAAAAAAAAAAAAF2EgAA\",\"\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAWAAAAA=\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAlsAAAD1DgAA\",\"\"]Bg[\"AAAAAAAAAAAAAQMEAAA=\",\"AAAAAbIMAAA=\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAZgAAAA=\",\"\"]Dk[\"AAAAAWQFAAABjQEAAAABKgUAAAHGEAAAAfAAAAA=\",\"AAHbBwAAAAFkBQAAAAAAAcYQAAA=\"]Dk[\"AAAAAaUFAAABnwEAAAACZwEAAC4FAAABzRAAAA==\",\"AAH0BwAAAAGlBQAAAAAAAc0QAAA=\"]Hc[\"AAAAAAABLQ8AAAETEwAAAAE3EwAAAXYTAAAAAAAAAAAAADowAAABAAAAAAANABAAAAAqBzwJeQkHDjoPPQ94D0YQVhDuEAESAxLREgUT\",\"AAAAAAAB7hEAAAHEEgAA\"]Ak[\"AAAAAAIZEwAAGhMAAA==\",\"\"]Cg[\"AAAAAS0SAAABcQkAAAEPBAAAAAABFAQAAA==\",\"AAAAAAEFDAAA\"]Ag[\"\",\"AAAAAAAAAQ4HAAA=\"]Ag[\"AAAAAAAAAAEbBwAA\",\"\"]Dk[\"AAAAAAAAOzAAAAEAAJUAAwCfACAA/QBmAGYBDQAAOzAAAAEAAD4AAgDAADwAZAEBAA==\",\"\"]Gg[\"AAAAAZ4FAAAAAk4AAAD5EgAAA1gBAABZAQAAexMAAAEVAwAAAAEfDAAAAAH8EQAAAAABHwcAAAAAAAJDBwAAbgkAAA==\",\"AAAAAZ4FAAAAAAH1DwAA\"]Dk[\"AAAAAVkFAAAAAR0AAAAAARIHAAAC2wAAANwAAAA=\",\"AAAAAlkFAAA7CgAAAAAAAQUHAAA=\"]Dk[\"AAAAAYsFAAAAAUAAAAACMgEAADMBAAABFwcAAA==\",\"AAAAAYsFAAABlwoAAAABCgcAAA==\"]Ck[\"AAAAAXEPAAAAAAAAAsQHAAAFCAAA\",\"AAAAAAAAAAAC1A8AANUPAAA=\"]Ck[\"AAAAAXIPAAAAAAAAAsYHAAAHCAAA\",\"AAAAAAAAAAAC1g8AANcPAAA=\"]Bk[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAk8QAACyEQAA\"]Ek[\"AAAAAykIAACiEQAA9hIAAAAAAAAAAAEECAAAAAABwwcAAA==\",\"AAAAAAAAAALfEAAA4BAAAALUDwAA1Q8AAA==\"]Ek[\"AAAAAyoIAACjEQAA9xIAAAAAAAAAAAEGCAAAAAABxQcAAA==\",\"AAAAAAAAAALhEAAA4hAAAALWDwAA1w8AAA==\"]Eg[\"AAAAAYAFAAAAATwAAAACIgEAACMBAAA=\",\"AAAAAYAFAAAAAAAAAAEfDAAAAAH8EQAAAAAAAAAAAbgFAAA=\"]Ac[\"\",\"AAAAAAFrDwAA\"]Cc[\"AAABnAIAAAFxBQAAAAEvAAAAAQoBAAA=\",\"AAAAAXEFAAA=\"]Gg[\"AAAAAUsFAAAAARcAAAAAAAHJAAAAAAAAAAAAAAAAOjAAAAEAAAAAAAkAEAAAAK8B3gHuDi8P7g8GECQSlhKmEsES\",\"AAHQBwAAAAFLBQAAAAHzDgAA\"]Ho[\"AAAAAXYFAAAAATIAAAABEQEAAAAAAAAAAAAAAAAAOjAAAAEAAAAAAAkAEAAAALEB3wHvDjEP7w8HECUSlxKnEsIS\",\"AAHnBwAAAAF2BQAAAAAB9A4AAAAAAAAAAAAAAAAAAfUOAAA=\"]Gk[\"AAACBQIAAA0CAAABQQUAAAACDgAAAFMHAAACswAAALQAAAA=\",\"AAACCAIAAAkCAAABQQUAAAAAAAAC/g4AAP8OAAAAAAILBAAAbQ8AAAABaxAAAAElDAAA\"]Do[\"AAAAAXgFAAAAAjMAAACLAAAAAxMBAAAUAQAAYQ8AAAELAwAA\",\"AAAAAXgFAAAAAAAAAfEDAAA=\"]Ck[\"AAAAAv0SAAD+EgAAAAAAAAAAAAABJwwAAA==\",\"AAAAAAABTBAAAA==\"]Co[\"AAAAAAABPwcAAAOzEQAAtBEAALURAAAAAYQBAAA=\",\"AAAAAAABLQ8AAA==\"]JLi[\"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\",1]Eo[\"AAAAAAA7MAAAAQAABQABAO0RBQA7MAAAAQAABQABAMMSBQAAOzAAAAEAAAUAAQA2EwUAOzAAAAEAAAUAAQB1EwUA\",\"\"]Ck[\"AAAAAAAAAcgFAAAAAAH8AwAA\",\"AAAAAAAAAxgMAAAZDAAAGgwAAA==\"]Cc[\"\",\"AAAAAAAE+AMAAF0HAAD1EAAAHRMAAAAAAuASAAA1EwAA\"]Fc[\"AAAAAXMFAAAAATEAAAACDQEAAA4BAAA=\",\"AAHkBwAAAAFzBQAAAAACbA8AAPUPAAACIgwAAAATAAAAAAJgEwAAiBMAAA==\"]Eo[\"AAAAAfIQAAABagkAAAACowAAAA4MAAABuxAAAAABAAQAAA==\",\"AAAAAAESBgAAAAIODAAAKg8AAAABwQcAAAHPDwAA\"]Dk[\"AAAABqsRAAD/EQAAXBMAAGcTAACFEwAAlBMAAAAAAAAByRIAAA==\",\"AAAAAAAAAAGzEAAA\"]A@g[\"AAAAOjAAAAEAAAAAAA8AEAAAABgEOwXWBfMGGgh0CfALHQw4DLIMgg9wEPMQ6xHMEmITOjAAAAEAAAAAAAkAEAAAAGsJaw/OD64RKBJwEnES/xKXE5sTOjAAAAEAAAAAAAgAEAAAAK4B9gMeDPMOZhC8EPsRkxIcEzowAAABAAAAAAAHABAAAACkAHEQ5xFXE38ThhOiE6QTAbAFAAAAAAAAAAAAARQTAAA=\",\"AAAAATsFAAABAQkAAAABDgwAAA==\"]ACk[\"AAAAOjAAAAEAAAAAAAgAEAAAABkEWwkRDOoOMA9DENYQMxM0EzowAAABAAAAAAAIABAAAAD6A8QF2AUoCAUMtBDsEfoSGxM6MAAAAQAAAAAACgAQAAAAsAEJBHAPdQ/hD/0PuhCvEW8SchLUEjowAAABAAAAAAAHABAAAAD9APQObA/1D+gRaBOjE6UTBbMFAAAiDAAAABMAAFgTAACHEwAAA60RAABJEwAAgBMAAAIfDAAAAg4AAAJgEwAAiBMAAAJtEAAA/BEAAA==\",\"AAAAAAAAAAAAAAAAAAHCBwAAAAAAAAEUBgAA\"]Ho[\"AAAAA2kHAAAMCAAAxBAAAANfCQAA+wsAAIETAAABWhAAAAABFggAAAACUA8AAJgSAAABLQcAAA==\",\"AAAAAAT7CwAA/AsAAP0LAAD+CwAAAAUpBQAAKgUAACsFAAAsBQAA5xEAAA==\"]Ho[\"AAAAA20HAAAQCAAAyxAAAANjCQAA/wsAAIMTAAABXhAAAAABGAgAAAACUg8AAJwSAAABMQcAAA==\",\"AAAAAAT/CwAAAAwAAAEMAAACDAAAAAUtBQAALgUAAC8FAAAwBQAA6BEAAA==\"]Mc[\"AAAAAUoFAAAAAAAAAcgAAAAAAAAAAAAAAAA6MAAAAQAAAAAAJAAQAAAA1wH5A/wGXwc+CW8JGwwgDCgMKw9LD9EP+w8AEAwQRBBQEFcQYhBkEMEQ4xD3EKURpxHpEfcRKRIrEmwSqBKqEtwS4xIBEx4TXRM=\",\"AAHPBwAAAAFKBQAAAAAAAAAAAAAAAAAAAAACRAkAAEYJAAA=\"]Mc[\"AAAAAXUFAAAAAAEQAQAAAAAAAAAAAAAAAAA6MAAAAQAAAAAAJAAQAAAA2AH7AwAHYAc/CXAJHAwhDCkMLA9MD9IP/A8BEA0QRRBREFgQYxBlEMIQ5BD4EKYRqBHqEfgRKhIsEm0SqRKrEt0S5BICEx8TXhM=\",\"AAHmBwAAAAF1BQAAAAAAAAAAAAAAAAAAAAACRQkAAEcJAAA=\"]Ag[\"\",\"AAAAAAAAAb4LAAA=\"]Ck[\"AAAAAZkFAAAAAAFPAQAA\",\"AAAAAZkFAAAAAAO/CwAAwAsAAMELAAA=\"]Bk[\"AAAAAAAAAAAAAAAAAAEQBAAAAAAAASYMAAA=\",\"\"]Bg[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAeUOAAA=\"]Bk[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAApITAACTEwAA\"]Ck[\"AAAAAAAAAAABSRMAAA==\",\"AAAAAAAAAAAAAAAAAAAAAAABqRMAAA==\"]Ag[\"AAACagIAAOACAAA=\",\"\"]Cc[\"AAAAAAACHgwAAPsRAAABAQ8AAA==\",\"AAAAAAABXQcAAA==\"]Gc[\"AAAAAXAFAAAAAS4AAAABCQEAAAABrREAAAEfDAAAAAH8EQAA\",\"AAHjBwAAAAFwBQAAAAFvEgAAAWwPAAAAAa0RAAAAAAAAAAAAAAABXgcAAA==\"]Do[\"AAAAAAAAAAECDwAAAtoFAAAyDwAA\",\"AAAAAYUBAAAAAAABAg8AAATaBQAAMg8AALYQAAC3EAAA\"]Do[\"AAAAAAAAAAEDDwAAAtsFAAAzDwAA\",\"AAAAAZEBAAAAAAABAw8AAATbBQAAMw8AALgQAAC5EAAA\"]Ao[\"AAAAAAHTDwAAAAAB9g8AAA==\",\"\"]Gc[\"AAAB0hAAAAJWAAAACggAAAHjAQAAAAGvAAAAAiEEAADvEAAAAv4OAABuDwAAAAEADwAAAgsEAABtDwAAAAIjDAAAaxAAAAIkDAAAJQwAAA==\",\"\"]Cc[\"AAAAAAAAAAMMBAAAIwQAAPEQAAA=\",\"AAAAAAABcA8AAA==\"]Ec[\"AAABCAIAAAFCBQAAAAIPAAAAVAcAAAK1AAAAtgAAAA==\",\"AAABCwIAAAFCBQAAAAAAAu8QAADwEAAA\"]Bo[\"AAABUhAAAAAAAAKrAAAArAAAAA==\",\"AAAAAAHLBQAA\"]Ck[\"AAABUxAAAAAAAAI+AQAAPwEAAA==\",\"AAAAAAAAAAAAAAABzAUAAA==\"]Ao[\"AAAAAAAAAaUSAAAB8A4AAA==\",\"\"]Hg[\"AAAAAVgAAAAB5AEAAAF1CQAAA7sAAAAcBwAAChAAAALJBQAAhw8AAAAC4AUAAHgMAAAG9g4AAPcOAAD4DgAAFBAAAGwQAAANEgAAASMIAAAAAvkOAAD7DgAAAfoOAAA=\",\"\"]Dk[\"AAAAAAABdgkAAAIdBwAAJAgAAALKBQAACxAAAAAB4QUAAAEVEAAA\",\"AAAAAAABdQ8AAA==\"]Bc[\"AAAAAAAAAAAAAAAAAAAAAAF2EgAA\",\"\"]AAg[\"AAAAAfAGAAABTQcAAATvBgAAHgwAAC0PAAD7EQAAAlcTAACGEwAAA7MQAABYEwAAhxMAAAXBBwAAthAAALcQAAC4EAAAuRAAAAHPDwAAAAAAAcIHAAA=\",\"AAAABPIQAADzEAAA9BAAAPYQAAAC7A4AAO0OAAAC9RAAABwTAAAFfxMAAKITAACjEwAApBMAAKUTAAABABMAAAGAEwAAAawRAAAAAAAAAAAAAgcPAAAgDwAABtkBAADaAQAAQwcAADQPAAA1DwAANg8AAA==\"]Dc[\"AAAAAAAAAAA7MAAAAQAABQABAOARBQA=\",\"AAAABKcBAACrAQAArAEAAK0BAAA=\"]Ao[\"AAAAAacBAAAAAAAAAXsPAAA=\",\"\"]Ao[\"AAAAAawBAAAAAAAAAXwPAAA=\",\"\"]Eo[\"AAAAAdUQAAABkBIAAAPlDwAAaBAAAL4QAAAB5xIAAAHgEAAAAdUPAAAC6BAAALcRAAAB2hAAAAAAAAABFhMAAA==\",\"\"]Eo[\"AAAAAdgQAAABkhIAAAPpDwAAahAAAMAQAAAB6hIAAAHiEAAAAdcPAAAC6hAAALkRAAAB3BAAAAAAAAABGBMAAA==\",\"\"]Bc[\"AAAAAAAAAiQEAAAkBwAAAAF9DwAA\",\"\"]Bc[\"AAAAAAAAAicEAAAnBwAAAAF+DwAA\",\"\"]Ec[\"AAAAAaIBAAAAAgASAAACEgAAAuESAADiEgAAAhUEAAC9CwAAAAAAAAABEAQAAA==\",\"AAAAAAHwAgAA\"]Gg[\"AAAAAUQFAAAAAhEAAABWBwAAArwAAAC9AAAA\",\"AAADEQIAABICAAATAgAAAUQFAAAAAAABhw8AAAAC4AUAAHgMAAACFBAAAA0SAAAAAAAB+g4AAA==\"]Eg[\"AAAAAXsFAAAAAjYAAACNAAAAAxgBAAAZAQAAYw8AAAENAwAA\",\"AAAAAXsFAAAAAAAAAAHhBQAAARUQAAA=\"]Ec[\"AAAAAAAAAT0BAAAAAAAAAAAAAAAAAAI4BQAAegkAAA==\",\"AAAAAAAAAAAAAAAAAAAAAAAAARQGAAA=\"]Ec[\"AAAB+QsAAAE6BQAAAAELAAAAAaAAAAA=\",\"AAAAAToFAAAAAU0JAAAAAAAAAAAAAAAAAAAB7QYAAA==\"]Dg[\"AAAAAAHGBQAAAAAAAAAAAAAAAAAAAAIPEAAAtRAAAA==\",\"AAHJBwAAAAAAAe8GAAA=\"]Ac[\"AAABLAIAAA==\",\"\"]Do[\"AAAAAVgFAAAAARwAAAAAAALZAAAA2gAAAA==\",\"AAACOgIAADwCAAABWAUAAAAAAAAAAc8SAAA=\"]Dk[\"AAAAAYoFAAAAAT8AAAACMAEAADEBAAA=\",\"AAACpwIAAKkCAAABigUAAAAAAAAAAdASAAA=\"]Bk[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAlIJAABUCQAA\"]Dk[\"AAAAAAAAAAAB9wAAAA==\",\"AAABWAIAAAAAAAAAAAAAAWcHAAABEQQAAAHDBwAAAAHyAwAA\"]Dg[\"AAAAAAAAAWABAAA=\",\"AAAB1AIAAAAAAAAAAAAAAWgHAAABEgQAAAHFBwAAAAH0AwAA\"]Dk[\"AAAAAWYFAAAAAY4BAAABKwUAAAHIEAAAAfIAAAA=\",\"AAHcBwAAAAFmBQAAAAAAAcgQAAA=\"]Dk[\"AAAAAacFAAAAAaABAAACaQEAAC8FAAABzxAAAA==\",\"AAH1BwAAAAGnBQAAAAAAAc8QAAA=\"]Ck[\"AAAAAXwFAAAAATcAAAACGgEAABsBAAA=\",\"AAAAAXwFAAAAAAEdBwAA\"]Eg[\"AAAAAVMFAAAAAhsAAACDAAAAAVkPAAABAwMAAALUAAAA9REAAA==\",\"AAHXBwAAATUCAAACUwUAADUKAAA=\"]Eg[\"AAAAAYUFAAAAAj4AAACOAAAAAisBAABkDwAAAQ4DAAAB9hEAAA==\",\"AAHvBwAAAaICAAABhQUAAAGQCgAA\"]Ao[\"AAAAAAAAAAAC/g8AALYQAAA=\",\"\"]Ao[\"AAAAAAAAAAAC/w8AALgQAAA=\",\"\"]Bg[\"AAAAAAAABLoAAADQBQAAGgwAALURAAA=\",\"\"]Ak[\"AAAAAAABOAAAAAEcAQAA\",\"\"]Do[\"AAAAAAAAAAAAAAAAAAAAAAAAAk8QAACyEQAA\",\"AAHHBwAAAAAAAAAAAAAAAAAAAAAAAAEOEAAA\"]Cg[\"AAAAAAMeBwAAcwkAAFkQAAA=\",\"AAAAAAAAAALwDgAA9g8AAA==\"]Ag[\"AAACWAIAANQCAAA=\",\"\"]Cc[\"AAAAAVgKAAACVwoAAL0KAAABvgoAAAIQEAAAEhAAAA==\",\"\"]Ac[\"\",\"AAAAAeYRAAA=\"]Gk[\"AAAAAVcFAAAAAV8TAAAAAQIJAAAE2AAAAH0PAAAGEgAARBMAAAHPDwAA\",\"AAEiEwAAAAOJAQAAVwUAAGkTAAAAASQTAAAAAQIJAAADfQ8AAAYSAABEEwAA\"]Bc[\"AAAAAYkBAAAAAAAAAAAAAXYQAAA=\",\"\"]Gk[\"AAAAAYkFAAAAAAEvAQAAAQMJAAADfg8AAAgSAABFEwAAAAAAAAHCBwAA\",\"AAEjEwAAAAOYAQAAiQUAAGoTAAAAASUTAAAAAQMJAAADfg8AAAgSAABFEwAA\"]Bc[\"AAAAAZgBAAAAAAAAAAAAAXoQAAA=\",\"\"]Fg[\"AAABEwIAAAFOBQAAAAEZAAAAAAACzAAAAM0AAAA=\",\"AAHTBwAAAAFOBQAAAAF1CQAAAhwHAAAKEAAAAyEEAAAiBAAAyQUAAA==\"]Eo[\"AAAAAX4FAAAAAToAAAACHgEAAB8BAAA=\",\"AAHrBwAAAAF+BQAAAAF2CQAAASQIAAAEIwQAAMoFAAALEAAA8RAAAA==\"]Ao[\"\",\"AAAAAAAAAAAAAAABfhAAAA==\"]Ek[\"AAAAAVwFAAAAASAAAAAAAALhAAAA4gAAAA==\",\"AAABQgIAAAFcBQAAAAAAAAHBBwAAAc8PAAAAAnYQAAB4EAAA\"]Eg[\"AAAAAZAFAAAAAUQAAAACOwEAADwBAAA=\",\"AAABsQIAAAGQBQAAAAAAAAAAAAJ6EAAAfBAAAAABwgcAAA==\"]Ao[\"AAAAAAAAAAHrDwAAAAExDAAA\",\"\"]Jk[\"AAAAAV0FAAAABSEAAACEAAAAtQUAAOMPAADlDwAAAr4LAABaDwAABAQDAAAHDAAA5A8AAOYPAAAE4wAAAOQAAAC2EAAAtxAAAAItDAAALgwAAALZEAAA2hAAAA==\",\"AAAAAl0FAAD5DwAAAooBAADcDwAAAAABSAkAAA==\"]Og[\"AAAAAZUFAAAABUcAAACRAAAAtgUAAOcPAADpDwAABkgBAABJAQAAvwsAAMALAADBCwAAZw8AAAQRAwAACAwAAOgPAADqDwAAArgQAAC5EAAAAi8MAAAwDAAAAtsQAADcEAAA\",\"AAAABJUFAAD8BwAA/QcAAPoPAAAEmgEAAN0PAADeDwAA3w8AAAAAAUkJAAACwQUAAMIFAAAAAAAAAAAAAAA7MAAAAQAACgACAP4HBQAeCAQA\"]Ck[\"AAABvQUAAAFeBQAAAAEiAAAAAAAB5QAAAA==\",\"AAAAAl4FAAAJDAAA\"]Dg[\"AAABvgUAAAGWBQAAAAFIAAAAAUoBAAAAAAAAAAAAAR8HAAA=\",\"AAAAApYFAAAKDAAA\"]Cc[\"AAAAAa0FAAAAAVQAAAABcwEAAA==\",\"AAAAAq0FAAALDAAA\"]Dg[\"AAAAAAAAAAAAAAABbRAAAA==\",\"AAAABuAPAAD0EQAAzRIAAM4SAAD9EgAA/hIAAA==\"]Ak[\"AAAAAAAAAAAB4hEAAA==\",\"\"]Ak[\"AAAAAAAAAAAB5REAAA==\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAl4AAADZAQAA\",\"\"]Bg[\"AAAAAAAAAAAAAQUEAAA=\",\"AAAAAXAQAAA=\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAl0AAABeBwAA\",\"\"]Bk[\"AAAAAAAAAAAAAAQtBwAALgcAAC8HAAAwBwAA\",\"\"]Ec[\"AAAABHQBAAB/AQAAbwUAANAPAAAAAAEIAQAAAAAABDEHAAAyBwAAMwcAADQHAAA=\",\"AAAAAW8FAAA=\"]Bg[\"AAAAAAAAAAAAAQYEAAA=\",\"AAAAAAFwEgAA\"]Cc[\"AAAAAVsKAAACWgoAAMAKAAABwQoAAAIREAAAExAAAA==\",\"\"]Ag[\"AAAAAAABTwcAAA==\",\"\"]No[\"AAAAAVUFAAABTQcAAAEuEgAAASoPAAAGFgwAAPAPAADKEgAA3hIAADwTAABjEwAAB9YAAACEAQAAcQwAAHsPAAAFEgAA1RIAANcSAAABRhMAAAAAAAAAAAAAA/0GAAB1DAAACRIAAA==\",\"AAAAAlUFAACDDwAAAAGIAQAAAQsSAAAGFgwAAPAPAADKEgAA3hIAADwTAABjEwAABXEMAAB7DwAABRIAANUSAADXEgAA\"]Bo[\"AAAAAYMPAAAAAYgBAAAAAuQPAAB3EAAAAAEtDAAA\",\"\"]Nc[\"AAAAAYcFAAAAAAEtAQAABxcMAADxDwAALxIAAMsSAADfEgAAPRMAAGQTAAAFcgwAAHwPAAAHEgAA1hIAANgSAAAAAZIBAAAAAAAAAAAABBQGAAABBwAAdgwAAAoSAAA=\",\"AAAAAocFAACEDwAAAAGXAQAAAQwSAAAGFwwAAPEPAADLEgAA3xIAAD0TAABkEwAABXIMAAB8DwAABxIAANYSAADYEgAA\"]Bo[\"AAAAAYQPAAAAAZcBAAAAAugPAAB7EAAAAAEvDAAA\",\"\"]Ao[\"AAAAAAAAAAAC1RIAANYSAAA=\",\"\"]Ao[\"AAAAAAAAAAAC1xIAANgSAAA=\",\"\"]Ec[\"AAABEgIAAAFFBQAAAAISAAAAVwcAAAK+AAAAvwAAAA==\",\"AAABFQIAAAFFBQAAAAAAAAAAAAEjCAAA\"]Bk[\"AAAAAaoBAAAAAAS5AAAAzwUAABkMAAC0EQAA\",\"\"]Ek[\"AAAAA4kSAACKEgAAixIAAAOGEgAAhxIAAIgSAAAE9QsAAPYLAAD3CwAA+AsAAATxCwAA8gsAAPMLAAD0CwAA\",\"\"]Cc[\"AAAAAAAAAAAB4REAAA==\",\"AAHKBwAAAAKDAQAAahIAAA==\"]Cc[\"AAAAAAAAAAAB5BEAAA==\",\"AAHfBwAAAAKQAQAAaxIAAA==\"]Bc[\"\",\"AAAAAAAAAAAAAAAAAAABHwcAAA==\"]Cg[\"AAAAAesOAAAAAAFHAQAAAeIPAAA=\",\"AAH7BwAAAAAAAdQSAAA=\"]Ek[\"AAAAAT8FAAAAAAGpAAAAAAAAAAAAAAAAAAAEZxIAAG4SAACFEgAAoBIAAA==\",\"AAHLBwAAAAI/BQAA1wUAAA==\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAA3cJAAB5DwAAkhMAAA==\",\"\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAA3gJAAB6DwAAkxMAAA==\",\"\"]Bk[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAlMJAABVCQAA\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAlwAAAC4BQAA\",\"\"]Ec[\"AAAAAWgFAAAAAY8BAAABLAUAAALKEAAA+xIAAAH0AAAA\",\"AAHdBwAAAAFoBQAAAAAAAsoQAAD7EgAA\"]Ec[\"AAAAAakFAAAAAaEBAAACawEAADAFAAAC0RAAAPwSAAA=\",\"AAH2BwAAAAGpBQAAAAAAAtEQAAD8EgAA\"]Ag[\"\",\"AAAAAAABqREAAA==\"]Co[\"AAAAAnkBAAD8BwAAAAABQAEAAA==\",\"AAAAAAAAAAAAAAAAAAAAAZkBAAA=\"]Ac[\"\",\"AAAAAAExEgAA\"]Cg[\"AAAAAAAAAgsSAAAMEgAAAAABrBEAAA==\",\"AAAAAAAAAAHZEgAA\"]Do[\"AAAAAXkFAAAAAjQAAACMAAAAAxUBAAAWAQAAYg8AAAEMAwAA\",\"AAAAAXkFAAAAAAABDAQAAA==\"]Ak[\"AAAAAAACJBMAACUTAAA=\",\"\"]Ac[\"AAAAAasBAAA=\",\"\"]Ac[\"AAAAAa0BAAA=\",\"\"]Eo[\"AAACugIAAM4CAAABnwUAAAABTwAAAAJaAQAAWwEAAA==\",\"AAAAAZ8FAAAAAAAFAwcAAAQHAAAFBwAABgcAAAcHAAA=\"]Fk[\"AAAABPIPAADzDwAAzRIAAM4SAAAB7A4AAAFMEAAAAaoRAAAAAAFGEwAAAAAAAAAAAAEmDAAA\",\"AAAAAAAAA7MRAAC0EQAAtREAAA==\"]Fc[\"AAAAAAAB7wYAAAILEgAADBIAAAGzEAAAAAGsEQAAAAACIBMAACETAAAAAAABdhIAAA==\",\"AAAAAAAAAVsTAAAB2RIAAA==\"]Ag[\"\",\"AAAAAAABsgUAAA==\"]Co[\"AAAAAYEFAAAAAT0AAAACJAEAACUBAAA=\",\"AAAAAYEFAAAAAAAAAAECDgAA\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAT4HAAA=\",\"\"]Jc[\"AAAAAlQFAABCEwAAAAAAAqARAABoEgAABNUAAACEAQAAKwwAAHMSAAADewkAAM8SAABGEwAAAAAAAUoQAAA=\",\"AAHYBwAAAAKHAQAAVAUAAAAAAAKgEQAAaBIAAAIrDAAAcxIAAAJ7CQAAzxIAAAAAAAFKEAAA\"]Jg[\"AAAAAoYFAABDEwAAAAABLAEAAAKhEQAAaRIAAAIsDAAAdBIAAAJ8CQAA0BIAAAGSAQAAAAABSxAAAA==\",\"AAHwBwAAAAKWAQAAhgUAAAH3BgAAAAACoREAAGkSAAACLAwAAHQSAAACfAkAANASAAAAAAABSxAAAA==\"]Ao[\"AAAAAAAAAa0AAAAAAAEIBAAA\",\"\"]Ag[\"AAACaAIAANsCAAA=\",\"\"]Ak[\"AAAAAAAAAiUEAAAlBwAA\",\"\"]Ak[\"AAAAAAAAAigEAAAoBwAA\",\"\"]Ag[\"AAACZwIAAOQCAAA=\",\"\"]Dk[\"AAAAAUMFAAAAAhAAAABVBwAAArcAAAC4AAAA\",\"AAABDQIAAAFDBQAAAAAAAAAAAQAPAAA=\"]Dg[\"AAAAAUAFAAAAAg0AAABSBwAAArEAAACyAAAA\",\"AAABBQIAAAFABQAAAAAAAAFuDwAA\"]Co[\"AAABsBEAAAF9BQAAAAE5AAAAAR0BAAA=\",\"AAHqBwAAAUMJAAABfQUAAA==\"]Ec[\"AAAAAAABsREAAAGhAAAAAAAAAAAAAAAAAAADHAQAAEAJAAAEDwAA\",\"AAAAAYEBAAAAAAAAAckSAAA=\"]Hk[\"AAACmAIAAKsCAAABlwUAAAABSQAAAAJLAQAATAEAAAAAAAAAAAAAAAAABN0BAAC6BQAAAgcAAD0HAAA=\",\"AAABvwIAAAKbAQAAlwUAAAAAAukSAADqEgAAAAAC6RAAAOoQAAA=\"]Fc[\"AAAAA+APAAD0DwAA9BEAAAACABIAAAISAAADcAEAAOESAADiEgAAAhUEAAC9CwAAAAAAAAABEAQAAA==\",\"AAAAAaIBAAA=\"]Eg[\"AAAAAVoFAAAAAR4AAAAAAALdAAAA3gAAAAAAAAAAAAAAAAM5BwAA3w4AAOUOAAA=\",\"AAABPwIAAAFaBQAA\"]Eg[\"AAAAAYwFAAAAAUEAAAACNAEAADUBAAAAAAAAAAAAAAAAAAM7BwAA4Q4AAOYOAAA=\",\"AAABqwIAAAGMBQAA\"]Hk[\"AAAF+wEAAAkCAAALAgAAEQIAABUCAAABTwUAAAACGgAAAFgHAAAAAALOAAAAzwAAAA==\",\"AAADLAIAAC4CAAAvAgAAAk8FAADzBgAAAhIGAADODwAAAZMSAAABKg8AAAG7EAAA\"]Ek[\"AAAAAX8FAAAAAjsAAABZBwAAAiABAAAhAQAA\",\"AAABmAIAAAF/BQAAAAAAAAAAAAAAAAAAAAACOAUAAD4HAAA=\"]Bk[\"AAAAAAAB4Q8AAAFGAQAA\",\"AAAAAAAAAAHiDwAA\"]Ek[\"AAAAAUwFAAAAAAAAAcoAAAAAAAAAAAAAAAAEYgcAAGQHAABECQAARgkAAA==\",\"AAHRBwAAAAJMBQAA1gUAAA==\"]Ek[\"AAAAAXcFAAAAAAESAQAAAAAAAAAAAAAAAAAEYwcAAGUHAABFCQAARwkAAA==\",\"AAHoBwAAAAF3BQAAAdgFAAA=\"]Ag[\"AAACYQIAAOECAAA=\",\"\"]Cg[\"AAAAAa8FAAAAAAAD8A4AAFQPAAD2DwAA\",\"AAH6BwAAAAGvBQAA\"]Dc[\"AAAAApEFAAD9BwAAAAABQQEAAA==\",\"AAAAAZEFAAAAAAAAAAAAAAAAAAELBwAA\"]Dg[\"AAAAAXcSAAABhQ8AAAAAAALQAAAAtxAAAA==\",\"AAFKCQAAAYYBAAABdxIAAAGFDwAA\"]Dg[\"AAAAAXgSAAABhg8AAAABJwEAAAABuRAAAA==\",\"AAFLCQAAAZUBAAABeBIAAAGGDwAA\"]Ac[\"\",\"AAAAAAFKEwAA\"]Ag[\"AAACYgIAAOICAAA=\",\"\"]Bc[\"\",\"AAAAAAAAAAAD4BEAAOERAADiEQAA\"]Bc[\"\",\"AAAAAAAAAAAD4xEAAOQRAADlEQAA\"]Ak[\"\",\"AAAAAQQSAAAAAAETEwAA\"]ADc[\"AAAAAVAFAAAAAAA6MAAAAQAAAAAAFAAQAAAAFAizDPEOOA87D3YPchDsEAAR/RFjEgMTWRNlE3ETcxOVE5kTnBOgE6cTOjAAAAEAAAAAAAwAEAAAANEA/g9uEPURBRIGEpQSvxLSEn0TjhOQE54TAAAB5RAAAA==\",\"AAHUBwAAAAFQBQAAAAAAOjAAAAEAAAAAABQAEAAAABQIswzxDjgPOw92D3IQ7BAAEf0RYxIDE1kTZRNxE3MTlROZE5wToBOnEzowAAABAAAAAAAJABAAAAD+D24Q9RGUEr8S0hJ9E44TkBOeEwAAAeUQAAA=\"]ADc[\"AAAAAYIFAAAAAAEoAQAAOjAAAAEAAAAAABQAEAAAABUItAzyDjkPPA93D3MQ7RABEf4RZBIEE1oTZhNyE3QTlhOaE50ToROoEzowAAABAAAAAAALABAAAAD/D28Q9hEHEggSlRLAEtMSfhOPE5ETnxMAAAHmEAAA\",\"AAHsBwAAAAGCBQAAAAAAOjAAAAEAAAAAABQAEAAAABUItAzyDjkPPA93D3MQ7RABEf4RZBIEE1oTZhNyE3QTlhOaE50ToROoEzowAAABAAAAAAAJABAAAAD/D28Q9hGVEsAS0xJ+E48TkROfEwAAAeYQAAA=\"]Ag[\"AAACOgIAAKcCAAA=\",\"\"]Bk[\"AAAAAAAAAAAAAf0DAAA=\",\"AAAAAAAAAQ4MAAA=\"]Do[\"AAAAAVsFAAAAAR8AAAAAAALfAAAA4AAAAAAAAAAAAAAAAAI6BwAA4A4AAA==\",\"AAAAAVsFAAA=\"]Do[\"AAAAAY0FAAAAAUIAAAACNgEAADcBAAAAAAAAAAAAAAAAAAI8BwAA4g4AAA==\",\"AAAAAY0FAAA=\"]Ag[\"AAACZQIAAOYCAAA=\",\"\"]Fc[\"AAAAAY8FAAAAAkMAAACPAAAAAjkBAABlDwAAAg8DAADiDwAA\",\"AAAAAY8FAAAAAAAAAAAAAAABwgcAAAAAAAABFAYAAA==\"]Ag[\"AAACXwIAAN8CAAA=\",\"\"]Ag[\"AAACZgIAAOUCAAA=\",\"\"]Ck[\"AAAAAAAAAAAAAAABeBAAAA==\",\"AAAAAVgKAAABVwoAAAABEBAAAA==\"]Co[\"AAAAAaMBAAAAAAAAAAAAAXwQAAA=\",\"AAAAAAG9CgAAAb4KAAABEhAAAA==\"]Dc[\"AAAAAAAAAALmDwAAeRAAAAABLgwAAA==\",\"AAAAAVsKAAABWgoAAAABERAAAA==\"]Dg[\"AAAAAaQBAAAAAAAC6g8AAH0QAAAAATAMAAA=\",\"AAAAAAHACgAAAcEKAAABExAAAA==\"]Cc[\"AAAAAAAAAiYEAAAmBwAAAAHoAAAA\",\"AAHaBwAAAAGLAQAA\"]Cc[\"AAAAAAAAA1EBAAApBAAAKQcAAA==\",\"AAHzBwAAAAGdAQAA\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAAzgHAAADCAAAIggAAA==\",\"\"]Ao[\"AAAAAAAAASkFAAAAAY4TAAA=\",\"\"]Bk[\"AAAAAAAAAS0FAAAAAY8TAAA=\",\"AAAAATEFAAA=\"]Eg[\"AAAAAAAAAAAB9QAAAAAAAAAAAAAAAAN9CQAAVQ8AACYSAAA=\",\"AAHeBwAAAAQMCAAADQgAAA4IAAAPCAAA\"]Eg[\"AAAAAAAAAWwBAAAAAAAAAAAAAAAAAAN+CQAAVg8AACcSAAA=\",\"AAH3BwAAAAQQCAAAEQgAABIIAAATCAAA\"]Bc[\"AAAAAAABiAEAAAABsRAAAAFxDAAA\",\"\"]Bc[\"AAAAAAABlwEAAAABshAAAAFyDAAA\",\"\"]Fo[\"AAAAAZQFAAAAAkYAAACQAAAAA0QBAABFAQAAZg8AAAEQAwAA\",\"AAABugIAAAGUBQAAAAAAAALBBQAAwgUAAAAAAAAAAAKZAQAACwcAAA==\"]Dg[\"AAAAAT0FAAAAAgwAAABRBwAAAqYAAACnAAAA\",\"AAAB+wEAAAE9BQAAAAABcRAAAA==\"]Do[\"AAAAAW0FAAAAAiwAAACJAAAAAwQBAAAFAQAAXw8AAAEJAwAA\",\"AAAAAW0FAAAAAAAAARQEAAA=\"]Ek[\"AAAAAVEFAAAAAAAC2hIAACwTAAAD0gAAAFQQAAC/EgAA\",\"AAHVBwAAAAFRBQAAAAAAAtoSAAAsEwAAAVQQAAA=\"]Ek[\"AAAAAYMFAAAAAAEpAQAAAtsSAAAtEwAAAlUQAADAEgAA\",\"AAHtBwAAAAGDBQAAAAAAAtsSAAAtEwAAAVUQAAA=\"]Bo[\"AAAAAAAAAAABwQUAAAAAAAAAAAKZAQAACwcAAA==\",\"\"]Fg[\"AAAAAVIFAAAAAAAEXQkAAIAPAADdEAAAjRIAAALTAAAA0hIAAA==\",\"AAHWBwAAAAFSBQAAAAAABF0JAACADwAA3RAAAI0SAAA=\"]Fg[\"AAAAAYQFAAAAAAEqAQAABF4JAACBDwAA3hAAAI4SAAAB0xIAAA==\",\"AAHuBwAAAAGEBQAAAAAABF4JAACBDwAA3hAAAI4SAAA=\"]Ag[\"\",\"AAAAAAAAAVsTAAA=\"]Bc[\"\",\"AAAAAAAAAAAAAAAAAAAAAAF2EgAA\"]Ag[\"\",\"AAAAAAAAAQEPAAA=\"]Co[\"AAAAAXIFAAAAATAAAAACCwEAAAwBAAA=\",\"AAAAAXIFAAAAAAAAAa0RAAA=\"]Fg[\"AAABIQIAAAFfBQAAAAEjAAAAAAETBwAAAuYAAADnAAAA\",\"AAACRgIAAPsGAAABXwUAAAAAAAEGBwAAAAAAAAAAAAAAAAHfDgAA\"]Gc[\"AAABfAIAAAKbAQAAmAUAAAABSgAAAAJNAQAATgEAAAEZBwAA\",\"AAACwQIAAP8GAAABmAUAAAAAAQwHAAAAAAAAAAAAAAAAAALdAQAA4Q4AAA==\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAA/8HAAAfCAAABQkAAA==\",\"\"]Cg[\"AAABQgIAAAAAAAAAAfgAAAA=\",\"AAAAAAAAAALkDwAA5g8AAA==\"]Cg[\"AAABsQIAAAAAAAFhAQAA\",\"AAAAAAAAAAPoDwAA6g8AAOsPAAA=\"]Bo[\"AAAAAjEFAACuBQAAATESAAA=\",\"AAAAAq4FAAADEQAA\"]Ao[\"\",\"AAAAAAAAAAKAEAAAgRAAAA==\"]Io[\"AAAAAaAFAAAAAVAAAAABXAEAAAbiBQAAdxAAAHkQAAB7EAAAfRAAAH8QAAAAAAAFdhAAAHgQAAB6EAAAfBAAAH4QAAAAAAAAAAABphMAAA==\",\"AAAAAaAFAAAAAATmEgAA5xIAAOkSAADqEgAAAAENBAAA\"]Co[\"AAAAAlcAAAALCAAAAAABsAAAAAIiBAAA8BAAAAH/DgAA\",\"AAAB0hAAAA==\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAlIJAADYDwAA\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAlQJAADaDwAA\",\"\"]Co[\"AAAAAWkFAAAAAAAAAvkAAAD6AAAA\",\"AAAAAWkFAAAAAAIQEAAAERAAAA==\"]Dc[\"AAAAAaIFAAAAAAJiAQAAYwEAAA==\",\"AAAAAqIFAADmEQAAAAACEhAAABMQAAA=\"]Ac[\"\",\"AAAB+QsAAA==\"]Ec[\"AAABPAIAAAFHBQAAAAEUAAAAAAACwgAAAMMAAAA=\",\"AAAAAUcFAAAEUAkAACMKAAAkCgAAJQoAAA==\"]Do[\"AAABqQIAAAFsBQAAAAEqAAAAAgEBAAACAQAA\",\"AAAAAWwFAAAEUQkAAG8KAABwCgAAcQoAAA==\"]Ak[\"\",\"AAAAAAACJBMAACUTAAA=\"]Cg[\"AAAAAjwFAABnCQAAAQEJAAAAAaUAAAA=\",\"AAAAAjwFAACCDwAA\"]Cg[\"AAAAAaIHAAACyg8AANwPAAAAAb4LAAAAAAAAAAABShAAAA==\",\"\"]Ck[\"AAAAAaMHAAACyw8AAN0PAAAAAr8LAADACwAAAAAAAAAAAUsQAAA=\",\"\"]Bc[\"AAAAAAAAAAAAAAAAAiATAAAhEwAA\",\"\"]Fo[\"AAAAAVYFAAAAAAADFgQAAGgJAAA8EwAAAtcAAAB9AQAA\",\"AAHZBwAAAAFWBQAAAAAAAhYEAABoCQAAAX0BAAAAAAAAAAAAAAABCRIAAA==\"]Fo[\"AAAAAYgFAAAAAAEuAQAAAxcEAABpCQAAPRMAAAF+AQAA\",\"AAHxBwAAAAGIBQAAAAAAAhcEAABpCQAAAX4BAAAAAAAAAAAAAAABChIAAA==\"]Ec[\"AAAAAZIFAAAAAAFCAQAAAAAAAAAAAAAAAAAFegEAAP4HAAAeCAAABAkAAHMMAAA=\",\"AAAAAZIFAAA=\"]Dg[\"AAAAAAFKEwAAAAGiAAAAAAAAAAACIBMAACETAAAAAAABdhIAAA==\",\"AAABggEAAA==\"]Ag[\"AAACZAIAANwCAAA=\",\"\"]Bc[\"\",\"AAAAAAAAAAAAAAAAAiATAAAhEwAA\"]Bk[\"AAAAAAAAAAAAAAABfhAAAA==\",\"AAAAAaMBAAA=\"]Cc[\"AAAAAAAAAALrDwAAfxAAAAABMQwAAA==\",\"AAAAAaQBAAA=\"]Ag[\"AAACYwIAAOMCAAA=\",\"\"]Dg[\"AAAAAAABJwAAAAAAAfYAAAA=\",\"AAAAAAAAAAAAAAAAAAAAAxQTAAAVEwAAFhMAAA==\"]Ck[\"AAAAAAABUgAAAAFfAQAA\",\"AAAAAAAAAAAAAAAAAAAAAhcTAAAYEwAA\"]Ag[\"AAACYAIAAN4CAAA=\",\"\"]Do[\"AAAAAaQHAAACzA8AAN4PAAAAAcELAAABCBAAAA==\",\"AAKUAQAA4AcAAAFMCQAAAAAAAAEIEAAA\"]Ec[\"AAAAApoFAAClBwAAAs0PAADfDwAAAAFQAQAAAQkQAAA=\",\"AAHyBwAAAZwBAAABmgUAAAAAAAEJEAAA\"]Bg[\"AAAAAAAAAAABwgUAAA==\",\"AAABkwEAAA==\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAlMJAADZDwAA\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAlUJAADbDwAA\",\"\"]") \ No newline at end of file diff --git a/pr/299/docs/set_up_connection/all.html b/pr/299/docs/set_up_connection/all.html new file mode 100644 index 000000000..93799f9a4 --- /dev/null +++ b/pr/299/docs/set_up_connection/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

\ No newline at end of file diff --git a/pr/299/docs/set_up_connection/constant.CLIENT_ADDR.html b/pr/299/docs/set_up_connection/constant.CLIENT_ADDR.html new file mode 100644 index 000000000..480b4b634 --- /dev/null +++ b/pr/299/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/299/docs/set_up_connection/constant.LOCALHOST_V4.html b/pr/299/docs/set_up_connection/constant.LOCALHOST_V4.html new file mode 100644 index 000000000..49f7c0f99 --- /dev/null +++ b/pr/299/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/299/docs/set_up_connection/constant.SERVER_ADDR.html b/pr/299/docs/set_up_connection/constant.SERVER_ADDR.html new file mode 100644 index 000000000..046828f6b --- /dev/null +++ b/pr/299/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/299/docs/set_up_connection/constant.SERVER_NAME.html b/pr/299/docs/set_up_connection/constant.SERVER_NAME.html new file mode 100644 index 000000000..84fc92733 --- /dev/null +++ b/pr/299/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/299/docs/set_up_connection/fn.client.html b/pr/299/docs/set_up_connection/fn.client.html new file mode 100644 index 000000000..0b2708bd9 --- /dev/null +++ b/pr/299/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/299/docs/set_up_connection/fn.main.html b/pr/299/docs/set_up_connection/fn.main.html new file mode 100644 index 000000000..23244837f --- /dev/null +++ b/pr/299/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/299/docs/set_up_connection/fn.server.html b/pr/299/docs/set_up_connection/fn.server.html new file mode 100644 index 000000000..2cfe5a51d --- /dev/null +++ b/pr/299/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/299/docs/set_up_connection/index.html b/pr/299/docs/set_up_connection/index.html new file mode 100644 index 000000000..9d78e0b7f --- /dev/null +++ b/pr/299/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/299/docs/set_up_connection/sidebar-items.js b/pr/299/docs/set_up_connection/sidebar-items.js new file mode 100644 index 000000000..3f14772bc --- /dev/null +++ b/pr/299/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/299/docs/settings.html b/pr/299/docs/settings.html new file mode 100644 index 000000000..abf467830 --- /dev/null +++ b/pr/299/docs/settings.html @@ -0,0 +1 @@ +Settings

All

Rustdoc settings

Back
\ No newline at end of file diff --git a/pr/299/docs/src-files.js b/pr/299/docs/src-files.js new file mode 100644 index 000000000..bdda437df --- /dev/null +++ b/pr/299/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/299/docs/src/bench/lib.rs.html b/pr/299/docs/src/bench/lib.rs.html new file mode 100644 index 000000000..95cda99f5 --- /dev/null +++ b/pr/299/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            CipherSuite::Aes128 => cipher_suite::TLS13_AES_128_GCM_SHA256,
+239            CipherSuite::Aes256 => cipher_suite::TLS13_AES_256_GCM_SHA384,
+240            CipherSuite::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(CipherSuite::Aes128),
+251            "aes256" => Ok(CipherSuite::Aes256),
+252            "chacha20" => Ok(CipherSuite::Chacha20),
+253            _ => Err(anyhow::anyhow!("Unknown cipher suite {}", s)),
+254        }
+255    }
+256}
\ No newline at end of file diff --git a/pr/299/docs/src/bench/stats.rs.html b/pr/299/docs/src/bench/stats.rs.html new file mode 100644 index 000000000..959a01e82 --- /dev/null +++ b/pr/299/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        TransferResult {
+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/299/docs/src/bulk/bulk.rs.html b/pr/299/docs/src/bulk/bulk.rs.html new file mode 100644 index 000000000..26522c25b --- /dev/null +++ b/pr/299/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    pub 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/299/docs/src/certificate/certificate.rs.html b/pr/299/docs/src/certificate/certificate.rs.html new file mode 100644 index 000000000..e8d65d513 --- /dev/null +++ b/pr/299/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/299/docs/src/data_transfer/data-transfer.rs.html b/pr/299/docs/src/data_transfer/data-transfer.rs.html new file mode 100644 index 000000000..fb76e6aea --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn/connection.rs.html b/pr/299/docs/src/iroh_quinn/connection.rs.html new file mode 100644 index 000000000..d52b89625 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn/connection.rs.html @@ -0,0 +1,1759 @@ +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(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        self.0.state.lock("on_closed").on_closed.push(tx);
+565        OnClosed {
+566            conn: self.weak_handle(),
+567            rx,
+568        }
+569    }
+570
+571    /// If the connection is closed, the reason why.
+572    ///
+573    /// Returns `None` if the connection is still open.
+574    pub fn close_reason(&self) -> Option<ConnectionError> {
+575        self.0.state.lock("close_reason").error.clone()
+576    }
+577
+578    /// Close the connection immediately.
+579    ///
+580    /// Pending operations will fail immediately with [`ConnectionError::LocallyClosed`]. No
+581    /// more data is sent to the peer and the peer may drop buffered data upon receiving
+582    /// the CONNECTION_CLOSE frame.
+583    ///
+584    /// `error_code` and `reason` are not interpreted, and are provided directly to the peer.
+585    ///
+586    /// `reason` will be truncated to fit in a single packet with overhead; to improve odds that it
+587    /// is preserved in full, it should be kept under 1KiB.
+588    ///
+589    /// # Gracefully closing a connection
+590    ///
+591    /// Only the peer last receiving application data can be certain that all data is
+592    /// delivered. The only reliable action it can then take is to close the connection,
+593    /// potentially with a custom error code. The delivery of the final CONNECTION_CLOSE
+594    /// frame is very likely if both endpoints stay online long enough, and
+595    /// [`Endpoint::wait_idle()`] can be used to provide sufficient time. Otherwise, the
+596    /// remote peer will time out the connection, provided that the idle timeout is not
+597    /// disabled.
+598    ///
+599    /// The sending side can not guarantee all stream data is delivered to the remote
+600    /// application. It only knows the data is delivered to the QUIC stack of the remote
+601    /// endpoint. Once the local side sends a CONNECTION_CLOSE frame in response to calling
+602    /// [`close()`] the remote endpoint may drop any data it received but is as yet
+603    /// undelivered to the application, including data that was acknowledged as received to
+604    /// the local endpoint.
+605    ///
+606    /// [`ConnectionError::LocallyClosed`]: crate::ConnectionError::LocallyClosed
+607    /// [`Endpoint::wait_idle()`]: crate::Endpoint::wait_idle
+608    /// [`close()`]: Connection::close
+609    pub fn close(&self, error_code: VarInt, reason: &[u8]) {
+610        let conn = &mut *self.0.state.lock("close");
+611        conn.close(error_code, Bytes::copy_from_slice(reason), &self.0.shared);
+612    }
+613
+614    /// Wait for the handshake to be confirmed.
+615    ///
+616    /// As a server, who must be authenticated by clients,
+617    /// this happens when the handshake completes
+618    /// upon receiving a TLS Finished message from the client.
+619    /// In return, the server send a HANDSHAKE_DONE frame.
+620    ///
+621    /// As a client, this happens when receiving a HANDSHAKE_DONE frame.
+622    /// At this point, the server has either accepted our authentication,
+623    /// or, if client authentication is not required, accepted our lack of authentication.
+624    pub async fn handshake_confirmed(&self) -> Result<(), ConnectionError> {
+625        {
+626            let conn = self.0.state.lock("handshake_confirmed");
+627            if let Some(error) = conn.error.as_ref() {
+628                return Err(error.clone());
+629            }
+630            if conn.handshake_confirmed {
+631                return Ok(());
+632            }
+633            // Construct the future while the lock is held to ensure we can't miss a wakeup if
+634            // the `Notify` is signaled immediately after we release the lock. `await` it after
+635            // the lock guard is out of scope.
+636            self.0.shared.handshake_confirmed.notified()
+637        }
+638        .await;
+639        if let Some(error) = self.0.state.lock("handshake_confirmed").error.as_ref() {
+640            Err(error.clone())
+641        } else {
+642            Ok(())
+643        }
+644    }
+645
+646    /// Transmit `data` as an unreliable, unordered application datagram
+647    ///
+648    /// Application datagrams are a low-level primitive. They may be lost or delivered out of order,
+649    /// and `data` must both fit inside a single QUIC packet and be smaller than the maximum
+650    /// dictated by the peer.
+651    ///
+652    /// Previously queued datagrams which are still unsent may be discarded to make space for this
+653    /// datagram, in order of oldest to newest.
+654    pub fn send_datagram(&self, data: Bytes) -> Result<(), SendDatagramError> {
+655        let conn = &mut *self.0.state.lock("send_datagram");
+656        if let Some(ref x) = conn.error {
+657            return Err(SendDatagramError::ConnectionLost(x.clone()));
+658        }
+659        use proto::SendDatagramError::*;
+660        match conn.inner.datagrams().send(data, true) {
+661            Ok(()) => {
+662                conn.wake();
+663                Ok(())
+664            }
+665            Err(e) => Err(match e {
+666                Blocked(..) => unreachable!(),
+667                UnsupportedByPeer => SendDatagramError::UnsupportedByPeer,
+668                Disabled => SendDatagramError::Disabled,
+669                TooLarge => SendDatagramError::TooLarge,
+670            }),
+671        }
+672    }
+673
+674    /// Transmit `data` as an unreliable, unordered application datagram
+675    ///
+676    /// Unlike [`send_datagram()`], this method will wait for buffer space during congestion
+677    /// conditions, which effectively prioritizes old datagrams over new datagrams.
+678    ///
+679    /// See [`send_datagram()`] for details.
+680    ///
+681    /// [`send_datagram()`]: Connection::send_datagram
+682    pub fn send_datagram_wait(&self, data: Bytes) -> SendDatagram<'_> {
+683        SendDatagram {
+684            conn: &self.0,
+685            data: Some(data),
+686            notify: self.0.shared.datagrams_unblocked.notified(),
+687        }
+688    }
+689
+690    /// Compute the maximum size of datagrams that may be passed to [`send_datagram()`].
+691    ///
+692    /// Returns `None` if datagrams are unsupported by the peer or disabled locally.
+693    ///
+694    /// This may change over the lifetime of a connection according to variation in the path MTU
+695    /// estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer's
+696    /// limit is large this is guaranteed to be a little over a kilobyte at minimum.
+697    ///
+698    /// Not necessarily the maximum size of received datagrams.
+699    ///
+700    /// [`send_datagram()`]: Connection::send_datagram
+701    pub fn max_datagram_size(&self) -> Option<usize> {
+702        self.0
+703            .state
+704            .lock("max_datagram_size")
+705            .inner
+706            .datagrams()
+707            .max_size()
+708    }
+709
+710    /// Bytes available in the outgoing datagram buffer
+711    ///
+712    /// When greater than zero, calling [`send_datagram()`](Self::send_datagram) with a datagram of
+713    /// at most this size is guaranteed not to cause older datagrams to be dropped.
+714    pub fn datagram_send_buffer_space(&self) -> usize {
+715        self.0
+716            .state
+717            .lock("datagram_send_buffer_space")
+718            .inner
+719            .datagrams()
+720            .send_buffer_space()
+721    }
+722
+723    /// The side of the connection (client or server)
+724    pub fn side(&self) -> Side {
+725        self.0.state.lock("side").inner.side()
+726    }
+727
+728    /// The peer's UDP address
+729    ///
+730    /// If [`ServerConfig::migration`] is `true`, clients may change addresses at will,
+731    /// e.g. when switching to a cellular internet connection.
+732    ///
+733    /// If [`multipath`] is enabled this will return the address of *any*
+734    /// path, and may not be consistent. Prefer [`Path::remote_address`] instead.
+735    ///
+736    /// [`ServerConfig::migration`]: crate::ServerConfig::migration
+737    /// [`multipath`]: crate::TransportConfig::max_concurrent_multipath_paths
+738    pub fn remote_address(&self) -> SocketAddr {
+739        // TODO: an unwrap again
+740        let state = self.0.state.lock("remote_address");
+741        state
+742            .inner
+743            .paths()
+744            .iter()
+745            .filter_map(|id| state.inner.network_path(*id).ok())
+746            .next()
+747            .unwrap()
+748            .remote
+749    }
+750
+751    /// The local IP address which was used when the peer established
+752    /// the connection
+753    ///
+754    /// This can be different from the address the endpoint is bound to, in case
+755    /// the endpoint is bound to a wildcard address like `0.0.0.0` or `::`.
+756    ///
+757    /// This will return `None` for clients, or when the platform does not expose this
+758    /// information. See [`quinn_udp::RecvMeta::dst_ip`](udp::RecvMeta::dst_ip) for a list of
+759    /// supported platforms when using [`quinn_udp`](udp) for I/O, which is the default.
+760    pub fn local_ip(&self) -> Option<IpAddr> {
+761        // TODO: an unwrap again
+762        let state = self.0.state.lock("remote_address");
+763        state
+764            .inner
+765            .paths()
+766            .iter()
+767            .filter_map(|id| state.inner.network_path(*id).ok())
+768            .next()
+769            .unwrap()
+770            .local_ip
+771    }
+772
+773    /// Current best estimate of this connection's latency (round-trip-time)
+774    pub fn rtt(&self, path_id: PathId) -> Option<Duration> {
+775        self.0.state.lock("rtt").inner.rtt(path_id)
+776    }
+777
+778    /// Returns connection statistics
+779    pub fn stats(&self) -> ConnectionStats {
+780        self.0.state.lock("stats").inner.stats()
+781    }
+782
+783    /// Returns path statistics
+784    pub fn path_stats(&self, path_id: PathId) -> Option<PathStats> {
+785        self.0.state.lock("path_stats").inner.path_stats(path_id)
+786    }
+787
+788    /// Current state of the congestion control algorithm, for debugging purposes
+789    pub fn congestion_state(&self, path_id: PathId) -> Option<Box<dyn Controller>> {
+790        self.0
+791            .state
+792            .lock("congestion_state")
+793            .inner
+794            .congestion_state(path_id)
+795            .map(|c| c.clone_box())
+796    }
+797
+798    /// Parameters negotiated during the handshake
+799    ///
+800    /// Guaranteed to return `Some` on fully established connections or after
+801    /// [`Connecting::handshake_data()`] succeeds. See that method's documentations for details on
+802    /// the returned value.
+803    ///
+804    /// [`Connection::handshake_data()`]: crate::Connecting::handshake_data
+805    pub fn handshake_data(&self) -> Option<Box<dyn Any>> {
+806        self.0
+807            .state
+808            .lock("handshake_data")
+809            .inner
+810            .crypto_session()
+811            .handshake_data()
+812    }
+813
+814    /// Cryptographic identity of the peer
+815    ///
+816    /// The dynamic type returned is determined by the configured
+817    /// [`Session`](proto::crypto::Session). For the default `rustls` session, the return value can
+818    /// be [`downcast`](Box::downcast) to a <code>Vec<[rustls::pki_types::CertificateDer]></code>
+819    pub fn peer_identity(&self) -> Option<Box<dyn Any>> {
+820        self.0
+821            .state
+822            .lock("peer_identity")
+823            .inner
+824            .crypto_session()
+825            .peer_identity()
+826    }
+827
+828    /// A stable identifier for this connection
+829    ///
+830    /// Peer addresses and connection IDs can change, but this value will remain
+831    /// fixed for the lifetime of the connection.
+832    pub fn stable_id(&self) -> usize {
+833        self.0.stable_id()
+834    }
+835
+836    /// Update traffic keys spontaneously
+837    ///
+838    /// This primarily exists for testing purposes.
+839    pub fn force_key_update(&self) {
+840        self.0
+841            .state
+842            .lock("force_key_update")
+843            .inner
+844            .force_key_update()
+845    }
+846
+847    /// Derive keying material from this connection's TLS session secrets.
+848    ///
+849    /// When both peers call this method with the same `label` and `context`
+850    /// arguments and `output` buffers of equal length, they will get the
+851    /// same sequence of bytes in `output`. These bytes are cryptographically
+852    /// strong and pseudorandom, and are suitable for use as keying material.
+853    ///
+854    /// See [RFC5705](https://tools.ietf.org/html/rfc5705) for more information.
+855    pub fn export_keying_material(
+856        &self,
+857        output: &mut [u8],
+858        label: &[u8],
+859        context: &[u8],
+860    ) -> Result<(), proto::crypto::ExportKeyingMaterialError> {
+861        self.0
+862            .state
+863            .lock("export_keying_material")
+864            .inner
+865            .crypto_session()
+866            .export_keying_material(output, label, context)
+867    }
+868
+869    /// Modify the number of remotely initiated unidirectional streams that may be concurrently open
+870    ///
+871    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+872    /// `count`s increase both minimum and worst-case memory consumption.
+873    pub fn set_max_concurrent_uni_streams(&self, count: VarInt) {
+874        let mut conn = self.0.state.lock("set_max_concurrent_uni_streams");
+875        conn.inner.set_max_concurrent_streams(Dir::Uni, count);
+876        // May need to send MAX_STREAMS to make progress
+877        conn.wake();
+878    }
+879
+880    /// See [`proto::TransportConfig::send_window()`]
+881    pub fn set_send_window(&self, send_window: u64) {
+882        let mut conn = self.0.state.lock("set_send_window");
+883        conn.inner.set_send_window(send_window);
+884        conn.wake();
+885    }
+886
+887    /// See [`proto::TransportConfig::receive_window()`]
+888    pub fn set_receive_window(&self, receive_window: VarInt) {
+889        let mut conn = self.0.state.lock("set_receive_window");
+890        conn.inner.set_receive_window(receive_window);
+891        conn.wake();
+892    }
+893
+894    /// Modify the number of remotely initiated bidirectional streams that may be concurrently open
+895    ///
+896    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+897    /// `count`s increase both minimum and worst-case memory consumption.
+898    pub fn set_max_concurrent_bi_streams(&self, count: VarInt) {
+899        let mut conn = self.0.state.lock("set_max_concurrent_bi_streams");
+900        conn.inner.set_max_concurrent_streams(Dir::Bi, count);
+901        // May need to send MAX_STREAMS to make progress
+902        conn.wake();
+903    }
+904
+905    /// Track changed on our external address as reported by the peer.
+906    pub fn observed_external_addr(&self) -> watch::Receiver<Option<SocketAddr>> {
+907        let conn = self.0.state.lock("external_addr");
+908        conn.observed_external_addr.subscribe()
+909    }
+910
+911    /// Is multipath enabled?
+912    // TODO(flub): not a useful API, once we do real things with multipath we can remove
+913    // this again.
+914    pub fn is_multipath_enabled(&self) -> bool {
+915        let conn = self.0.state.lock("is_multipath_enabled");
+916        conn.inner.is_multipath_negotiated()
+917    }
+918
+919    /// Registers one address at which this endpoint might be reachable
+920    ///
+921    /// When the NAT traversal extension is negotiated, servers send these addresses to clients in
+922    /// `ADD_ADDRESS` frames. This allows clients to obtain server address candidates to initiate
+923    /// NAT traversal attempts. Clients provide their own reachable addresses in `REACH_OUT` frames
+924    /// when [`Self::initiate_nat_traversal_round`] is called.
+925    pub fn add_nat_traversal_address(&self, address: SocketAddr) -> Result<(), iroh_hp::Error> {
+926        let mut conn = self.0.state.lock("add_nat_traversal_addresses");
+927        conn.inner.add_nat_traversal_address(address)
+928    }
+929
+930    /// Removes one or more addresses from the set of addresses at which this endpoint is reachable
+931    ///
+932    /// When the NAT traversal extension is negotiated, servers send address removals to
+933    /// clients in `REMOVE_ADDRESS` frames. This allows clients to stop using outdated
+934    /// server address candidates that are no longer valid for NAT traversal.
+935    ///
+936    /// For clients, removed addresses will no longer be advertised in `REACH_OUT` frames.
+937    ///
+938    /// Addresses not present in the set will be silently ignored.
+939    pub fn remove_nat_traversal_address(&self, address: SocketAddr) -> Result<(), iroh_hp::Error> {
+940        let mut conn = self.0.state.lock("remove_nat_traversal_addresses");
+941        conn.inner.remove_nat_traversal_address(address)
+942    }
+943
+944    /// Get the current local nat traversal addresses
+945    pub fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+946        let conn = self.0.state.lock("get_local_nat_traversal_addresses");
+947        conn.inner.get_local_nat_traversal_addresses()
+948    }
+949
+950    /// Get the currently advertised nat traversal addresses by the server
+951    pub fn get_remote_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+952        let conn = self.0.state.lock("get_remote_nat_traversal_addresses");
+953        conn.inner.get_remote_nat_traversal_addresses()
+954    }
+955
+956    /// Initiates a new nat traversal round
+957    ///
+958    /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT`
+959    /// frames, and initiating probing of the known remote addresses. When a new round is
+960    /// initiated, the previous one is cancelled, and paths that have not been opened are closed.
+961    ///
+962    /// Returns the server addresses that are now being probed.
+963    pub fn initiate_nat_traversal_round(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+964        let mut conn = self.0.state.lock("initiate_nat_traversal_round");
+965        let now = conn.runtime.now();
+966        conn.inner.initiate_nat_traversal_round(now)
+967    }
+968}
+969
+970pin_project! {
+971    /// Future produced by [`Connection::open_uni`]
+972    pub struct OpenUni<'a> {
+973        conn: &'a ConnectionRef,
+974        #[pin]
+975        notify: Notified<'a>,
+976    }
+977}
+978
+979impl Future for OpenUni<'_> {
+980    type Output = Result<SendStream, ConnectionError>;
+981    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+982        let this = self.project();
+983        let (conn, id, is_0rtt) = ready!(poll_open(ctx, this.conn, this.notify, Dir::Uni))?;
+984        Poll::Ready(Ok(SendStream::new(conn, id, is_0rtt)))
+985    }
+986}
+987
+988pin_project! {
+989    /// Future produced by [`Connection::open_bi`]
+990    pub struct OpenBi<'a> {
+991        conn: &'a ConnectionRef,
+992        #[pin]
+993        notify: Notified<'a>,
+994    }
+995}
+996
+997impl Future for OpenBi<'_> {
+998    type Output = Result<(SendStream, RecvStream), ConnectionError>;
+999    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1000        let this = self.project();
+1001        let (conn, id, is_0rtt) = ready!(poll_open(ctx, this.conn, this.notify, Dir::Bi))?;
+1002
+1003        Poll::Ready(Ok((
+1004            SendStream::new(conn.clone(), id, is_0rtt),
+1005            RecvStream::new(conn, id, is_0rtt),
+1006        )))
+1007    }
+1008}
+1009
+1010fn poll_open<'a>(
+1011    ctx: &mut Context<'_>,
+1012    conn: &'a ConnectionRef,
+1013    mut notify: Pin<&mut Notified<'a>>,
+1014    dir: Dir,
+1015) -> Poll<Result<(ConnectionRef, StreamId, bool), ConnectionError>> {
+1016    let mut state = conn.state.lock("poll_open");
+1017    if let Some(ref e) = state.error {
+1018        return Poll::Ready(Err(e.clone()));
+1019    } else if let Some(id) = state.inner.streams().open(dir) {
+1020        let is_0rtt = state.inner.side().is_client() && state.inner.is_handshaking();
+1021        drop(state); // Release the lock so clone can take it
+1022        return Poll::Ready(Ok((conn.clone(), id, is_0rtt)));
+1023    }
+1024    loop {
+1025        match notify.as_mut().poll(ctx) {
+1026            // `state` lock ensures we didn't race with readiness
+1027            Poll::Pending => return Poll::Pending,
+1028            // Spurious wakeup, get a new future
+1029            Poll::Ready(()) => {
+1030                notify.set(conn.shared.stream_budget_available[dir as usize].notified())
+1031            }
+1032        }
+1033    }
+1034}
+1035
+1036pin_project! {
+1037    /// Future produced by [`Connection::accept_uni`]
+1038    pub struct AcceptUni<'a> {
+1039        conn: &'a ConnectionRef,
+1040        #[pin]
+1041        notify: Notified<'a>,
+1042    }
+1043}
+1044
+1045impl Future for AcceptUni<'_> {
+1046    type Output = Result<RecvStream, ConnectionError>;
+1047
+1048    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1049        let this = self.project();
+1050        let (conn, id, is_0rtt) = ready!(poll_accept(ctx, this.conn, this.notify, Dir::Uni))?;
+1051        Poll::Ready(Ok(RecvStream::new(conn, id, is_0rtt)))
+1052    }
+1053}
+1054
+1055pin_project! {
+1056    /// Future produced by [`Connection::accept_bi`]
+1057    pub struct AcceptBi<'a> {
+1058        conn: &'a ConnectionRef,
+1059        #[pin]
+1060        notify: Notified<'a>,
+1061    }
+1062}
+1063
+1064impl Future for AcceptBi<'_> {
+1065    type Output = Result<(SendStream, RecvStream), ConnectionError>;
+1066
+1067    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1068        let this = self.project();
+1069        let (conn, id, is_0rtt) = ready!(poll_accept(ctx, this.conn, this.notify, Dir::Bi))?;
+1070        Poll::Ready(Ok((
+1071            SendStream::new(conn.clone(), id, is_0rtt),
+1072            RecvStream::new(conn, id, is_0rtt),
+1073        )))
+1074    }
+1075}
+1076
+1077fn poll_accept<'a>(
+1078    ctx: &mut Context<'_>,
+1079    conn: &'a ConnectionRef,
+1080    mut notify: Pin<&mut Notified<'a>>,
+1081    dir: Dir,
+1082) -> Poll<Result<(ConnectionRef, StreamId, bool), ConnectionError>> {
+1083    let mut state = conn.state.lock("poll_accept");
+1084    // Check for incoming streams before checking `state.error` so that already-received streams,
+1085    // which are necessarily finite, can be drained from a closed connection.
+1086    if let Some(id) = state.inner.streams().accept(dir) {
+1087        let is_0rtt = state.inner.is_handshaking();
+1088        state.wake(); // To send additional stream ID credit
+1089        drop(state); // Release the lock so clone can take it
+1090        return Poll::Ready(Ok((conn.clone(), id, is_0rtt)));
+1091    } else if let Some(ref e) = state.error {
+1092        return Poll::Ready(Err(e.clone()));
+1093    }
+1094    loop {
+1095        match notify.as_mut().poll(ctx) {
+1096            // `state` lock ensures we didn't race with readiness
+1097            Poll::Pending => return Poll::Pending,
+1098            // Spurious wakeup, get a new future
+1099            Poll::Ready(()) => notify.set(conn.shared.stream_incoming[dir as usize].notified()),
+1100        }
+1101    }
+1102}
+1103
+1104pin_project! {
+1105    /// Future produced by [`Connection::read_datagram`]
+1106    pub struct ReadDatagram<'a> {
+1107        conn: &'a ConnectionRef,
+1108        #[pin]
+1109        notify: Notified<'a>,
+1110    }
+1111}
+1112
+1113impl Future for ReadDatagram<'_> {
+1114    type Output = Result<Bytes, ConnectionError>;
+1115    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1116        let mut this = self.project();
+1117        let mut state = this.conn.state.lock("ReadDatagram::poll");
+1118        // Check for buffered datagrams before checking `state.error` so that already-received
+1119        // datagrams, which are necessarily finite, can be drained from a closed connection.
+1120        if let Some(x) = state.inner.datagrams().recv() {
+1121            return Poll::Ready(Ok(x));
+1122        } else if let Some(ref e) = state.error {
+1123            return Poll::Ready(Err(e.clone()));
+1124        }
+1125        loop {
+1126            match this.notify.as_mut().poll(ctx) {
+1127                // `state` lock ensures we didn't race with readiness
+1128                Poll::Pending => return Poll::Pending,
+1129                // Spurious wakeup, get a new future
+1130                Poll::Ready(()) => this
+1131                    .notify
+1132                    .set(this.conn.shared.datagram_received.notified()),
+1133            }
+1134        }
+1135    }
+1136}
+1137
+1138pin_project! {
+1139    /// Future produced by [`Connection::send_datagram_wait`]
+1140    pub struct SendDatagram<'a> {
+1141        conn: &'a ConnectionRef,
+1142        data: Option<Bytes>,
+1143        #[pin]
+1144        notify: Notified<'a>,
+1145    }
+1146}
+1147
+1148impl Future for SendDatagram<'_> {
+1149    type Output = Result<(), SendDatagramError>;
+1150    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1151        let mut this = self.project();
+1152        let mut state = this.conn.state.lock("SendDatagram::poll");
+1153        if let Some(ref e) = state.error {
+1154            return Poll::Ready(Err(SendDatagramError::ConnectionLost(e.clone())));
+1155        }
+1156        use proto::SendDatagramError::*;
+1157        match state
+1158            .inner
+1159            .datagrams()
+1160            .send(this.data.take().unwrap(), false)
+1161        {
+1162            Ok(()) => {
+1163                state.wake();
+1164                Poll::Ready(Ok(()))
+1165            }
+1166            Err(e) => Poll::Ready(Err(match e {
+1167                Blocked(data) => {
+1168                    this.data.replace(data);
+1169                    loop {
+1170                        match this.notify.as_mut().poll(ctx) {
+1171                            Poll::Pending => return Poll::Pending,
+1172                            // Spurious wakeup, get a new future
+1173                            Poll::Ready(()) => this
+1174                                .notify
+1175                                .set(this.conn.shared.datagrams_unblocked.notified()),
+1176                        }
+1177                    }
+1178                }
+1179                UnsupportedByPeer => SendDatagramError::UnsupportedByPeer,
+1180                Disabled => SendDatagramError::Disabled,
+1181                TooLarge => SendDatagramError::TooLarge,
+1182            })),
+1183        }
+1184    }
+1185}
+1186
+1187/// Future returned by [`Connection::on_closed`]
+1188///
+1189/// Resolves to a tuple of ([`ConnectionError`], [`ConnectionStats`]).
+1190pub struct OnClosed {
+1191    rx: oneshot::Receiver<(ConnectionError, ConnectionStats)>,
+1192    conn: WeakConnectionHandle,
+1193}
+1194
+1195impl Drop for OnClosed {
+1196    fn drop(&mut self) {
+1197        if self.rx.is_terminated() {
+1198            return;
+1199        };
+1200        if let Some(conn) = self.conn.upgrade() {
+1201            self.rx.close();
+1202            conn.0
+1203                .state
+1204                .lock("OnClosed::drop")
+1205                .on_closed
+1206                .retain(|tx| !tx.is_closed());
+1207        }
+1208    }
+1209}
+1210
+1211impl Future for OnClosed {
+1212    type Output = (ConnectionError, ConnectionStats);
+1213
+1214    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+1215        let this = self.get_mut();
+1216        // The `expect` is safe because `State::drop` ensures that all senders are triggered
+1217        // before being dropped.
+1218        Pin::new(&mut this.rx)
+1219            .poll(cx)
+1220            .map(|x| x.expect("on_close sender is never dropped before sending"))
+1221    }
+1222}
+1223
+1224#[derive(Debug)]
+1225pub(crate) struct ConnectionRef(Arc<ConnectionInner>);
+1226
+1227impl ConnectionRef {
+1228    fn from_arc(inner: Arc<ConnectionInner>) -> Self {
+1229        inner.state.lock("from_arc").ref_count += 1;
+1230        Self(inner)
+1231    }
+1232
+1233    fn stable_id(&self) -> usize {
+1234        &*self.0 as *const _ as usize
+1235    }
+1236}
+1237
+1238impl Clone for ConnectionRef {
+1239    fn clone(&self) -> Self {
+1240        Self::from_arc(Arc::clone(&self.0))
+1241    }
+1242}
+1243
+1244impl Drop for ConnectionRef {
+1245    fn drop(&mut self) {
+1246        let conn = &mut *self.state.lock("drop");
+1247        if let Some(x) = conn.ref_count.checked_sub(1) {
+1248            conn.ref_count = x;
+1249            if x == 0 && !conn.inner.is_closed() {
+1250                // If the driver is alive, it's just it and us, so we'd better shut it down. If it's
+1251                // not, we can't do any harm. If there were any streams being opened, then either
+1252                // the connection will be closed for an unrelated reason or a fresh reference will
+1253                // be constructed for the newly opened stream.
+1254                conn.implicit_close(&self.shared);
+1255            }
+1256        }
+1257    }
+1258}
+1259
+1260impl std::ops::Deref for ConnectionRef {
+1261    type Target = ConnectionInner;
+1262    fn deref(&self) -> &Self::Target {
+1263        &self.0
+1264    }
+1265}
+1266
+1267#[derive(Debug)]
+1268pub(crate) struct ConnectionInner {
+1269    pub(crate) state: Mutex<State>,
+1270    pub(crate) shared: Shared,
+1271}
+1272
+1273/// A handle to some connection internals, use with care.
+1274///
+1275/// This contains a weak reference to the connection so will not itself keep the connection
+1276/// alive.
+1277#[derive(Debug, Clone)]
+1278pub struct WeakConnectionHandle(Weak<ConnectionInner>);
+1279
+1280impl WeakConnectionHandle {
+1281    /// Returns `true` if the [`Connection`] associated with this handle is still alive.
+1282    pub fn is_alive(&self) -> bool {
+1283        self.0.upgrade().is_some()
+1284    }
+1285
+1286    /// Upgrade the handle to a full `Connection`
+1287    pub fn upgrade(&self) -> Option<Connection> {
+1288        self.0
+1289            .upgrade()
+1290            .map(|inner| Connection(ConnectionRef::from_arc(inner)))
+1291    }
+1292}
+1293
+1294#[derive(Debug, Default)]
+1295pub(crate) struct Shared {
+1296    handshake_confirmed: Notify,
+1297    /// Notified when new streams may be locally initiated due to an increase in stream ID flow
+1298    /// control budget
+1299    stream_budget_available: [Notify; 2],
+1300    /// Notified when the peer has initiated a new stream
+1301    stream_incoming: [Notify; 2],
+1302    datagram_received: Notify,
+1303    datagrams_unblocked: Notify,
+1304    closed: Notify,
+1305}
+1306
+1307pub(crate) struct State {
+1308    pub(crate) inner: proto::Connection,
+1309    driver: Option<Waker>,
+1310    handle: ConnectionHandle,
+1311    on_handshake_data: Option<oneshot::Sender<()>>,
+1312    on_connected: Option<oneshot::Sender<bool>>,
+1313    connected: bool,
+1314    handshake_confirmed: bool,
+1315    timer: Option<Pin<Box<dyn AsyncTimer>>>,
+1316    timer_deadline: Option<Instant>,
+1317    conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+1318    endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+1319    pub(crate) blocked_writers: FxHashMap<StreamId, Waker>,
+1320    pub(crate) blocked_readers: FxHashMap<StreamId, Waker>,
+1321    pub(crate) stopped: FxHashMap<StreamId, Arc<Notify>>,
+1322    /// Always set to Some before the connection becomes drained
+1323    pub(crate) error: Option<ConnectionError>,
+1324    /// Tracks paths being opened
+1325    open_path: FxHashMap<PathId, watch::Sender<Result<(), PathError>>>,
+1326    /// Tracks paths being closed
+1327    pub(crate) close_path: FxHashMap<PathId, oneshot::Sender<VarInt>>,
+1328    pub(crate) path_events: tokio::sync::broadcast::Sender<PathEvent>,
+1329    /// Number of live handles that can be used to initiate or handle I/O; excludes the driver
+1330    ref_count: usize,
+1331    sender: Pin<Box<dyn UdpSender>>,
+1332    pub(crate) runtime: Arc<dyn Runtime>,
+1333    send_buffer: Vec<u8>,
+1334    /// We buffer a transmit when the underlying I/O would block
+1335    buffered_transmit: Option<proto::Transmit>,
+1336    /// Our last external address reported by the peer. When multipath is enabled, this will be the
+1337    /// last report across all paths.
+1338    pub(crate) observed_external_addr: watch::Sender<Option<SocketAddr>>,
+1339    pub(crate) nat_traversal_updates: tokio::sync::broadcast::Sender<iroh_hp::Event>,
+1340    on_closed: Vec<oneshot::Sender<(ConnectionError, ConnectionStats)>>,
+1341}
+1342
+1343impl State {
+1344    #[allow(clippy::too_many_arguments)]
+1345    fn new(
+1346        inner: proto::Connection,
+1347        handle: ConnectionHandle,
+1348        endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+1349        conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+1350        on_handshake_data: oneshot::Sender<()>,
+1351        on_connected: oneshot::Sender<bool>,
+1352        sender: Pin<Box<dyn UdpSender>>,
+1353        runtime: Arc<dyn Runtime>,
+1354    ) -> Self {
+1355        Self {
+1356            inner,
+1357            driver: None,
+1358            handle,
+1359            on_handshake_data: Some(on_handshake_data),
+1360            on_connected: Some(on_connected),
+1361            connected: false,
+1362            handshake_confirmed: false,
+1363            timer: None,
+1364            timer_deadline: None,
+1365            conn_events,
+1366            endpoint_events,
+1367            blocked_writers: FxHashMap::default(),
+1368            blocked_readers: FxHashMap::default(),
+1369            stopped: FxHashMap::default(),
+1370            open_path: FxHashMap::default(),
+1371            close_path: FxHashMap::default(),
+1372            error: None,
+1373            ref_count: 0,
+1374            sender,
+1375            runtime,
+1376            send_buffer: Vec::new(),
+1377            buffered_transmit: None,
+1378            path_events: tokio::sync::broadcast::channel(32).0,
+1379            observed_external_addr: watch::Sender::new(None),
+1380            nat_traversal_updates: tokio::sync::broadcast::channel(32).0,
+1381            on_closed: Vec::new(),
+1382        }
+1383    }
+1384
+1385    fn drive_transmit(&mut self, cx: &mut Context) -> io::Result<bool> {
+1386        let now = self.runtime.now();
+1387        let mut transmits = 0;
+1388
+1389        let max_datagrams = self
+1390            .sender
+1391            .max_transmit_segments()
+1392            .min(MAX_TRANSMIT_SEGMENTS);
+1393
+1394        loop {
+1395            // Retry the last transmit, or get a new one.
+1396            let t = match self.buffered_transmit.take() {
+1397                Some(t) => t,
+1398                None => {
+1399                    self.send_buffer.clear();
+1400                    match self
+1401                        .inner
+1402                        .poll_transmit(now, max_datagrams, &mut self.send_buffer)
+1403                    {
+1404                        Some(t) => {
+1405                            transmits += match t.segment_size {
+1406                                None => 1,
+1407                                Some(s) => t.size.div_ceil(s), // round up
+1408                            };
+1409                            t
+1410                        }
+1411                        None => break,
+1412                    }
+1413                }
+1414            };
+1415
+1416            let len = t.size;
+1417            match self
+1418                .sender
+1419                .as_mut()
+1420                .poll_send(&udp_transmit(&t, &self.send_buffer[..len]), cx)
+1421            {
+1422                Poll::Pending => {
+1423                    self.buffered_transmit = Some(t);
+1424                    return Ok(false);
+1425                }
+1426                Poll::Ready(Err(e)) => return Err(e),
+1427                Poll::Ready(Ok(())) => {}
+1428            }
+1429
+1430            if transmits >= MAX_TRANSMIT_DATAGRAMS {
+1431                // TODO: What isn't ideal here yet is that if we don't poll all
+1432                // datagrams that could be sent we don't go into the `app_limited`
+1433                // state and CWND continues to grow until we get here the next time.
+1434                // See https://github.com/quinn-rs/quinn/issues/1126
+1435                return Ok(true);
+1436            }
+1437        }
+1438
+1439        Ok(false)
+1440    }
+1441
+1442    fn forward_endpoint_events(&mut self) {
+1443        while let Some(event) = self.inner.poll_endpoint_events() {
+1444            // If the endpoint driver is gone, noop.
+1445            let _ = self.endpoint_events.send((self.handle, event));
+1446        }
+1447    }
+1448
+1449    /// If this returns `Err`, the endpoint is dead, so the driver should exit immediately.
+1450    fn process_conn_events(
+1451        &mut self,
+1452        shared: &Shared,
+1453        cx: &mut Context,
+1454    ) -> Result<(), ConnectionError> {
+1455        loop {
+1456            match self.conn_events.poll_recv(cx) {
+1457                Poll::Ready(Some(ConnectionEvent::Rebind(sender))) => {
+1458                    self.sender = sender;
+1459                    self.inner.local_address_changed();
+1460                }
+1461                Poll::Ready(Some(ConnectionEvent::Proto(event))) => {
+1462                    self.inner.handle_event(event);
+1463                }
+1464                Poll::Ready(Some(ConnectionEvent::Close { reason, error_code })) => {
+1465                    self.close(error_code, reason, shared);
+1466                }
+1467                Poll::Ready(None) => {
+1468                    return Err(ConnectionError::TransportError(TransportError::new(
+1469                        TransportErrorCode::INTERNAL_ERROR,
+1470                        "endpoint driver future was dropped".to_string(),
+1471                    )));
+1472                }
+1473                Poll::Pending => {
+1474                    return Ok(());
+1475                }
+1476            }
+1477        }
+1478    }
+1479
+1480    fn forward_app_events(&mut self, shared: &Shared) {
+1481        while let Some(event) = self.inner.poll() {
+1482            use proto::Event::*;
+1483            match event {
+1484                HandshakeDataReady => {
+1485                    if let Some(x) = self.on_handshake_data.take() {
+1486                        let _ = x.send(());
+1487                    }
+1488                }
+1489                Connected => {
+1490                    self.connected = true;
+1491                    if let Some(x) = self.on_connected.take() {
+1492                        // We don't care if the on-connected future was dropped
+1493                        let _ = x.send(self.inner.accepted_0rtt());
+1494                    }
+1495                    if self.inner.side().is_client() && !self.inner.accepted_0rtt() {
+1496                        // Wake up rejected 0-RTT streams so they can fail immediately with
+1497                        // `ZeroRttRejected` errors.
+1498                        wake_all(&mut self.blocked_writers);
+1499                        wake_all(&mut self.blocked_readers);
+1500                        wake_all_notify(&mut self.stopped);
+1501                    }
+1502                }
+1503                HandshakeConfirmed => {
+1504                    self.handshake_confirmed = true;
+1505                    shared.handshake_confirmed.notify_waiters();
+1506                }
+1507                ConnectionLost { reason } => {
+1508                    self.terminate(reason, shared);
+1509                }
+1510                Stream(StreamEvent::Writable { id }) => wake_stream(id, &mut self.blocked_writers),
+1511                Stream(StreamEvent::Opened { dir: Dir::Uni }) => {
+1512                    shared.stream_incoming[Dir::Uni as usize].notify_waiters();
+1513                }
+1514                Stream(StreamEvent::Opened { dir: Dir::Bi }) => {
+1515                    shared.stream_incoming[Dir::Bi as usize].notify_waiters();
+1516                }
+1517                DatagramReceived => {
+1518                    shared.datagram_received.notify_waiters();
+1519                }
+1520                DatagramsUnblocked => {
+1521                    shared.datagrams_unblocked.notify_waiters();
+1522                }
+1523                Stream(StreamEvent::Readable { id }) => wake_stream(id, &mut self.blocked_readers),
+1524                Stream(StreamEvent::Available { dir }) => {
+1525                    // Might mean any number of streams are ready, so we wake up everyone
+1526                    shared.stream_budget_available[dir as usize].notify_waiters();
+1527                }
+1528                Stream(StreamEvent::Finished { id }) => wake_stream_notify(id, &mut self.stopped),
+1529                Stream(StreamEvent::Stopped { id, .. }) => {
+1530                    wake_stream_notify(id, &mut self.stopped);
+1531                    wake_stream(id, &mut self.blocked_writers);
+1532                }
+1533                Path(ref evt @ PathEvent::ObservedAddr { addr: observed, .. }) => {
+1534                    self.path_events.send(evt.clone()).ok();
+1535                    self.observed_external_addr.send_if_modified(|addr| {
+1536                        let old = addr.replace(observed);
+1537                        old != *addr
+1538                    });
+1539                }
+1540                Path(ref evt @ PathEvent::Opened { id }) => {
+1541                    self.path_events.send(evt.clone()).ok();
+1542                    if let Some(sender) = self.open_path.remove(&id) {
+1543                        sender.send_modify(|value| *value = Ok(()));
+1544                    }
+1545                }
+1546                Path(ref evt @ PathEvent::Closed { id, error_code }) => {
+1547                    self.path_events.send(evt.clone()).ok();
+1548                    if let Some(sender) = self.close_path.remove(&id) {
+1549                        let _ = sender.send(error_code);
+1550                    }
+1551                }
+1552                Path(evt @ PathEvent::Abandoned { .. }) => {
+1553                    self.path_events.send(evt).ok();
+1554                }
+1555                Path(ref evt @ PathEvent::LocallyClosed { id, error }) => {
+1556                    self.path_events.send(evt.clone()).ok();
+1557                    if let Some(sender) = self.open_path.remove(&id) {
+1558                        sender.send_modify(|value| *value = Err(error));
+1559                    }
+1560                    // this will happen also for already opened paths
+1561                }
+1562                Path(evt @ PathEvent::RemoteStatus { .. }) => {
+1563                    self.path_events.send(evt).ok();
+1564                }
+1565                NatTraversal(update) => {
+1566                    self.nat_traversal_updates.send(update).ok();
+1567                }
+1568            }
+1569        }
+1570    }
+1571
+1572    fn drive_timer(&mut self, cx: &mut Context) -> bool {
+1573        // Check whether we need to (re)set the timer. If so, we must poll again to ensure the
+1574        // timer is registered with the runtime (and check whether it's already
+1575        // expired).
+1576        match self.inner.poll_timeout() {
+1577            Some(deadline) => {
+1578                if let Some(delay) = &mut self.timer {
+1579                    // There is no need to reset the tokio timer if the deadline
+1580                    // did not change
+1581                    if self
+1582                        .timer_deadline
+1583                        .map(|current_deadline| current_deadline != deadline)
+1584                        .unwrap_or(true)
+1585                    {
+1586                        delay.as_mut().reset(deadline);
+1587                    }
+1588                } else {
+1589                    self.timer = Some(self.runtime.new_timer(deadline));
+1590                }
+1591                // Store the actual expiration time of the timer
+1592                self.timer_deadline = Some(deadline);
+1593            }
+1594            None => {
+1595                self.timer_deadline = None;
+1596                return false;
+1597            }
+1598        }
+1599
+1600        if self.timer_deadline.is_none() {
+1601            return false;
+1602        }
+1603
+1604        let delay = self
+1605            .timer
+1606            .as_mut()
+1607            .expect("timer must exist in this state")
+1608            .as_mut();
+1609        if delay.poll(cx).is_pending() {
+1610            // Since there wasn't a timeout event, there is nothing new
+1611            // for the connection to do
+1612            return false;
+1613        }
+1614
+1615        // A timer expired, so the caller needs to check for
+1616        // new transmits, which might cause new timers to be set.
+1617        self.inner.handle_timeout(self.runtime.now());
+1618        self.timer_deadline = None;
+1619        true
+1620    }
+1621
+1622    /// Wake up a blocked `Driver` task to process I/O
+1623    pub(crate) fn wake(&mut self) {
+1624        if let Some(x) = self.driver.take() {
+1625            x.wake();
+1626        }
+1627    }
+1628
+1629    /// Used to wake up all blocked futures when the connection becomes closed for any reason
+1630    fn terminate(&mut self, reason: ConnectionError, shared: &Shared) {
+1631        self.error = Some(reason.clone());
+1632        if let Some(x) = self.on_handshake_data.take() {
+1633            let _ = x.send(());
+1634        }
+1635        wake_all(&mut self.blocked_writers);
+1636        wake_all(&mut self.blocked_readers);
+1637        shared.stream_budget_available[Dir::Uni as usize].notify_waiters();
+1638        shared.stream_budget_available[Dir::Bi as usize].notify_waiters();
+1639        shared.stream_incoming[Dir::Uni as usize].notify_waiters();
+1640        shared.stream_incoming[Dir::Bi as usize].notify_waiters();
+1641        shared.datagram_received.notify_waiters();
+1642        shared.datagrams_unblocked.notify_waiters();
+1643        if let Some(x) = self.on_connected.take() {
+1644            let _ = x.send(false);
+1645        }
+1646        shared.handshake_confirmed.notify_waiters();
+1647        wake_all_notify(&mut self.stopped);
+1648        shared.closed.notify_waiters();
+1649
+1650        // Send to the registered on_closed futures.
+1651        let stats = self.inner.stats();
+1652        for tx in self.on_closed.drain(..) {
+1653            tx.send((reason.clone(), stats.clone())).ok();
+1654        }
+1655    }
+1656
+1657    fn close(&mut self, error_code: VarInt, reason: Bytes, shared: &Shared) {
+1658        self.inner.close(self.runtime.now(), error_code, reason);
+1659        self.terminate(ConnectionError::LocallyClosed, shared);
+1660        self.wake();
+1661    }
+1662
+1663    /// Close for a reason other than the application's explicit request
+1664    pub(crate) fn implicit_close(&mut self, shared: &Shared) {
+1665        self.close(0u32.into(), Bytes::new(), shared);
+1666    }
+1667
+1668    pub(crate) fn check_0rtt(&self) -> Result<(), ()> {
+1669        if self.inner.is_handshaking()
+1670            || self.inner.accepted_0rtt()
+1671            || self.inner.side().is_server()
+1672        {
+1673            Ok(())
+1674        } else {
+1675            Err(())
+1676        }
+1677    }
+1678}
+1679
+1680impl Drop for State {
+1681    fn drop(&mut self) {
+1682        if !self.inner.is_drained() {
+1683            // Ensure the endpoint can tidy up
+1684            let _ = self
+1685                .endpoint_events
+1686                .send((self.handle, proto::EndpointEvent::drained()));
+1687        }
+1688
+1689        if !self.on_closed.is_empty() {
+1690            // Ensure that all on_closed oneshot senders are triggered before dropping.
+1691            let reason = self.error.as_ref().expect("closed without error reason");
+1692            let stats = self.inner.stats();
+1693            for tx in self.on_closed.drain(..) {
+1694                tx.send((reason.clone(), stats.clone())).ok();
+1695            }
+1696        }
+1697    }
+1698}
+1699
+1700impl fmt::Debug for State {
+1701    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+1702        f.debug_struct("State").field("inner", &self.inner).finish()
+1703    }
+1704}
+1705
+1706fn wake_stream(stream_id: StreamId, wakers: &mut FxHashMap<StreamId, Waker>) {
+1707    if let Some(waker) = wakers.remove(&stream_id) {
+1708        waker.wake();
+1709    }
+1710}
+1711
+1712fn wake_all(wakers: &mut FxHashMap<StreamId, Waker>) {
+1713    wakers.drain().for_each(|(_, waker)| waker.wake())
+1714}
+1715
+1716fn wake_stream_notify(stream_id: StreamId, wakers: &mut FxHashMap<StreamId, Arc<Notify>>) {
+1717    if let Some(notify) = wakers.remove(&stream_id) {
+1718        notify.notify_waiters()
+1719    }
+1720}
+1721
+1722fn wake_all_notify(wakers: &mut FxHashMap<StreamId, Arc<Notify>>) {
+1723    wakers
+1724        .drain()
+1725        .for_each(|(_, notify)| notify.notify_waiters())
+1726}
+1727
+1728/// Errors that can arise when sending a datagram
+1729#[derive(Debug, Error, Clone, Eq, PartialEq)]
+1730pub enum SendDatagramError {
+1731    /// The peer does not support receiving datagram frames
+1732    #[error("datagrams not supported by peer")]
+1733    UnsupportedByPeer,
+1734    /// Datagram support is disabled locally
+1735    #[error("datagram support disabled")]
+1736    Disabled,
+1737    /// The datagram is larger than the connection can currently accommodate
+1738    ///
+1739    /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been
+1740    /// exceeded.
+1741    #[error("datagram too large")]
+1742    TooLarge,
+1743    /// The connection was lost
+1744    #[error("connection lost")]
+1745    ConnectionLost(#[from] ConnectionError),
+1746}
+1747
+1748/// The maximum amount of datagrams which will be produced in a single `drive_transmit` call
+1749///
+1750/// This limits the amount of CPU resources consumed by datagram generation,
+1751/// and allows other tasks (like receiving ACKs) to run in between.
+1752const MAX_TRANSMIT_DATAGRAMS: usize = 20;
+1753
+1754/// The maximum amount of datagrams that are sent in a single transmit
+1755///
+1756/// This can be lower than the maximum platform capabilities, to avoid excessive
+1757/// memory allocations when calling `poll_transmit()`. Benchmarks have shown
+1758/// that numbers around 10 are a good compromise.
+1759const MAX_TRANSMIT_SEGMENTS: NonZeroUsize = NonZeroUsize::new(10).expect("known");
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn/endpoint.rs.html b/pr/299/docs/src/iroh_quinn/endpoint.rs.html new file mode 100644 index 000000000..db47b564f --- /dev/null +++ b/pr/299/docs/src/iroh_quinn/endpoint.rs.html @@ -0,0 +1,934 @@ +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    pub(crate) default_client_config: Option<ClientConfig>,
+58    runtime: Arc<dyn Runtime>,
+59}
+60
+61impl Endpoint {
+62    /// Helper to construct an endpoint for use with outgoing connections only
+63    ///
+64    /// Note that `addr` is the *local* address to bind to, which should usually be a wildcard
+65    /// address like `0.0.0.0:0` or `[::]:0`, which allow communication with any reachable IPv4 or
+66    /// IPv6 address respectively from an OS-assigned port.
+67    ///
+68    /// If an IPv6 address is provided, attempts to make the socket dual-stack so as to allow
+69    /// communication with both IPv4 and IPv6 addresses. As such, calling `Endpoint::client` with
+70    /// the address `[::]:0` is a reasonable default to maximize the ability to connect to other
+71    /// address. For example:
+72    ///
+73    /// ```
+74    /// iroh_quinn::Endpoint::client((std::net::Ipv6Addr::UNSPECIFIED, 0).into());
+75    /// ```
+76    ///
+77    /// Some environments may not allow creation of dual-stack sockets, in which case an IPv6
+78    /// client will only be able to connect to IPv6 servers. An IPv4 client is never dual-stack.
+79    #[cfg(all(
+80        not(wasm_browser),
+81        any(feature = "runtime-tokio", feature = "runtime-smol"),
+82        any(feature = "aws-lc-rs", feature = "ring"), // `EndpointConfig::default()` is only available with these
+83    ))]
+84    pub fn client(addr: SocketAddr) -> io::Result<Self> {
+85        let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))?;
+86        if addr.is_ipv6() {
+87            if let Err(e) = socket.set_only_v6(false) {
+88                tracing::debug!(%e, "unable to make socket dual-stack");
+89            }
+90        }
+91        socket.bind(&addr.into())?;
+92        let runtime =
+93            default_runtime().ok_or_else(|| io::Error::other("no async runtime found"))?;
+94        Self::new_with_abstract_socket(
+95            EndpointConfig::default(),
+96            None,
+97            runtime.wrap_udp_socket(socket.into())?,
+98            runtime,
+99        )
+100    }
+101
+102    /// Returns relevant stats from this Endpoint
+103    pub fn stats(&self) -> EndpointStats {
+104        self.inner.state.lock().unwrap().stats
+105    }
+106
+107    /// Helper to construct an endpoint for use with both incoming and outgoing connections
+108    ///
+109    /// Platform defaults for dual-stack sockets vary. For example, any socket bound to a wildcard
+110    /// IPv6 address on Windows will not by default be able to communicate with IPv4
+111    /// addresses. Portable applications should bind an address that matches the family they wish to
+112    /// communicate within.
+113    #[cfg(all(
+114        not(wasm_browser),
+115        any(feature = "runtime-tokio", feature = "runtime-smol"),
+116        any(feature = "aws-lc-rs", feature = "ring"), // `EndpointConfig::default()` is only available with these
+117    ))]
+118    pub fn server(config: ServerConfig, addr: SocketAddr) -> io::Result<Self> {
+119        let socket = std::net::UdpSocket::bind(addr)?;
+120        let runtime =
+121            default_runtime().ok_or_else(|| io::Error::other("no async runtime found"))?;
+122        Self::new_with_abstract_socket(
+123            EndpointConfig::default(),
+124            Some(config),
+125            runtime.wrap_udp_socket(socket)?,
+126            runtime,
+127        )
+128    }
+129
+130    /// Construct an endpoint with arbitrary configuration and socket
+131    #[cfg(not(wasm_browser))]
+132    pub fn new(
+133        config: EndpointConfig,
+134        server_config: Option<ServerConfig>,
+135        socket: std::net::UdpSocket,
+136        runtime: Arc<dyn Runtime>,
+137    ) -> io::Result<Self> {
+138        let socket = runtime.wrap_udp_socket(socket)?;
+139        Self::new_with_abstract_socket(config, server_config, socket, runtime)
+140    }
+141
+142    /// Construct an endpoint with arbitrary configuration and pre-constructed abstract socket
+143    ///
+144    /// Useful when `socket` has additional state (e.g. sidechannels) attached for which shared
+145    /// ownership is needed.
+146    pub fn new_with_abstract_socket(
+147        config: EndpointConfig,
+148        server_config: Option<ServerConfig>,
+149        socket: Box<dyn AsyncUdpSocket>,
+150        runtime: Arc<dyn Runtime>,
+151    ) -> io::Result<Self> {
+152        let addr = socket.local_addr()?;
+153        let allow_mtud = !socket.may_fragment();
+154        let rc = EndpointRef::new(
+155            socket,
+156            proto::Endpoint::new(
+157                Arc::new(config),
+158                server_config.map(Arc::new),
+159                allow_mtud,
+160                None,
+161            ),
+162            addr.is_ipv6(),
+163            runtime.clone(),
+164        );
+165        let driver = EndpointDriver(rc.clone());
+166        runtime.spawn(Box::pin(
+167            async {
+168                if let Err(e) = driver.await {
+169                    tracing::error!("I/O error: {}", e);
+170                }
+171            }
+172            .instrument(Span::current()),
+173        ));
+174        Ok(Self {
+175            inner: rc,
+176            default_client_config: None,
+177            runtime,
+178        })
+179    }
+180
+181    /// Get the next incoming connection attempt from a client
+182    ///
+183    /// Yields [`Incoming`]s, or `None` if the endpoint is [`close`](Self::close)d. [`Incoming`]
+184    /// can be `await`ed to obtain the final [`Connection`](crate::Connection), or used to e.g.
+185    /// filter connection attempts or force address validation, or converted into an intermediate
+186    /// `Connecting` future which can be used to e.g. send 0.5-RTT data.
+187    pub fn accept(&self) -> Accept<'_> {
+188        Accept {
+189            endpoint: self,
+190            notify: self.inner.shared.incoming.notified(),
+191        }
+192    }
+193
+194    /// Set the client configuration used by `connect`
+195    pub fn set_default_client_config(&mut self, config: ClientConfig) {
+196        self.default_client_config = Some(config);
+197    }
+198
+199    /// Connect to a remote endpoint
+200    ///
+201    /// `server_name` must be covered by the certificate presented by the server. This prevents a
+202    /// connection from being intercepted by an attacker with a valid certificate for some other
+203    /// server.
+204    ///
+205    /// May fail immediately due to configuration errors, or in the future if the connection could
+206    /// not be established.
+207    pub fn connect(&self, addr: SocketAddr, server_name: &str) -> Result<Connecting, ConnectError> {
+208        let config = match &self.default_client_config {
+209            Some(config) => config.clone(),
+210            None => return Err(ConnectError::NoDefaultClientConfig),
+211        };
+212
+213        self.connect_with(config, addr, server_name)
+214    }
+215
+216    /// Connect to a remote endpoint using a custom configuration.
+217    ///
+218    /// See [`connect()`] for details.
+219    ///
+220    /// [`connect()`]: Endpoint::connect
+221    pub fn connect_with(
+222        &self,
+223        config: ClientConfig,
+224        addr: SocketAddr,
+225        server_name: &str,
+226    ) -> Result<Connecting, ConnectError> {
+227        let mut endpoint = self.inner.state.lock().unwrap();
+228        if endpoint.driver_lost || endpoint.recv_state.connections.close.is_some() {
+229            return Err(ConnectError::EndpointStopping);
+230        }
+231        if addr.is_ipv6() && !endpoint.ipv6 {
+232            return Err(ConnectError::InvalidRemoteAddress(addr));
+233        }
+234        let addr = if endpoint.ipv6 {
+235            SocketAddr::V6(ensure_ipv6(addr))
+236        } else {
+237            addr
+238        };
+239
+240        let (ch, conn) = endpoint
+241            .inner
+242            .connect(self.runtime.now(), config, addr, server_name)?;
+243
+244        let sender = endpoint.socket.create_sender();
+245        endpoint.stats.outgoing_handshakes += 1;
+246        Ok(endpoint
+247            .recv_state
+248            .connections
+249            .insert(ch, conn, sender, self.runtime.clone()))
+250    }
+251
+252    /// Switch to a new UDP socket
+253    ///
+254    /// See [`Endpoint::rebind_abstract()`] for details.
+255    #[cfg(not(wasm_browser))]
+256    pub fn rebind(&self, socket: std::net::UdpSocket) -> io::Result<()> {
+257        self.rebind_abstract(self.runtime.wrap_udp_socket(socket)?)
+258    }
+259
+260    /// Switch to a new UDP socket
+261    ///
+262    /// Allows the endpoint's address to be updated live, affecting all active connections. Incoming
+263    /// connections and connections to servers unreachable from the new address will be lost.
+264    ///
+265    /// On error, the old UDP socket is retained.
+266    pub fn rebind_abstract(&self, socket: Box<dyn AsyncUdpSocket>) -> io::Result<()> {
+267        let addr = socket.local_addr()?;
+268        let mut inner = self.inner.state.lock().unwrap();
+269        inner.prev_socket = Some(mem::replace(&mut inner.socket, socket));
+270        inner.ipv6 = addr.is_ipv6();
+271
+272        // Update connection socket references
+273        for sender in inner.recv_state.connections.senders.values() {
+274            // Ignoring errors from dropped connections
+275            let _ = sender.send(ConnectionEvent::Rebind(inner.socket.create_sender()));
+276        }
+277        if let Some(driver) = inner.driver.take() {
+278            // Ensure the driver can register for wake-ups from the new socket
+279            driver.wake();
+280        }
+281
+282        Ok(())
+283    }
+284
+285    /// Replace the server configuration, affecting new incoming connections only
+286    ///
+287    /// Useful for e.g. refreshing TLS certificates without disrupting existing connections.
+288    pub fn set_server_config(&self, server_config: Option<ServerConfig>) {
+289        self.inner
+290            .state
+291            .lock()
+292            .unwrap()
+293            .inner
+294            .set_server_config(server_config.map(Arc::new))
+295    }
+296
+297    /// Get the local `SocketAddr` the underlying socket is bound to
+298    pub fn local_addr(&self) -> io::Result<SocketAddr> {
+299        self.inner.state.lock().unwrap().socket.local_addr()
+300    }
+301
+302    /// Get the number of connections that are currently open
+303    pub fn open_connections(&self) -> usize {
+304        self.inner.state.lock().unwrap().inner.open_connections()
+305    }
+306
+307    /// Close all of this endpoint's connections immediately and cease accepting new connections.
+308    ///
+309    /// See [`Connection::close()`] for details.
+310    ///
+311    /// [`Connection::close()`]: crate::Connection::close
+312    pub fn close(&self, error_code: VarInt, reason: &[u8]) {
+313        let reason = Bytes::copy_from_slice(reason);
+314        let mut endpoint = self.inner.state.lock().unwrap();
+315        endpoint.recv_state.connections.close = Some((error_code, reason.clone()));
+316        for sender in endpoint.recv_state.connections.senders.values() {
+317            // Ignoring errors from dropped connections
+318            let _ = sender.send(ConnectionEvent::Close {
+319                error_code,
+320                reason: reason.clone(),
+321            });
+322        }
+323        self.inner.shared.incoming.notify_waiters();
+324    }
+325
+326    /// Wait for all connections on the endpoint to be cleanly shut down
+327    ///
+328    /// Waiting for this condition before exiting ensures that a good-faith effort is made to notify
+329    /// peers of recent connection closes, whereas exiting immediately could force them to wait out
+330    /// the idle timeout period.
+331    ///
+332    /// Does not proactively close existing connections or cause incoming connections to be
+333    /// rejected. Consider calling [`close()`] if that is desired.
+334    ///
+335    /// [`close()`]: Endpoint::close
+336    pub async fn wait_idle(&self) {
+337        loop {
+338            {
+339                let endpoint = &mut *self.inner.state.lock().unwrap();
+340                if endpoint.recv_state.connections.is_empty() {
+341                    break;
+342                }
+343                // Construct future while lock is held to avoid race
+344                self.inner.shared.idle.notified()
+345            }
+346            .await;
+347        }
+348    }
+349}
+350
+351/// Statistics on [Endpoint] activity
+352#[non_exhaustive]
+353#[derive(Debug, Default, Copy, Clone)]
+354pub struct EndpointStats {
+355    /// Cumulative number of Quic handshakes accepted by this [Endpoint]
+356    pub accepted_handshakes: u64,
+357    /// Cumulative number of Quic handshakes sent from this [Endpoint]
+358    pub outgoing_handshakes: u64,
+359    /// Cumulative number of Quic handshakes refused on this [Endpoint]
+360    pub refused_handshakes: u64,
+361    /// Cumulative number of Quic handshakes ignored on this [Endpoint]
+362    pub ignored_handshakes: u64,
+363}
+364
+365/// A future that drives IO on an endpoint
+366///
+367/// This task functions as the switch point between the UDP socket object and the
+368/// `Endpoint` responsible for routing datagrams to their owning `Connection`.
+369/// In order to do so, it also facilitates the exchange of different types of events
+370/// flowing between the `Endpoint` and the tasks managing `Connection`s. As such,
+371/// running this task is necessary to keep the endpoint's connections running.
+372///
+373/// `EndpointDriver` futures terminate when all clones of the `Endpoint` have been dropped, or when
+374/// an I/O error occurs.
+375#[must_use = "endpoint drivers must be spawned for I/O to occur"]
+376#[derive(Debug)]
+377pub(crate) struct EndpointDriver(pub(crate) EndpointRef);
+378
+379impl Future for EndpointDriver {
+380    type Output = Result<(), io::Error>;
+381
+382    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+383        let mut endpoint = self.0.state.lock().unwrap();
+384        if endpoint.driver.is_none() {
+385            endpoint.driver = Some(cx.waker().clone());
+386        }
+387
+388        let now = endpoint.runtime.now();
+389        let mut keep_going = false;
+390        keep_going |= endpoint.drive_recv(cx, now)?;
+391        keep_going |= endpoint.handle_events(cx, &self.0.shared);
+392
+393        if !endpoint.recv_state.incoming.is_empty() {
+394            self.0.shared.incoming.notify_waiters();
+395        }
+396
+397        if endpoint.ref_count == 0 && endpoint.recv_state.connections.is_empty() {
+398            Poll::Ready(Ok(()))
+399        } else {
+400            drop(endpoint);
+401            // If there is more work to do schedule the endpoint task again.
+402            // `wake_by_ref()` is called outside the lock to minimize
+403            // lock contention on a multithreaded runtime.
+404            if keep_going {
+405                cx.waker().wake_by_ref();
+406            }
+407            Poll::Pending
+408        }
+409    }
+410}
+411
+412impl Drop for EndpointDriver {
+413    fn drop(&mut self) {
+414        let mut endpoint = self.0.state.lock().unwrap();
+415        endpoint.driver_lost = true;
+416        self.0.shared.incoming.notify_waiters();
+417        // Drop all outgoing channels, signaling the termination of the endpoint to the associated
+418        // connections.
+419        endpoint.recv_state.connections.senders.clear();
+420    }
+421}
+422
+423#[derive(Debug)]
+424pub(crate) struct EndpointInner {
+425    pub(crate) state: Mutex<State>,
+426    pub(crate) shared: Shared,
+427}
+428
+429impl EndpointInner {
+430    pub(crate) fn accept(
+431        &self,
+432        incoming: proto::Incoming,
+433        server_config: Option<Arc<ServerConfig>>,
+434    ) -> Result<Connecting, ConnectionError> {
+435        let mut state = self.state.lock().unwrap();
+436        let mut response_buffer = Vec::new();
+437        let now = state.runtime.now();
+438        match state
+439            .inner
+440            .accept(incoming, now, &mut response_buffer, server_config)
+441        {
+442            Ok((handle, conn)) => {
+443                state.stats.accepted_handshakes += 1;
+444                let sender = state.socket.create_sender();
+445                let runtime = state.runtime.clone();
+446                Ok(state
+447                    .recv_state
+448                    .connections
+449                    .insert(handle, conn, sender, runtime))
+450            }
+451            Err(error) => {
+452                if let Some(transmit) = error.response {
+453                    respond(transmit, &response_buffer, &mut state.sender);
+454                }
+455                Err(error.cause)
+456            }
+457        }
+458    }
+459
+460    pub(crate) fn refuse(&self, incoming: proto::Incoming) {
+461        let mut state = self.state.lock().unwrap();
+462        state.stats.refused_handshakes += 1;
+463        let mut response_buffer = Vec::new();
+464        let transmit = state.inner.refuse(incoming, &mut response_buffer);
+465        respond(transmit, &response_buffer, &mut state.sender);
+466    }
+467
+468    pub(crate) fn retry(&self, incoming: proto::Incoming) -> Result<(), proto::RetryError> {
+469        let mut state = self.state.lock().unwrap();
+470        let mut response_buffer = Vec::new();
+471        let transmit = state.inner.retry(incoming, &mut response_buffer)?;
+472        respond(transmit, &response_buffer, &mut state.sender);
+473        Ok(())
+474    }
+475
+476    pub(crate) fn ignore(&self, incoming: proto::Incoming) {
+477        let mut state = self.state.lock().unwrap();
+478        state.stats.ignored_handshakes += 1;
+479        state.inner.ignore(incoming);
+480    }
+481}
+482
+483#[derive(Debug)]
+484pub(crate) struct State {
+485    socket: Box<dyn AsyncUdpSocket>,
+486    sender: Pin<Box<dyn UdpSender>>,
+487    /// During an active migration, abandoned_socket receives traffic
+488    /// until the first packet arrives on the new socket.
+489    prev_socket: Option<Box<dyn AsyncUdpSocket>>,
+490    inner: proto::Endpoint,
+491    recv_state: RecvState,
+492    driver: Option<Waker>,
+493    ipv6: bool,
+494    events: mpsc::UnboundedReceiver<(ConnectionHandle, EndpointEvent)>,
+495    /// Number of live handles that can be used to initiate or handle I/O; excludes the driver
+496    ref_count: usize,
+497    driver_lost: bool,
+498    runtime: Arc<dyn Runtime>,
+499    stats: EndpointStats,
+500}
+501
+502#[derive(Debug)]
+503pub(crate) struct Shared {
+504    incoming: Notify,
+505    idle: Notify,
+506}
+507
+508impl State {
+509    fn drive_recv(&mut self, cx: &mut Context, now: Instant) -> Result<bool, io::Error> {
+510        let get_time = || self.runtime.now();
+511        self.recv_state.recv_limiter.start_cycle(get_time);
+512        if let Some(socket) = &mut self.prev_socket {
+513            // We don't care about the `PollProgress` from old sockets.
+514            let poll_res = self.recv_state.poll_socket(
+515                cx,
+516                &mut self.inner,
+517                &mut **socket,
+518                &mut self.sender,
+519                &*self.runtime,
+520                now,
+521            );
+522            if poll_res.is_err() {
+523                self.prev_socket = None;
+524            }
+525        };
+526        let poll_res = self.recv_state.poll_socket(
+527            cx,
+528            &mut self.inner,
+529            &mut *self.socket,
+530            &mut self.sender,
+531            &*self.runtime,
+532            now,
+533        );
+534        self.recv_state.recv_limiter.finish_cycle(get_time);
+535        let poll_res = poll_res?;
+536        if poll_res.received_connection_packet {
+537            // Traffic has arrived on self.socket, therefore there is no need for the abandoned
+538            // one anymore. TODO: Account for multiple outgoing connections.
+539            self.prev_socket = None;
+540        }
+541        Ok(poll_res.keep_going)
+542    }
+543
+544    fn handle_events(&mut self, cx: &mut Context, shared: &Shared) -> bool {
+545        for _ in 0..IO_LOOP_BOUND {
+546            let (ch, event) = match self.events.poll_recv(cx) {
+547                Poll::Ready(Some(x)) => x,
+548                Poll::Ready(None) => unreachable!("EndpointInner owns one sender"),
+549                Poll::Pending => {
+550                    return false;
+551                }
+552            };
+553
+554            if event.is_drained() {
+555                self.recv_state.connections.senders.remove(&ch);
+556                if self.recv_state.connections.is_empty() {
+557                    shared.idle.notify_waiters();
+558                }
+559            }
+560            let Some(event) = self.inner.handle_event(ch, event) else {
+561                continue;
+562            };
+563            // Ignoring errors from dropped connections that haven't yet been cleaned up
+564            let _ = self
+565                .recv_state
+566                .connections
+567                .senders
+568                .get_mut(&ch)
+569                .unwrap()
+570                .send(ConnectionEvent::Proto(event));
+571        }
+572
+573        true
+574    }
+575}
+576
+577impl Drop for State {
+578    fn drop(&mut self) {
+579        for incoming in self.recv_state.incoming.drain(..) {
+580            self.inner.ignore(incoming);
+581        }
+582    }
+583}
+584
+585fn respond(
+586    transmit: proto::Transmit,
+587    response_buffer: &[u8],
+588    sender: &mut Pin<Box<dyn UdpSender>>,
+589) {
+590    // Send if there's kernel buffer space; otherwise, drop it
+591    //
+592    // As an endpoint-generated packet, we know this is an
+593    // immediate, stateless response to an unconnected peer,
+594    // one of:
+595    //
+596    // - A version negotiation response due to an unknown version
+597    // - A `CLOSE` due to a malformed or unwanted connection attempt
+598    // - A stateless reset due to an unrecognized connection
+599    // - A `Retry` packet due to a connection attempt when
+600    //   `use_retry` is set
+601    //
+602    // In each case, a well-behaved peer can be trusted to retry a
+603    // few times, which is guaranteed to produce the same response
+604    // from us. Repeated failures might at worst cause a peer's new
+605    // connection attempt to time out, which is acceptable if we're
+606    // under such heavy load that there's never room for this code
+607    // to transmit. This is morally equivalent to the packet getting
+608    // lost due to congestion further along the link, which
+609    // similarly relies on peer retries for recovery.
+610
+611    // Copied from rust 1.85's std::task::Waker::noop() implementation for backwards compatibility
+612    const NOOP: RawWaker = {
+613        const VTABLE: RawWakerVTable = RawWakerVTable::new(
+614            // Cloning just returns a new no-op raw waker
+615            |_| NOOP,
+616            // `wake` does nothing
+617            |_| {},
+618            // `wake_by_ref` does nothing
+619            |_| {},
+620            // Dropping does nothing as we don't allocate anything
+621            |_| {},
+622        );
+623        RawWaker::new(std::ptr::null(), &VTABLE)
+624    };
+625    // SAFETY: Copied from rust stdlib, the NOOP waker is thread-safe and doesn't violate the RawWakerVTable contract,
+626    // it doesn't access the data pointer at all.
+627    let waker = unsafe { Waker::from_raw(NOOP) };
+628    let mut cx = Context::from_waker(&waker);
+629    _ = sender.as_mut().poll_send(
+630        &udp_transmit(&transmit, &response_buffer[..transmit.size]),
+631        &mut cx,
+632    );
+633}
+634
+635#[inline]
+636fn proto_ecn(ecn: udp::EcnCodepoint) -> proto::EcnCodepoint {
+637    match ecn {
+638        udp::EcnCodepoint::Ect0 => proto::EcnCodepoint::Ect0,
+639        udp::EcnCodepoint::Ect1 => proto::EcnCodepoint::Ect1,
+640        udp::EcnCodepoint::Ce => proto::EcnCodepoint::Ce,
+641    }
+642}
+643
+644#[derive(Debug)]
+645struct ConnectionSet {
+646    /// Senders for communicating with the endpoint's connections
+647    senders: FxHashMap<ConnectionHandle, mpsc::UnboundedSender<ConnectionEvent>>,
+648    /// Stored to give out clones to new ConnectionInners
+649    sender: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+650    /// Set if the endpoint has been manually closed
+651    close: Option<(VarInt, Bytes)>,
+652}
+653
+654impl ConnectionSet {
+655    fn insert(
+656        &mut self,
+657        handle: ConnectionHandle,
+658        conn: proto::Connection,
+659        sender: Pin<Box<dyn UdpSender>>,
+660        runtime: Arc<dyn Runtime>,
+661    ) -> Connecting {
+662        let (send, recv) = mpsc::unbounded_channel();
+663        if let Some((error_code, ref reason)) = self.close {
+664            send.send(ConnectionEvent::Close {
+665                error_code,
+666                reason: reason.clone(),
+667            })
+668            .unwrap();
+669        }
+670        self.senders.insert(handle, send);
+671        Connecting::new(handle, conn, self.sender.clone(), recv, sender, runtime)
+672    }
+673
+674    fn is_empty(&self) -> bool {
+675        self.senders.is_empty()
+676    }
+677}
+678
+679pub(crate) fn ensure_ipv6(x: SocketAddr) -> SocketAddrV6 {
+680    match x {
+681        SocketAddr::V6(x) => x,
+682        SocketAddr::V4(x) => SocketAddrV6::new(x.ip().to_ipv6_mapped(), x.port(), 0, 0),
+683    }
+684}
+685
+686pin_project! {
+687    /// Future produced by [`Endpoint::accept`]
+688    pub struct Accept<'a> {
+689        endpoint: &'a Endpoint,
+690        #[pin]
+691        notify: Notified<'a>,
+692    }
+693}
+694
+695impl Future for Accept<'_> {
+696    type Output = Option<Incoming>;
+697    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+698        let mut this = self.project();
+699        let mut endpoint = this.endpoint.inner.state.lock().unwrap();
+700        if endpoint.driver_lost {
+701            return Poll::Ready(None);
+702        }
+703        if let Some(incoming) = endpoint.recv_state.incoming.pop_front() {
+704            // Release the mutex lock on endpoint so cloning it doesn't deadlock
+705            drop(endpoint);
+706            let incoming = Incoming::new(incoming, this.endpoint.inner.clone());
+707            return Poll::Ready(Some(incoming));
+708        }
+709        if endpoint.recv_state.connections.close.is_some() {
+710            return Poll::Ready(None);
+711        }
+712        loop {
+713            match this.notify.as_mut().poll(ctx) {
+714                // `state` lock ensures we didn't race with readiness
+715                Poll::Pending => return Poll::Pending,
+716                // Spurious wakeup, get a new future
+717                Poll::Ready(()) => this
+718                    .notify
+719                    .set(this.endpoint.inner.shared.incoming.notified()),
+720            }
+721        }
+722    }
+723}
+724
+725#[derive(Debug)]
+726pub(crate) struct EndpointRef(Arc<EndpointInner>);
+727
+728impl EndpointRef {
+729    pub(crate) fn new(
+730        socket: Box<dyn AsyncUdpSocket>,
+731        inner: proto::Endpoint,
+732        ipv6: bool,
+733        runtime: Arc<dyn Runtime>,
+734    ) -> Self {
+735        let (sender, events) = mpsc::unbounded_channel();
+736        let recv_state = RecvState::new(sender, socket.max_receive_segments(), &inner);
+737        let sender = socket.create_sender();
+738        Self(Arc::new(EndpointInner {
+739            shared: Shared {
+740                incoming: Notify::new(),
+741                idle: Notify::new(),
+742            },
+743            state: Mutex::new(State {
+744                socket,
+745                sender,
+746                prev_socket: None,
+747                inner,
+748                ipv6,
+749                events,
+750                driver: None,
+751                ref_count: 0,
+752                driver_lost: false,
+753                recv_state,
+754                runtime,
+755                stats: EndpointStats::default(),
+756            }),
+757        }))
+758    }
+759}
+760
+761impl Clone for EndpointRef {
+762    fn clone(&self) -> Self {
+763        self.0.state.lock().unwrap().ref_count += 1;
+764        Self(self.0.clone())
+765    }
+766}
+767
+768impl Drop for EndpointRef {
+769    fn drop(&mut self) {
+770        let endpoint = &mut *self.0.state.lock().unwrap();
+771        if let Some(x) = endpoint.ref_count.checked_sub(1) {
+772            endpoint.ref_count = x;
+773            if x == 0 {
+774                // If the driver is about to be on its own, ensure it can shut down if the last
+775                // connection is gone.
+776                if let Some(task) = endpoint.driver.take() {
+777                    task.wake();
+778                }
+779            }
+780        }
+781    }
+782}
+783
+784impl std::ops::Deref for EndpointRef {
+785    type Target = EndpointInner;
+786    fn deref(&self) -> &Self::Target {
+787        &self.0
+788    }
+789}
+790
+791/// State directly involved in handling incoming packets
+792struct RecvState {
+793    incoming: VecDeque<proto::Incoming>,
+794    connections: ConnectionSet,
+795    recv_buf: Box<[u8]>,
+796    recv_limiter: WorkLimiter,
+797}
+798
+799impl RecvState {
+800    fn new(
+801        sender: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+802        max_receive_segments: NonZeroUsize,
+803        endpoint: &proto::Endpoint,
+804    ) -> Self {
+805        let recv_buf = vec![
+806            0;
+807            endpoint.config().get_max_udp_payload_size().min(64 * 1024) as usize
+808                * max_receive_segments.get()
+809                * BATCH_SIZE
+810        ];
+811        Self {
+812            connections: ConnectionSet {
+813                senders: FxHashMap::default(),
+814                sender,
+815                close: None,
+816            },
+817            incoming: VecDeque::new(),
+818            recv_buf: recv_buf.into(),
+819            recv_limiter: WorkLimiter::new(RECV_TIME_BOUND),
+820        }
+821    }
+822
+823    fn poll_socket(
+824        &mut self,
+825        cx: &mut Context,
+826        endpoint: &mut proto::Endpoint,
+827        socket: &mut dyn AsyncUdpSocket,
+828        sender: &mut Pin<Box<dyn UdpSender>>,
+829        runtime: &dyn Runtime,
+830        now: Instant,
+831    ) -> Result<PollProgress, io::Error> {
+832        let mut received_connection_packet = false;
+833        let mut metas = [RecvMeta::default(); BATCH_SIZE];
+834        let mut iovs: [IoSliceMut; BATCH_SIZE] = {
+835            let mut bufs = self
+836                .recv_buf
+837                .chunks_mut(self.recv_buf.len() / BATCH_SIZE)
+838                .map(IoSliceMut::new);
+839
+840            // expect() safe as self.recv_buf is chunked into BATCH_SIZE items
+841            // and iovs will be of size BATCH_SIZE, thus from_fn is called
+842            // exactly BATCH_SIZE times.
+843            std::array::from_fn(|_| bufs.next().expect("BATCH_SIZE elements"))
+844        };
+845        loop {
+846            match socket.poll_recv(cx, &mut iovs, &mut metas) {
+847                Poll::Ready(Ok(msgs)) => {
+848                    self.recv_limiter.record_work(msgs);
+849                    for (meta, buf) in metas.iter().zip(iovs.iter()).take(msgs) {
+850                        let mut data: BytesMut = buf[0..meta.len].into();
+851                        while !data.is_empty() {
+852                            let buf = data.split_to(meta.stride.min(data.len()));
+853                            let mut response_buffer = Vec::new();
+854                            let addresses = FourTuple {
+855                                remote: meta.addr,
+856                                local_ip: meta.dst_ip,
+857                            };
+858                            match endpoint.handle(
+859                                now,
+860                                addresses,
+861                                meta.ecn.map(proto_ecn),
+862                                buf,
+863                                &mut response_buffer,
+864                            ) {
+865                                Some(DatagramEvent::NewConnection(incoming)) => {
+866                                    if self.connections.close.is_none() {
+867                                        self.incoming.push_back(incoming);
+868                                    } else {
+869                                        let transmit =
+870                                            endpoint.refuse(incoming, &mut response_buffer);
+871                                        respond(transmit, &response_buffer, sender);
+872                                    }
+873                                }
+874                                Some(DatagramEvent::ConnectionEvent(handle, event)) => {
+875                                    // Ignoring errors from dropped connections that haven't yet been cleaned up
+876                                    received_connection_packet = true;
+877                                    let _ = self
+878                                        .connections
+879                                        .senders
+880                                        .get_mut(&handle)
+881                                        .unwrap()
+882                                        .send(ConnectionEvent::Proto(event));
+883                                }
+884                                Some(DatagramEvent::Response(transmit)) => {
+885                                    respond(transmit, &response_buffer, sender);
+886                                }
+887                                None => {}
+888                            }
+889                        }
+890                    }
+891                }
+892                Poll::Pending => {
+893                    return Ok(PollProgress {
+894                        received_connection_packet,
+895                        keep_going: false,
+896                    });
+897                }
+898                // Ignore ECONNRESET as it's undefined in QUIC and may be injected by an
+899                // attacker
+900                Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::ConnectionReset => {
+901                    continue;
+902                }
+903                Poll::Ready(Err(e)) => {
+904                    return Err(e);
+905                }
+906            }
+907            if !self.recv_limiter.allow_work(|| runtime.now()) {
+908                return Ok(PollProgress {
+909                    received_connection_packet,
+910                    keep_going: true,
+911                });
+912            }
+913        }
+914    }
+915}
+916
+917impl fmt::Debug for RecvState {
+918    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+919        f.debug_struct("RecvState")
+920            .field("incoming", &self.incoming)
+921            .field("connections", &self.connections)
+922            // recv_buf too large
+923            .field("recv_limiter", &self.recv_limiter)
+924            .finish_non_exhaustive()
+925    }
+926}
+927
+928#[derive(Default)]
+929struct PollProgress {
+930    /// Whether a datagram was routed to an existing connection
+931    received_connection_packet: bool,
+932    /// Whether datagram handling was interrupted early by the work limiter for fairness
+933    keep_going: bool,
+934}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn/incoming.rs.html b/pr/299/docs/src/iroh_quinn/incoming.rs.html new file mode 100644 index 000000000..a9d589515 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn/lib.rs.html b/pr/299/docs/src/iroh_quinn/lib.rs.html new file mode 100644 index 000000000..2411dab70 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn/lib.rs.html @@ -0,0 +1,140 @@ +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#![warn(unreachable_pub)]
+42#![warn(clippy::use_self)]
+43
+44use std::pin::Pin;
+45
+46mod connection;
+47mod endpoint;
+48mod incoming;
+49mod mutex;
+50mod path;
+51mod recv_stream;
+52mod runtime;
+53mod send_stream;
+54mod work_limiter;
+55
+56#[cfg(not(wasm_browser))]
+57pub(crate) use std::time::{Duration, Instant};
+58#[cfg(wasm_browser)]
+59pub(crate) use web_time::{Duration, Instant};
+60
+61#[cfg(feature = "bloom")]
+62pub use proto::BloomTokenLog;
+63pub use proto::{
+64    AckFrequencyConfig, ApplicationClose, Chunk, ClientConfig, ClosedStream, ConfigError,
+65    ConnectError, ConnectionClose, ConnectionError, ConnectionId, ConnectionIdGenerator,
+66    ConnectionStats, Dir, EcnCodepoint, EndpointConfig, FrameStats, FrameType, IdleTimeout,
+67    MtuDiscoveryConfig, NoneTokenLog, NoneTokenStore, PathId, PathStats, ServerConfig, Side,
+68    StdSystemTime, StreamId, TimeSource, TokenLog, TokenMemoryCache, TokenReuseError, TokenStore,
+69    Transmit, TransportConfig, TransportErrorCode, UdpStats, ValidationTokenConfig, VarInt,
+70    VarIntBoundsExceeded, Written, congestion, crypto,
+71};
+72#[cfg(feature = "qlog")]
+73pub use proto::{QlogConfig, QlogFactory, QlogFileFactory};
+74#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+75pub use rustls;
+76pub use udp;
+77
+78pub use crate::connection::{
+79    AcceptBi, AcceptUni, Connecting, Connection, OnClosed, OpenBi, OpenUni, ReadDatagram,
+80    SendDatagram, SendDatagramError, WeakConnectionHandle, ZeroRttAccepted,
+81};
+82pub use crate::endpoint::{Accept, Endpoint, EndpointStats};
+83pub use crate::incoming::{Incoming, IncomingFuture, RetryError};
+84pub use crate::path::{OpenPath, Path};
+85pub use crate::recv_stream::{
+86    ReadError, ReadExactError, ReadToEndError, RecvStream, ResetError, UnorderedRecvStream,
+87};
+88#[cfg(feature = "runtime-smol")]
+89pub use crate::runtime::SmolRuntime;
+90#[cfg(feature = "runtime-tokio")]
+91pub use crate::runtime::TokioRuntime;
+92#[cfg(any(feature = "runtime-tokio", feature = "runtime-smol"))]
+93pub use crate::runtime::default_runtime;
+94pub use crate::runtime::{AsyncTimer, AsyncUdpSocket, Runtime, UdpSender};
+95pub use crate::send_stream::{SendStream, StoppedError, WriteError};
+96
+97#[cfg(test)]
+98mod tests;
+99
+100#[derive(Debug)]
+101enum ConnectionEvent {
+102    Close {
+103        error_code: VarInt,
+104        reason: bytes::Bytes,
+105    },
+106    Proto(proto::ConnectionEvent),
+107    Rebind(Pin<Box<dyn UdpSender>>),
+108}
+109
+110fn udp_transmit<'a>(t: &proto::Transmit, buffer: &'a [u8]) -> udp::Transmit<'a> {
+111    udp::Transmit {
+112        destination: t.destination,
+113        ecn: t.ecn.map(udp_ecn),
+114        contents: buffer,
+115        segment_size: t.segment_size,
+116        src_ip: t.src_ip,
+117    }
+118}
+119
+120fn udp_ecn(ecn: proto::EcnCodepoint) -> udp::EcnCodepoint {
+121    match ecn {
+122        proto::EcnCodepoint::Ect0 => udp::EcnCodepoint::Ect0,
+123        proto::EcnCodepoint::Ect1 => udp::EcnCodepoint::Ect1,
+124        proto::EcnCodepoint::Ce => udp::EcnCodepoint::Ce,
+125    }
+126}
+127
+128/// Maximum number of datagrams processed in send/recv calls to make before moving on to other processing
+129///
+130/// This helps ensure we don't starve anything when the CPU is slower than the link.
+131/// Value is selected by picking a low number which didn't degrade throughput in benchmarks.
+132const IO_LOOP_BOUND: usize = 160;
+133
+134/// The maximum amount of time that should be spent in `recvmsg()` calls per endpoint iteration
+135///
+136/// 50us are chosen so that an endpoint iteration with a 50us sendmsg limit blocks
+137/// the runtime for a maximum of about 100us.
+138/// Going much lower does not yield any noticeable difference, since a single `recvmmsg`
+139/// batch of size 32 was observed to take 30us on some systems.
+140const RECV_TIME_BOUND: Duration = Duration::from_micros(50);
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn/mutex.rs.html b/pr/299/docs/src/iroh_quinn/mutex.rs.html new file mode 100644 index 000000000..ad0484c77 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn/path.rs.html b/pr/299/docs/src/iroh_quinn/path.rs.html new file mode 100644 index 000000000..dd44d7f0f --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn/recv_stream.rs.html b/pr/299/docs/src/iroh_quinn/recv_stream.rs.html new file mode 100644 index 000000000..327272bc9 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn/runtime/mod.rs.html b/pr/299/docs/src/iroh_quinn/runtime/mod.rs.html new file mode 100644 index 000000000..9b510729f --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn/runtime/smol.rs.html b/pr/299/docs/src/iroh_quinn/runtime/smol.rs.html new file mode 100644 index 000000000..aa772e08f --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn/runtime/tokio.rs.html b/pr/299/docs/src/iroh_quinn/runtime/tokio.rs.html new file mode 100644 index 000000000..48ba2180b --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn/send_stream.rs.html b/pr/299/docs/src/iroh_quinn/send_stream.rs.html new file mode 100644 index 000000000..b21a2cdd1 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn/send_stream.rs.html @@ -0,0 +1,443 @@ +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>(&mut self, cx: &mut Context, write_fn: F) -> Poll<Result<R, WriteError>>
+138    where
+139        F: FnOnce(&mut proto::SendStream) -> Result<R, proto::WriteError>,
+140    {
+141        use proto::WriteError::*;
+142        let mut conn = self.conn.state.lock("SendStream::poll_write");
+143        if self.is_0rtt {
+144            conn.check_0rtt()
+145                .map_err(|()| WriteError::ZeroRttRejected)?;
+146        }
+147        if let Some(ref x) = conn.error {
+148            return Poll::Ready(Err(WriteError::ConnectionLost(x.clone())));
+149        }
+150
+151        let result = match write_fn(&mut conn.inner.send_stream(self.stream)) {
+152            Ok(result) => result,
+153            Err(Blocked) => {
+154                conn.blocked_writers.insert(self.stream, cx.waker().clone());
+155                return Poll::Pending;
+156            }
+157            Err(Stopped(error_code)) => {
+158                return Poll::Ready(Err(WriteError::Stopped(error_code)));
+159            }
+160            Err(ClosedStream) => {
+161                return Poll::Ready(Err(WriteError::ClosedStream));
+162            }
+163        };
+164
+165        conn.wake();
+166        Poll::Ready(Ok(result))
+167    }
+168
+169    /// Notify the peer that no more data will ever be written to this stream
+170    ///
+171    /// It is an error to write to a [`SendStream`] after `finish()`ing it. [`reset()`](Self::reset)
+172    /// may still be called after `finish` to abandon transmission of any stream data that might
+173    /// still be buffered.
+174    ///
+175    /// To wait for the peer to receive all buffered stream data, see [`stopped()`](Self::stopped).
+176    ///
+177    /// May fail if [`finish()`](Self::finish) or [`reset()`](Self::reset) was previously
+178    /// called. This error is harmless and serves only to indicate that the caller may have
+179    /// incorrect assumptions about the stream's state.
+180    pub fn finish(&mut self) -> Result<(), ClosedStream> {
+181        let mut conn = self.conn.state.lock("finish");
+182        match conn.inner.send_stream(self.stream).finish() {
+183            Ok(()) => {
+184                conn.wake();
+185                Ok(())
+186            }
+187            Err(FinishError::ClosedStream) => Err(ClosedStream::default()),
+188            // Harmless. If the application needs to know about stopped streams at this point, it
+189            // should call `stopped`.
+190            Err(FinishError::Stopped(_)) => Ok(()),
+191        }
+192    }
+193
+194    /// Close the send stream immediately.
+195    ///
+196    /// No new data can be written after calling this method. Locally buffered data is dropped, and
+197    /// previously transmitted data will no longer be retransmitted if lost. If an attempt has
+198    /// already been made to finish the stream, the peer may still receive all written data.
+199    ///
+200    /// May fail if [`finish()`](Self::finish) or [`reset()`](Self::reset) was previously
+201    /// called. This error is harmless and serves only to indicate that the caller may have
+202    /// incorrect assumptions about the stream's state.
+203    pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+204        let mut conn = self.conn.state.lock("SendStream::reset");
+205        if self.is_0rtt && conn.check_0rtt().is_err() {
+206            return Ok(());
+207        }
+208        conn.inner.send_stream(self.stream).reset(error_code)?;
+209        conn.wake();
+210        Ok(())
+211    }
+212
+213    /// Set the priority of the send stream
+214    ///
+215    /// Every send stream has an initial priority of 0. Locally buffered data from streams with
+216    /// higher priority will be transmitted before data from streams with lower priority. Changing
+217    /// the priority of a stream with pending data may only take effect after that data has been
+218    /// transmitted. Using many different priority levels per connection may have a negative
+219    /// impact on performance.
+220    pub fn set_priority(&self, priority: i32) -> Result<(), ClosedStream> {
+221        let mut conn = self.conn.state.lock("SendStream::set_priority");
+222        conn.inner.send_stream(self.stream).set_priority(priority)?;
+223        Ok(())
+224    }
+225
+226    /// Get the priority of the send stream
+227    pub fn priority(&self) -> Result<i32, ClosedStream> {
+228        let mut conn = self.conn.state.lock("SendStream::priority");
+229        conn.inner.send_stream(self.stream).priority()
+230    }
+231
+232    /// Completes when the peer stops the stream or reads the stream to completion
+233    ///
+234    /// Yields `Some` with the stop error code if the peer stops the stream. Yields `None` if the
+235    /// local side [`finish()`](Self::finish)es the stream and then the peer acknowledges receipt
+236    /// of all stream data (although not necessarily the processing of it), after which the peer
+237    /// closing the stream is no longer meaningful.
+238    ///
+239    /// For a variety of reasons, the peer may not send acknowledgements immediately upon receiving
+240    /// data. As such, relying on `stopped` to know when the peer has read a stream to completion
+241    /// may introduce more latency than using an application-level response of some sort.
+242    pub fn stopped(
+243        &self,
+244    ) -> impl Future<Output = Result<Option<VarInt>, StoppedError>> + Send + Sync + 'static {
+245        let conn = self.conn.clone();
+246        let stream = self.stream;
+247        let is_0rtt = self.is_0rtt;
+248        async move {
+249            loop {
+250                // The `Notify::notified` future needs to be created while the lock is being held,
+251                // otherwise a wakeup could be missed if triggered in between releasing the lock
+252                // and creating the future.
+253                // The lock may only be held in a block without `await`s, otherwise the future
+254                // becomes `!Send`. `Notify::notified` is lifetime-bound to `Notify`, therefore
+255                // we need to declare `notify` outside of the block, and initialize it inside.
+256                let notify;
+257                {
+258                    let mut conn = conn.state.lock("SendStream::stopped");
+259                    if let Some(output) = send_stream_stopped(&mut conn, stream, is_0rtt) {
+260                        return output;
+261                    }
+262
+263                    notify = conn.stopped.entry(stream).or_default().clone();
+264                    notify.notified()
+265                }
+266                .await
+267            }
+268        }
+269    }
+270
+271    /// Get the identity of this stream
+272    pub fn id(&self) -> StreamId {
+273        self.stream
+274    }
+275
+276    /// Attempt to write bytes from buf into the stream.
+277    ///
+278    /// On success, returns Poll::Ready(Ok(num_bytes_written)).
+279    ///
+280    /// If the stream is not ready for writing, the method returns Poll::Pending and arranges
+281    /// for the current task (via cx.waker().wake_by_ref()) to receive a notification when the
+282    /// stream becomes writable or is closed.
+283    pub fn poll_write(
+284        self: Pin<&mut Self>,
+285        cx: &mut Context,
+286        buf: &[u8],
+287    ) -> Poll<Result<usize, WriteError>> {
+288        pin!(self.get_mut().write(buf)).as_mut().poll(cx)
+289    }
+290}
+291
+292/// Check if a send stream is stopped.
+293///
+294/// Returns `Some` if the stream is stopped or the connection is closed.
+295/// Returns `None` if the stream is not stopped.
+296fn send_stream_stopped(
+297    conn: &mut State,
+298    stream: StreamId,
+299    is_0rtt: bool,
+300) -> Option<Result<Option<VarInt>, StoppedError>> {
+301    if is_0rtt && conn.check_0rtt().is_err() {
+302        return Some(Err(StoppedError::ZeroRttRejected));
+303    }
+304    match conn.inner.send_stream(stream).stopped() {
+305        Err(ClosedStream { .. }) => Some(Ok(None)),
+306        Ok(Some(error_code)) => Some(Ok(Some(error_code))),
+307        Ok(None) => conn.error.clone().map(|error| Err(error.into())),
+308    }
+309}
+310
+311#[cfg(feature = "futures-io")]
+312impl futures_io::AsyncWrite for SendStream {
+313    fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll<io::Result<usize>> {
+314        self.poll_write(cx, buf).map_err(Into::into)
+315    }
+316
+317    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<()>> {
+318        Poll::Ready(Ok(()))
+319    }
+320
+321    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<()>> {
+322        Poll::Ready(self.get_mut().finish().map_err(Into::into))
+323    }
+324}
+325
+326impl tokio::io::AsyncWrite for SendStream {
+327    fn poll_write(
+328        self: Pin<&mut Self>,
+329        cx: &mut Context<'_>,
+330        buf: &[u8],
+331    ) -> Poll<io::Result<usize>> {
+332        self.poll_write(cx, buf).map_err(Into::into)
+333    }
+334
+335    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<()>> {
+336        Poll::Ready(Ok(()))
+337    }
+338
+339    fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<()>> {
+340        Poll::Ready(self.get_mut().finish().map_err(Into::into))
+341    }
+342}
+343
+344impl Drop for SendStream {
+345    fn drop(&mut self) {
+346        let mut conn = self.conn.state.lock("SendStream::drop");
+347
+348        // clean up any previously registered wakers
+349        conn.blocked_writers.remove(&self.stream);
+350
+351        if conn.error.is_some() || (self.is_0rtt && conn.check_0rtt().is_err()) {
+352            return;
+353        }
+354        match conn.inner.send_stream(self.stream).finish() {
+355            Ok(()) => conn.wake(),
+356            Err(FinishError::Stopped(reason)) => {
+357                if conn.inner.send_stream(self.stream).reset(reason).is_ok() {
+358                    conn.wake();
+359                }
+360            }
+361            // Already finished or reset, which is fine.
+362            Err(FinishError::ClosedStream) => {}
+363        }
+364    }
+365}
+366
+367/// Errors that arise from writing to a stream
+368#[derive(Debug, Error, Clone, PartialEq, Eq)]
+369pub enum WriteError {
+370    /// The peer is no longer accepting data on this stream
+371    ///
+372    /// Carries an application-defined error code.
+373    #[error("sending stopped by peer: error {0}")]
+374    Stopped(VarInt),
+375    /// The connection was lost
+376    #[error("connection lost")]
+377    ConnectionLost(#[from] ConnectionError),
+378    /// The stream has already been finished or reset
+379    #[error("closed stream")]
+380    ClosedStream,
+381    /// This was a 0-RTT stream and the server rejected it
+382    ///
+383    /// Can only occur on clients for 0-RTT streams, which can be opened using
+384    /// [`Connecting::into_0rtt()`].
+385    ///
+386    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+387    #[error("0-RTT rejected")]
+388    ZeroRttRejected,
+389}
+390
+391impl From<ClosedStream> for WriteError {
+392    #[inline]
+393    fn from(_: ClosedStream) -> Self {
+394        Self::ClosedStream
+395    }
+396}
+397
+398impl From<StoppedError> for WriteError {
+399    fn from(x: StoppedError) -> Self {
+400        match x {
+401            StoppedError::ConnectionLost(e) => Self::ConnectionLost(e),
+402            StoppedError::ZeroRttRejected => Self::ZeroRttRejected,
+403        }
+404    }
+405}
+406
+407impl From<WriteError> for io::Error {
+408    fn from(x: WriteError) -> Self {
+409        use WriteError::*;
+410        let kind = match x {
+411            Stopped(_) | ZeroRttRejected => io::ErrorKind::ConnectionReset,
+412            ConnectionLost(_) | ClosedStream => io::ErrorKind::NotConnected,
+413        };
+414        Self::new(kind, x)
+415    }
+416}
+417
+418/// Errors that arise while monitoring for a send stream stop from the peer
+419#[derive(Debug, Error, Clone, PartialEq, Eq)]
+420pub enum StoppedError {
+421    /// The connection was lost
+422    #[error("connection lost")]
+423    ConnectionLost(#[from] ConnectionError),
+424    /// This was a 0-RTT stream and the server rejected it
+425    ///
+426    /// Can only occur on clients for 0-RTT streams, which can be opened using
+427    /// [`Connecting::into_0rtt()`].
+428    ///
+429    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+430    #[error("0-RTT rejected")]
+431    ZeroRttRejected,
+432}
+433
+434impl From<StoppedError> for io::Error {
+435    fn from(x: StoppedError) -> Self {
+436        use StoppedError::*;
+437        let kind = match x {
+438            ZeroRttRejected => io::ErrorKind::ConnectionReset,
+439            ConnectionLost(_) => io::ErrorKind::NotConnected,
+440        };
+441        Self::new(kind, x)
+442    }
+443}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn/work_limiter.rs.html b/pr/299/docs/src/iroh_quinn/work_limiter.rs.html new file mode 100644 index 000000000..ad6c82eea --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/address_discovery.rs.html b/pr/299/docs/src/iroh_quinn_proto/address_discovery.rs.html new file mode 100644 index 000000000..d65b64801 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/bloom_token_log.rs.html b/pr/299/docs/src/iroh_quinn_proto/bloom_token_log.rs.html new file mode 100644 index 000000000..a2c6b8721 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/cid_generator.rs.html b/pr/299/docs/src/iroh_quinn_proto/cid_generator.rs.html new file mode 100644 index 000000000..111ef0b80 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/cid_queue.rs.html b/pr/299/docs/src/iroh_quinn_proto/cid_queue.rs.html new file mode 100644 index 000000000..1ce9a8be5 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/cid_queue.rs.html @@ -0,0 +1,486 @@ +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    // TODO(@divma): remove
+129    #[cfg(test)]
+130    pub(crate) fn next_reserved(&mut self) -> Option<ConnectionId> {
+131        let (i, cid_data) = self.iter_from_reserved().nth(1)?;
+132
+133        self.cursor_reserved = (self.cursor_reserved + i) % Self::LEN;
+134        Some(cid_data.0)
+135    }
+136
+137    /// Iterate CIDs in CidQueue that are not `None`, including the active CID
+138    fn iter_from_active(&self) -> impl Iterator<Item = (usize, CidData)> + '_ {
+139        (0..Self::LEN).filter_map(move |step| {
+140            let index = (self.cursor + step) % Self::LEN;
+141            self.buffer[index].map(|cid_data| (step, cid_data))
+142        })
+143    }
+144
+145    /// Iterate CIDs in CidQueue that are not `None`, including the active CID.
+146    ///
+147    /// Along with the CID, it returns the offset counted from [`Self::cursor_reserved`] where the CID is stored.
+148    fn iter_from_reserved(&self) -> impl Iterator<Item = (usize, CidData)> + '_ {
+149        (0..(Self::LEN - self.reserved_len())).filter_map(move |step| {
+150            let index = (self.cursor_reserved + step) % Self::LEN;
+151            self.buffer[index].map(|cid_data| (step, cid_data))
+152        })
+153    }
+154
+155    /// The length of the internal buffer's section of CIDs that are marked as reserved.
+156    fn reserved_len(&self) -> usize {
+157        if self.cursor_reserved >= self.cursor {
+158            self.cursor_reserved - self.cursor
+159        } else {
+160            self.cursor_reserved + Self::LEN - self.cursor
+161        }
+162    }
+163
+164    /// Replace the initial CID
+165    pub(crate) fn update_initial_cid(&mut self, cid: ConnectionId) {
+166        debug_assert_eq!(self.offset, 0);
+167        self.buffer[self.cursor] = Some(CidData(cid, None));
+168    }
+169
+170    /// Return active remote CID itself
+171    pub(crate) fn active(&self) -> ConnectionId {
+172        self.buffer[self.cursor].unwrap().0
+173    }
+174
+175    /// Return the sequence number of active remote CID
+176    pub(crate) fn active_seq(&self) -> u64 {
+177        self.offset
+178    }
+179
+180    pub(crate) const LEN: usize = 5;
+181}
+182
+183#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+184pub(crate) enum InsertError {
+185    /// CID was already retired
+186    Retired,
+187    /// Sequence number violates the leading edge of the window
+188    ExceedsLimit,
+189}
+190
+191#[cfg(test)]
+192mod tests {
+193    use super::*;
+194
+195    fn cid(sequence: u64, retire_prior_to: u64) -> NewConnectionId {
+196        NewConnectionId {
+197            path_id: None,
+198            sequence,
+199            id: ConnectionId::new(&sequence.to_be_bytes()),
+200            reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]),
+201            retire_prior_to,
+202        }
+203    }
+204
+205    fn initial_cid() -> ConnectionId {
+206        ConnectionId::new(&[0xFF; 8])
+207    }
+208
+209    #[test]
+210    fn next_dense() {
+211        let mut q = CidQueue::new(initial_cid());
+212        assert!(q.next().is_none());
+213        assert!(q.next().is_none());
+214
+215        for i in 1..CidQueue::LEN as u64 {
+216            q.insert(cid(i, 0)).unwrap();
+217        }
+218        for i in 1..CidQueue::LEN as u64 {
+219            let (_, retire) = q.next().unwrap();
+220            assert_eq!(q.active_seq(), i);
+221            assert_eq!(retire.end - retire.start, 1);
+222        }
+223        assert!(q.next().is_none());
+224    }
+225
+226    #[test]
+227    fn next_sparse() {
+228        let mut q = CidQueue::new(initial_cid());
+229        let seqs = (1..CidQueue::LEN as u64).filter(|x| x % 2 == 0);
+230        for i in seqs.clone() {
+231            q.insert(cid(i, 0)).unwrap();
+232        }
+233        for i in seqs {
+234            let (_, retire) = q.next().unwrap();
+235            dbg!(&retire);
+236            assert_eq!(q.active_seq(), i);
+237            assert_eq!(retire, (q.active_seq().saturating_sub(2))..q.active_seq());
+238        }
+239        assert!(q.next().is_none());
+240    }
+241
+242    #[test]
+243    fn wrap() {
+244        let mut q = CidQueue::new(initial_cid());
+245
+246        for i in 1..CidQueue::LEN as u64 {
+247            q.insert(cid(i, 0)).unwrap();
+248        }
+249        for _ in 1..(CidQueue::LEN as u64 - 1) {
+250            q.next().unwrap();
+251        }
+252        for i in CidQueue::LEN as u64..(CidQueue::LEN as u64 + 3) {
+253            q.insert(cid(i, 0)).unwrap();
+254        }
+255        for i in (CidQueue::LEN as u64 - 1)..(CidQueue::LEN as u64 + 3) {
+256            q.next().unwrap();
+257            assert_eq!(q.active_seq(), i);
+258        }
+259        assert!(q.next().is_none());
+260    }
+261
+262    #[test]
+263    fn retire_dense() {
+264        let mut q = CidQueue::new(initial_cid());
+265
+266        for i in 1..CidQueue::LEN as u64 {
+267            q.insert(cid(i, 0)).unwrap();
+268        }
+269        assert_eq!(q.active_seq(), 0);
+270
+271        assert_eq!(q.insert(cid(4, 2)).unwrap().unwrap().0, 0..2);
+272        assert_eq!(q.active_seq(), 2);
+273        assert_eq!(q.insert(cid(4, 2)), Ok(None));
+274
+275        for i in 2..(CidQueue::LEN as u64 - 1) {
+276            let _ = q.next().unwrap();
+277            assert_eq!(q.active_seq(), i + 1);
+278            assert_eq!(q.insert(cid(i + 1, i + 1)), Ok(None));
+279        }
+280
+281        assert!(q.next().is_none());
+282    }
+283
+284    #[test]
+285    fn retire_sparse() {
+286        // Retiring CID 0 when CID 1 is not known should retire CID 1 as we move to CID 2
+287        let mut q = CidQueue::new(initial_cid());
+288        q.insert(cid(2, 0)).unwrap();
+289        assert_eq!(q.insert(cid(3, 1)).unwrap().unwrap().0, 0..2,);
+290        assert_eq!(q.active_seq(), 2);
+291    }
+292
+293    #[test]
+294    fn retire_many() {
+295        let mut q = CidQueue::new(initial_cid());
+296        q.insert(cid(2, 0)).unwrap();
+297        assert_eq!(
+298            q.insert(cid(1_000_000, 1_000_000)).unwrap().unwrap().0,
+299            0..CidQueue::LEN as u64,
+300        );
+301        assert_eq!(q.active_seq(), 1_000_000);
+302    }
+303
+304    #[test]
+305    fn insert_limit() {
+306        let mut q = CidQueue::new(initial_cid());
+307        assert_eq!(q.insert(cid(CidQueue::LEN as u64 - 1, 0)), Ok(None));
+308        assert_eq!(
+309            q.insert(cid(CidQueue::LEN as u64, 0)),
+310            Err(InsertError::ExceedsLimit)
+311        );
+312    }
+313
+314    #[test]
+315    fn insert_duplicate() {
+316        let mut q = CidQueue::new(initial_cid());
+317        q.insert(cid(0, 0)).unwrap();
+318        q.insert(cid(0, 0)).unwrap();
+319    }
+320
+321    #[test]
+322    fn insert_retired() {
+323        let mut q = CidQueue::new(initial_cid());
+324        assert_eq!(
+325            q.insert(cid(0, 0)),
+326            Ok(None),
+327            "reinserting active CID succeeds"
+328        );
+329        assert!(q.next().is_none(), "active CID isn't requeued");
+330        q.insert(cid(1, 0)).unwrap();
+331        q.next().unwrap();
+332        assert_eq!(
+333            q.insert(cid(0, 0)),
+334            Err(InsertError::Retired),
+335            "previous active CID is already retired"
+336        );
+337    }
+338
+339    #[test]
+340    fn retire_then_insert_next() {
+341        let mut q = CidQueue::new(initial_cid());
+342        for i in 1..CidQueue::LEN as u64 {
+343            q.insert(cid(i, 0)).unwrap();
+344        }
+345        q.next().unwrap();
+346        q.insert(cid(CidQueue::LEN as u64, 0)).unwrap();
+347        assert_eq!(
+348            q.insert(cid(CidQueue::LEN as u64 + 1, 0)),
+349            Err(InsertError::ExceedsLimit)
+350        );
+351    }
+352
+353    #[test]
+354    fn always_valid() {
+355        let mut q = CidQueue::new(initial_cid());
+356        assert!(q.next().is_none());
+357        assert_eq!(q.active(), initial_cid());
+358        assert_eq!(q.active_seq(), 0);
+359    }
+360
+361    #[test]
+362    fn reserved_smoke() {
+363        let mut q = CidQueue::new(initial_cid());
+364        assert_eq!(q.next_reserved(), None);
+365
+366        let one = cid(1, 0);
+367        q.insert(one).unwrap();
+368        assert_eq!(q.next_reserved(), Some(one.id));
+369
+370        let two = cid(2, 2);
+371        let (retired_range, reset_token) = q.insert(two).unwrap().unwrap();
+372        assert_eq!(reset_token, two.reset_token);
+373        assert_eq!(retired_range, 0..2);
+374
+375        assert_eq!(q.next_reserved(), None);
+376
+377        let four = cid(4, 2);
+378        q.insert(four).unwrap();
+379        println!("{q:?}");
+380        assert_eq!(q.next_reserved(), Some(four.id));
+381        assert_eq!(q.active(), two.id);
+382
+383        assert_eq!(q.next(), None);
+384    }
+385
+386    #[test]
+387    fn reserve_multiple() {
+388        let mut q = CidQueue::new(initial_cid());
+389        let one = cid(1, 0);
+390        let two = cid(2, 0);
+391        q.insert(one).unwrap();
+392        q.insert(two).unwrap();
+393        assert_eq!(q.next_reserved(), Some(one.id));
+394        assert_eq!(q.next_reserved(), Some(two.id));
+395        assert_eq!(q.next_reserved(), None);
+396    }
+397
+398    #[test]
+399    fn reserve_multiple_sparse() {
+400        let mut q = CidQueue::new(initial_cid());
+401        let two = cid(2, 0);
+402        let four = cid(4, 0);
+403        q.insert(two).unwrap();
+404        q.insert(four).unwrap();
+405        assert_eq!(q.next_reserved(), Some(two.id));
+406        assert_eq!(q.next_reserved(), Some(four.id));
+407        assert_eq!(q.next_reserved(), None);
+408    }
+409
+410    #[test]
+411    fn reserve_many_next_clears() {
+412        let mut q = CidQueue::new(initial_cid());
+413        for i in 1..CidQueue::LEN {
+414            q.insert(cid(i as u64, 0)).unwrap();
+415        }
+416
+417        for _ in 0..CidQueue::LEN - 2 {
+418            assert!(q.next_reserved().is_some());
+419        }
+420
+421        assert!(q.next().is_some());
+422        assert_eq!(q.next(), None);
+423    }
+424
+425    #[test]
+426    fn reserve_many_next_reserved_none() {
+427        let mut q = CidQueue::new(initial_cid());
+428        for i in 1..CidQueue::LEN {
+429            q.insert(cid(i as u64, 0)).unwrap();
+430        }
+431
+432        for _ in 0..CidQueue::LEN - 1 {
+433            assert!(q.next_reserved().is_some());
+434        }
+435
+436        assert_eq!(q.next_reserved(), None);
+437    }
+438
+439    #[test]
+440    fn one_active_all_else_reserved_next_none() {
+441        let mut q = CidQueue::new(initial_cid());
+442        for i in 1..CidQueue::LEN {
+443            q.insert(cid(i as u64, 0)).unwrap();
+444        }
+445
+446        for _ in 0..CidQueue::LEN - 1 {
+447            assert!(q.next_reserved().is_some());
+448        }
+449
+450        assert_eq!(q.next(), None);
+451    }
+452
+453    #[test]
+454    fn insert_reserve_advance() {
+455        let mut q = CidQueue::new(initial_cid());
+456
+457        let first = cid(1, 0);
+458        let second = cid(2, 0);
+459        let third = cid(3, 0);
+460
+461        q.insert(first).unwrap();
+462        q.insert(second).unwrap();
+463
+464        assert_eq!(q.next_reserved(), Some(first.id));
+465        q.insert(third).unwrap();
+466        q.next();
+467        assert_eq!(q.active(), second.id);
+468    }
+469
+470    #[test]
+471    fn sparse_insert_reserve_insert_advance() {
+472        let mut q = CidQueue::new(initial_cid());
+473
+474        let one = cid(1, 0);
+475        let two = cid(2, 0);
+476        let three = cid(3, 0);
+477
+478        q.insert(two).unwrap();
+479        q.insert(three).unwrap();
+480        assert_eq!(q.next_reserved(), Some(two.id));
+481        q.insert(one).unwrap();
+482        q.next();
+483        assert_eq!(q.active(), three.id);
+484        assert_eq!(q.next_reserved(), None);
+485    }
+486}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/coding.rs.html b/pr/299/docs/src/iroh_quinn_proto/coding.rs.html new file mode 100644 index 000000000..4799e8f89 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/coding.rs.html @@ -0,0 +1,146 @@ +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}
+38impl Encodable for u8 {
+39    fn encode<B: BufMut>(&self, buf: &mut B) {
+40        buf.put_u8(*self);
+41    }
+42}
+43
+44impl Decodable for u16 {
+45    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+46        if buf.remaining() < 2 {
+47            return Err(UnexpectedEnd);
+48        }
+49        Ok(buf.get_u16())
+50    }
+51}
+52impl Encodable for u16 {
+53    fn encode<B: BufMut>(&self, buf: &mut B) {
+54        buf.put_u16(*self);
+55    }
+56}
+57
+58impl Decodable for u32 {
+59    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+60        if buf.remaining() < 4 {
+61            return Err(UnexpectedEnd);
+62        }
+63        Ok(buf.get_u32())
+64    }
+65}
+66impl Encodable for u32 {
+67    fn encode<B: BufMut>(&self, buf: &mut B) {
+68        buf.put_u32(*self);
+69    }
+70}
+71
+72impl Decodable for u64 {
+73    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+74        if buf.remaining() < 8 {
+75            return Err(UnexpectedEnd);
+76        }
+77        Ok(buf.get_u64())
+78    }
+79}
+80impl Encodable for u64 {
+81    fn encode<B: BufMut>(&self, buf: &mut B) {
+82        buf.put_u64(*self);
+83    }
+84}
+85
+86impl Decodable for Ipv4Addr {
+87    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+88        if buf.remaining() < 4 {
+89            return Err(UnexpectedEnd);
+90        }
+91        let mut octets = [0; 4];
+92        buf.copy_to_slice(&mut octets);
+93        Ok(octets.into())
+94    }
+95}
+96impl Encodable for Ipv4Addr {
+97    fn encode<B: BufMut>(&self, buf: &mut B) {
+98        buf.put_slice(&self.octets());
+99    }
+100}
+101
+102impl Decodable for Ipv6Addr {
+103    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+104        if buf.remaining() < 16 {
+105            return Err(UnexpectedEnd);
+106        }
+107        let mut octets = [0; 16];
+108        buf.copy_to_slice(&mut octets);
+109        Ok(octets.into())
+110    }
+111}
+112impl Encodable for Ipv6Addr {
+113    fn encode<B: BufMut>(&self, buf: &mut B) {
+114        buf.put_slice(&self.octets());
+115    }
+116}
+117
+118pub(crate) trait BufExt {
+119    fn get<T: Decodable>(&mut self) -> Result<T>;
+120    fn get_var(&mut self) -> Result<u64>;
+121}
+122
+123impl<T: Buf> BufExt for T {
+124    fn get<U: Decodable>(&mut self) -> Result<U> {
+125        U::decode(self)
+126    }
+127
+128    fn get_var(&mut self) -> Result<u64> {
+129        Ok(VarInt::decode(self)?.into_inner())
+130    }
+131}
+132
+133pub(crate) trait BufMutExt {
+134    fn write<T: Encodable>(&mut self, x: T);
+135    fn write_var(&mut self, x: u64);
+136}
+137
+138impl<T: BufMut> BufMutExt for T {
+139    fn write<U: Encodable>(&mut self, x: U) {
+140        x.encode(self);
+141    }
+142
+143    fn write_var(&mut self, x: u64) {
+144        VarInt::from_u64(x).unwrap().encode(self);
+145    }
+146}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/config/mod.rs.html b/pr/299/docs/src/iroh_quinn_proto/config/mod.rs.html new file mode 100644 index 000000000..f0a2971c4 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/config/mod.rs.html @@ -0,0 +1,703 @@ +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    #[deprecated(since = "0.11.13", note = "use `try_with_platform_verifier()` instead")]
+631    #[cfg(feature = "platform-verifier")]
+632    pub fn with_platform_verifier() -> Self {
+633        Self::try_with_platform_verifier().expect("use try_with_platform_verifier() instead")
+634    }
+635
+636    /// Create a client configuration that trusts the platform's native roots
+637    #[cfg(feature = "platform-verifier")]
+638    pub fn try_with_platform_verifier() -> Result<Self, rustls::Error> {
+639        Ok(Self::new(Arc::new(
+640            crypto::rustls::QuicClientConfig::with_platform_verifier()?,
+641        )))
+642    }
+643
+644    /// Create a client configuration that trusts specified trust anchors
+645    pub fn with_root_certificates(
+646        roots: Arc<rustls::RootCertStore>,
+647    ) -> Result<Self, rustls::client::VerifierBuilderError> {
+648        Ok(Self::new(Arc::new(crypto::rustls::QuicClientConfig::new(
+649            WebPkiServerVerifier::builder_with_provider(roots, configured_provider()).build()?,
+650        ))))
+651    }
+652}
+653
+654impl fmt::Debug for ClientConfig {
+655    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+656        fmt.debug_struct("ClientConfig")
+657            .field("transport", &self.transport)
+658            // crypto not debug
+659            // token_store not debug
+660            .field("version", &self.version)
+661            .finish_non_exhaustive()
+662    }
+663}
+664
+665/// Errors in the configuration of an endpoint
+666#[derive(Debug, Error, Clone, PartialEq, Eq)]
+667#[non_exhaustive]
+668pub enum ConfigError {
+669    /// Value exceeds supported bounds
+670    #[error("value exceeds supported bounds")]
+671    OutOfBounds,
+672}
+673
+674impl From<TryFromIntError> for ConfigError {
+675    fn from(_: TryFromIntError) -> Self {
+676        Self::OutOfBounds
+677    }
+678}
+679
+680impl From<VarIntBoundsExceeded> for ConfigError {
+681    fn from(_: VarIntBoundsExceeded) -> Self {
+682        Self::OutOfBounds
+683    }
+684}
+685
+686/// Object to get current [`SystemTime`]
+687///
+688/// This exists to allow system time to be mocked in tests, or wherever else desired.
+689pub trait TimeSource: Send + Sync {
+690    /// Get [`SystemTime::now()`](SystemTime::now) or the mocked equivalent
+691    fn now(&self) -> SystemTime;
+692}
+693
+694/// Default implementation of [`TimeSource`]
+695///
+696/// Implements `now` by calling [`SystemTime::now()`](SystemTime::now).
+697pub struct StdSystemTime;
+698
+699impl TimeSource for StdSystemTime {
+700    fn now(&self) -> SystemTime {
+701        SystemTime::now()
+702    }
+703}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/config/qlog.rs.html b/pr/299/docs/src/iroh_quinn_proto/config/qlog.rs.html new file mode 100644 index 000000000..faa2f75fd --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/config/transport.rs.html b/pr/299/docs/src/iroh_quinn_proto/config/transport.rs.html new file mode 100644 index 000000000..dc343d2ce --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/congestion.rs.html b/pr/299/docs/src/iroh_quinn_proto/congestion.rs.html new file mode 100644 index 000000000..4c4feb6bf --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/congestion/bbr/bw_estimation.rs.html b/pr/299/docs/src/iroh_quinn_proto/congestion/bbr/bw_estimation.rs.html new file mode 100644 index 000000000..1c23f5cd5 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/congestion/bbr/min_max.rs.html b/pr/299/docs/src/iroh_quinn_proto/congestion/bbr/min_max.rs.html new file mode 100644 index 000000000..a64b972e4 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/congestion/bbr/mod.rs.html b/pr/299/docs/src/iroh_quinn_proto/congestion/bbr/mod.rs.html new file mode 100644 index 000000000..5290f93a7 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/congestion/bbr/mod.rs.html @@ -0,0 +1,647 @@ +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            if self.exit_probe_rtt_at.is_none() {
+239                // If the window has reached the appropriate size, schedule exiting
+240                // ProbeRtt.  The CWND during ProbeRtt is
+241                // kMinimumCongestionWindow, but we allow an extra packet since QUIC
+242                // checks CWND before sending a packet.
+243                if bytes_in_flight < self.get_probe_rtt_cwnd() + self.current_mtu {
+244                    const K_PROBE_RTT_TIME: Duration = Duration::from_millis(200);
+245                    self.exit_probe_rtt_at = Some(now + K_PROBE_RTT_TIME);
+246                }
+247            } else if is_round_start && now >= self.exit_probe_rtt_at.unwrap() {
+248                if !self.is_at_full_bandwidth {
+249                    self.enter_startup_mode();
+250                } else {
+251                    self.enter_probe_bandwidth_mode(now);
+252                }
+253            }
+254        }
+255
+256        self.exiting_quiescence = false;
+257    }
+258
+259    fn get_target_cwnd(&self, gain: f32) -> u64 {
+260        let bw = self.max_bandwidth.get_estimate();
+261        let bdp = self.min_rtt.as_micros() as u64 * bw;
+262        let bdpf = bdp as f64;
+263        let cwnd = ((gain as f64 * bdpf) / 1_000_000f64) as u64;
+264        // BDP estimate will be zero if no bandwidth samples are available yet.
+265        if cwnd == 0 {
+266            return self.init_cwnd;
+267        }
+268        cwnd.max(self.min_cwnd)
+269    }
+270
+271    fn get_probe_rtt_cwnd(&self) -> u64 {
+272        const K_MODERATE_PROBE_RTT_MULTIPLIER: f32 = 0.75;
+273        if PROBE_RTT_BASED_ON_BDP {
+274            return self.get_target_cwnd(K_MODERATE_PROBE_RTT_MULTIPLIER);
+275        }
+276        self.min_cwnd
+277    }
+278
+279    fn calculate_pacing_rate(&mut self) {
+280        let bw = self.max_bandwidth.get_estimate();
+281        if bw == 0 {
+282            return;
+283        }
+284        let target_rate = (bw as f64 * self.pacing_gain as f64) as u64;
+285        if self.is_at_full_bandwidth {
+286            self.pacing_rate = target_rate;
+287            return;
+288        }
+289
+290        // Pace at the rate of initial_window / RTT as soon as RTT measurements are
+291        // available.
+292        if self.pacing_rate == 0 && self.min_rtt.as_nanos() != 0 {
+293            self.pacing_rate =
+294                BandwidthEstimation::bw_from_delta(self.init_cwnd, self.min_rtt).unwrap();
+295            return;
+296        }
+297
+298        // Do not decrease the pacing rate during startup.
+299        if self.pacing_rate < target_rate {
+300            self.pacing_rate = target_rate;
+301        }
+302    }
+303
+304    fn calculate_cwnd(&mut self, bytes_acked: u64, excess_acked: u64) {
+305        if self.mode == Mode::ProbeRtt {
+306            return;
+307        }
+308        let mut target_window = self.get_target_cwnd(self.cwnd_gain);
+309        if self.is_at_full_bandwidth {
+310            // Add the max recently measured ack aggregation to CWND.
+311            target_window += self.ack_aggregation.max_ack_height.get();
+312        } else {
+313            // Add the most recent excess acked.  Because CWND never decreases in
+314            // STARTUP, this will automatically create a very localized max filter.
+315            target_window += excess_acked;
+316        }
+317        // Instead of immediately setting the target CWND as the new one, BBR grows
+318        // the CWND towards |target_window| by only increasing it |bytes_acked| at a
+319        // time.
+320        if self.is_at_full_bandwidth {
+321            self.cwnd = target_window.min(self.cwnd + bytes_acked);
+322        } else if (self.cwnd_gain < target_window as f32) || (self.acked_bytes < self.init_cwnd) {
+323            // If the connection is not yet out of startup phase, do not decrease
+324            // the window.
+325            self.cwnd += bytes_acked;
+326        }
+327
+328        // Enforce the limits on the congestion window.
+329        if self.cwnd < self.min_cwnd {
+330            self.cwnd = self.min_cwnd;
+331        }
+332    }
+333
+334    fn calculate_recovery_window(&mut self, bytes_acked: u64, bytes_lost: u64, in_flight: u64) {
+335        if !self.recovery_state.in_recovery() {
+336            return;
+337        }
+338        // Set up the initial recovery window.
+339        if self.recovery_window == 0 {
+340            self.recovery_window = self.min_cwnd.max(in_flight + bytes_acked);
+341            return;
+342        }
+343
+344        // Remove losses from the recovery window, while accounting for a potential
+345        // integer underflow.
+346        if self.recovery_window >= bytes_lost {
+347            self.recovery_window -= bytes_lost;
+348        } else {
+349            // k_max_segment_size = current_mtu
+350            self.recovery_window = self.current_mtu;
+351        }
+352        // In CONSERVATION mode, just subtracting losses is sufficient.  In GROWTH,
+353        // release additional |bytes_acked| to achieve a slow-start-like behavior.
+354        if self.recovery_state == RecoveryState::Growth {
+355            self.recovery_window += bytes_acked;
+356        }
+357
+358        // Sanity checks.  Ensure that we always allow to send at least an MSS or
+359        // |bytes_acked| in response, whichever is larger.
+360        self.recovery_window = self
+361            .recovery_window
+362            .max(in_flight + bytes_acked)
+363            .max(self.min_cwnd);
+364    }
+365
+366    /// <https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control#section-4.3.2.2>
+367    fn check_if_full_bw_reached(&mut self, app_limited: bool) {
+368        if app_limited {
+369            return;
+370        }
+371        let target = (self.bw_at_last_round as f64 * K_STARTUP_GROWTH_TARGET as f64) as u64;
+372        let bw = self.max_bandwidth.get_estimate();
+373        if bw >= target {
+374            self.bw_at_last_round = bw;
+375            self.round_wo_bw_gain = 0;
+376            self.ack_aggregation.max_ack_height.reset();
+377            return;
+378        }
+379
+380        self.round_wo_bw_gain += 1;
+381        if self.round_wo_bw_gain >= K_ROUND_TRIPS_WITHOUT_GROWTH_BEFORE_EXITING_STARTUP as u64
+382            || (self.recovery_state.in_recovery())
+383        {
+384            self.is_at_full_bandwidth = true;
+385        }
+386    }
+387}
+388
+389impl Controller for Bbr {
+390    fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64) {
+391        self.max_sent_packet_number = last_packet_number;
+392        self.max_bandwidth.on_sent(now, bytes);
+393    }
+394
+395    fn on_ack(
+396        &mut self,
+397        now: Instant,
+398        sent: Instant,
+399        bytes: u64,
+400        app_limited: bool,
+401        rtt: &RttEstimator,
+402    ) {
+403        self.max_bandwidth
+404            .on_ack(now, sent, bytes, self.round_count, app_limited);
+405        self.acked_bytes += bytes;
+406        if self.is_min_rtt_expired(now, app_limited) || self.min_rtt > rtt.min() {
+407            self.min_rtt = rtt.min();
+408        }
+409    }
+410
+411    fn on_end_acks(
+412        &mut self,
+413        now: Instant,
+414        in_flight: u64,
+415        app_limited: bool,
+416        largest_packet_num_acked: Option<u64>,
+417    ) {
+418        let bytes_acked = self.max_bandwidth.bytes_acked_this_window();
+419        let excess_acked = self.ack_aggregation.update_ack_aggregation_bytes(
+420            bytes_acked,
+421            now,
+422            self.round_count,
+423            self.max_bandwidth.get_estimate(),
+424        );
+425        self.max_bandwidth.end_acks(self.round_count, app_limited);
+426        if let Some(largest_acked_packet) = largest_packet_num_acked {
+427            self.max_acked_packet_number = largest_acked_packet;
+428        }
+429
+430        let mut is_round_start = false;
+431        if bytes_acked > 0 {
+432            is_round_start =
+433                self.max_acked_packet_number > self.current_round_trip_end_packet_number;
+434            if is_round_start {
+435                self.current_round_trip_end_packet_number = self.max_sent_packet_number;
+436                self.round_count += 1;
+437            }
+438        }
+439
+440        self.update_recovery_state(is_round_start);
+441
+442        if self.mode == Mode::ProbeBw {
+443            self.update_gain_cycle_phase(now, in_flight);
+444        }
+445
+446        if is_round_start && !self.is_at_full_bandwidth {
+447            self.check_if_full_bw_reached(app_limited);
+448        }
+449
+450        self.maybe_exit_startup_or_drain(now, in_flight);
+451
+452        self.maybe_enter_or_exit_probe_rtt(now, is_round_start, in_flight, app_limited);
+453
+454        // After the model is updated, recalculate the pacing rate and congestion window.
+455        self.calculate_pacing_rate();
+456        self.calculate_cwnd(bytes_acked, excess_acked);
+457        self.calculate_recovery_window(bytes_acked, self.loss_state.lost_bytes, in_flight);
+458
+459        self.prev_in_flight_count = in_flight;
+460        self.loss_state.reset();
+461    }
+462
+463    fn on_congestion_event(
+464        &mut self,
+465        _now: Instant,
+466        _sent: Instant,
+467        _is_persistent_congestion: bool,
+468        _is_ecn: bool,
+469        lost_bytes: u64,
+470    ) {
+471        self.loss_state.lost_bytes += lost_bytes;
+472    }
+473
+474    fn on_mtu_update(&mut self, new_mtu: u16) {
+475        self.current_mtu = new_mtu as u64;
+476        self.min_cwnd = calculate_min_window(self.current_mtu);
+477        self.init_cwnd = self.config.initial_window.max(self.min_cwnd);
+478        self.cwnd = self.cwnd.max(self.min_cwnd);
+479    }
+480
+481    fn window(&self) -> u64 {
+482        if self.mode == Mode::ProbeRtt {
+483            return self.get_probe_rtt_cwnd();
+484        } else if self.recovery_state.in_recovery() && self.mode != Mode::Startup {
+485            return self.cwnd.min(self.recovery_window);
+486        }
+487        self.cwnd
+488    }
+489
+490    fn metrics(&self) -> ControllerMetrics {
+491        ControllerMetrics {
+492            congestion_window: self.window(),
+493            ssthresh: None,
+494            pacing_rate: Some(self.pacing_rate * 8),
+495        }
+496    }
+497
+498    fn clone_box(&self) -> Box<dyn Controller> {
+499        Box::new(self.clone())
+500    }
+501
+502    fn initial_window(&self) -> u64 {
+503        self.config.initial_window
+504    }
+505
+506    fn into_any(self: Box<Self>) -> Box<dyn Any> {
+507        self
+508    }
+509}
+510
+511/// Configuration for the [`Bbr`] congestion controller
+512#[derive(Debug, Clone)]
+513pub struct BbrConfig {
+514    initial_window: u64,
+515}
+516
+517impl BbrConfig {
+518    /// Default limit on the amount of outstanding data in bytes.
+519    ///
+520    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
+521    pub fn initial_window(&mut self, value: u64) -> &mut Self {
+522        self.initial_window = value;
+523        self
+524    }
+525}
+526
+527impl Default for BbrConfig {
+528    fn default() -> Self {
+529        Self {
+530            initial_window: K_MAX_INITIAL_CONGESTION_WINDOW * BASE_DATAGRAM_SIZE,
+531        }
+532    }
+533}
+534
+535impl ControllerFactory for BbrConfig {
+536    fn build(self: Arc<Self>, _now: Instant, current_mtu: u16) -> Box<dyn Controller> {
+537        Box::new(Bbr::new(self, current_mtu))
+538    }
+539}
+540
+541#[derive(Debug, Default, Copy, Clone)]
+542struct AckAggregationState {
+543    max_ack_height: MinMax,
+544    aggregation_epoch_start_time: Option<Instant>,
+545    aggregation_epoch_bytes: u64,
+546}
+547
+548impl AckAggregationState {
+549    fn update_ack_aggregation_bytes(
+550        &mut self,
+551        newly_acked_bytes: u64,
+552        now: Instant,
+553        round: u64,
+554        max_bandwidth: u64,
+555    ) -> u64 {
+556        // Compute how many bytes are expected to be delivered, assuming max
+557        // bandwidth is correct.
+558        let expected_bytes_acked = max_bandwidth
+559            * now
+560                .saturating_duration_since(self.aggregation_epoch_start_time.unwrap_or(now))
+561                .as_micros() as u64
+562            / 1_000_000;
+563
+564        // Reset the current aggregation epoch as soon as the ack arrival rate is
+565        // less than or equal to the max bandwidth.
+566        if self.aggregation_epoch_bytes <= expected_bytes_acked {
+567            // Reset to start measuring a new aggregation epoch.
+568            self.aggregation_epoch_bytes = newly_acked_bytes;
+569            self.aggregation_epoch_start_time = Some(now);
+570            return 0;
+571        }
+572
+573        // Compute how many extra bytes were delivered vs max bandwidth.
+574        // Include the bytes most recently acknowledged to account for stretch acks.
+575        self.aggregation_epoch_bytes += newly_acked_bytes;
+576        let diff = self.aggregation_epoch_bytes - expected_bytes_acked;
+577        self.max_ack_height.update_max(round, diff);
+578        diff
+579    }
+580}
+581
+582#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+583enum Mode {
+584    // Startup phase of the connection.
+585    Startup,
+586    // After achieving the highest possible bandwidth during the startup, lower
+587    // the pacing rate in order to drain the queue.
+588    Drain,
+589    // Cruising mode.
+590    ProbeBw,
+591    // Temporarily slow down sending in order to empty the buffer and measure
+592    // the real minimum RTT.
+593    ProbeRtt,
+594}
+595
+596// Indicates how the congestion control limits the amount of bytes in flight.
+597#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+598enum RecoveryState {
+599    // Do not limit.
+600    NotInRecovery,
+601    // Allow an extra outstanding byte for each byte acknowledged.
+602    Conservation,
+603    // Allow two extra outstanding bytes for each byte acknowledged (slow
+604    // start).
+605    Growth,
+606}
+607
+608impl RecoveryState {
+609    pub(super) fn in_recovery(&self) -> bool {
+610        !matches!(self, Self::NotInRecovery)
+611    }
+612}
+613
+614#[derive(Debug, Clone, Default)]
+615struct LossState {
+616    lost_bytes: u64,
+617}
+618
+619impl LossState {
+620    pub(super) fn reset(&mut self) {
+621        self.lost_bytes = 0;
+622    }
+623
+624    pub(super) fn has_losses(&self) -> bool {
+625        self.lost_bytes != 0
+626    }
+627}
+628
+629fn calculate_min_window(current_mtu: u64) -> u64 {
+630    4 * current_mtu
+631}
+632
+633// The gain used for the STARTUP, equal to 2/ln(2).
+634const K_DEFAULT_HIGH_GAIN: f32 = 2.885;
+635// The newly derived CWND gain for STARTUP, 2.
+636const K_DERIVED_HIGH_CWNDGAIN: f32 = 2.0;
+637// The cycle of gains used during the ProbeBw stage.
+638const K_PACING_GAIN: [f32; 8] = [1.25, 0.75, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
+639
+640const K_STARTUP_GROWTH_TARGET: f32 = 1.25;
+641const K_ROUND_TRIPS_WITHOUT_GROWTH_BEFORE_EXITING_STARTUP: u8 = 3;
+642
+643// Do not allow initial congestion window to be greater than 200 packets.
+644const K_MAX_INITIAL_CONGESTION_WINDOW: u64 = 200;
+645
+646const PROBE_RTT_BASED_ON_BDP: bool = true;
+647const DRAIN_TO_TARGET: bool = true;
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/congestion/cubic.rs.html b/pr/299/docs/src/iroh_quinn_proto/congestion/cubic.rs.html new file mode 100644 index 000000000..d0beb3ee4 --- /dev/null +++ b/pr/299/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            if self.state.window < prior_state.window {
+236                self.state = prior_state;
+237            }
+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/299/docs/src/iroh_quinn_proto/congestion/new_reno.rs.html b/pr/299/docs/src/iroh_quinn_proto/congestion/new_reno.rs.html new file mode 100644 index 000000000..c6b1f772b --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/connection/ack_frequency.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/ack_frequency.rs.html new file mode 100644 index 000000000..f6c1cd72b --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/connection/assembler.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/assembler.rs.html new file mode 100644 index 000000000..92fc59de0 --- /dev/null +++ b/pr/299/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 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/299/docs/src/iroh_quinn_proto/connection/cid_state.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/cid_state.rs.html new file mode 100644 index 000000000..f756a56ed --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/connection/datagrams.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/datagrams.rs.html new file mode 100644 index 000000000..6419a431a --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/datagrams.rs.html @@ -0,0 +1,215 @@ +datagrams.rs - source

iroh_quinn_proto/connection/
datagrams.rs

1use std::collections::VecDeque;
+2
+3use bytes::{BufMut, Bytes};
+4use thiserror::Error;
+5use tracing::{debug, trace};
+6
+7use super::Connection;
+8use crate::{
+9    TransportError,
+10    coding::Encodable,
+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    /// Used to ensure that reductions in MTU don't get us stuck in a state where we have a datagram
+150    /// queued but can't send it.
+151    pub(super) fn drop_oversized(&mut self, max_payload: usize) {
+152        self.outgoing.retain(|datagram| {
+153            let result = datagram.data.len() < max_payload;
+154            if !result {
+155                trace!(
+156                    "dropping {} byte datagram violating {} byte limit",
+157                    datagram.data.len(),
+158                    max_payload
+159                );
+160                self.outgoing_total -= datagram.data.len();
+161            }
+162            result
+163        });
+164    }
+165
+166    /// Attempt to write a datagram frame into `buf`, consuming it from `self.outgoing`
+167    ///
+168    /// Returns whether a frame was written. At most `max_size` bytes will be written, including
+169    /// framing.
+170    pub(super) fn write(&mut self, buf: &mut impl BufMut) -> bool {
+171        let datagram = match self.outgoing.pop_front() {
+172            Some(x) => x,
+173            None => return false,
+174        };
+175
+176        if buf.remaining_mut() < datagram.size(true) {
+177            // Future work: we could be more clever about cramming small datagrams into
+178            // mostly-full packets when a larger one is queued first
+179            self.outgoing.push_front(datagram);
+180            return false;
+181        }
+182
+183        trace!(len = datagram.data.len(), "DATAGRAM");
+184
+185        self.outgoing_total -= datagram.data.len();
+186        datagram.encode(buf);
+187        true
+188    }
+189
+190    pub(super) fn recv(&mut self) -> Option<Bytes> {
+191        let x = self.incoming.pop_front()?.data;
+192        self.recv_buffered -= x.len();
+193        Some(x)
+194    }
+195}
+196
+197/// Errors that can arise when sending a datagram
+198#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+199pub enum SendDatagramError {
+200    /// The peer does not support receiving datagram frames
+201    #[error("datagrams not supported by peer")]
+202    UnsupportedByPeer,
+203    /// Datagram support is disabled locally
+204    #[error("datagram support disabled")]
+205    Disabled,
+206    /// The datagram is larger than the connection can currently accommodate
+207    ///
+208    /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been
+209    /// exceeded.
+210    #[error("datagram too large")]
+211    TooLarge,
+212    /// Send would block
+213    #[error("datagram send blocked")]
+214    Blocked(Bytes),
+215}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/mod.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/mod.rs.html new file mode 100644 index 000000000..cf3dfa70c --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/mod.rs.html @@ -0,0 +1,6871 @@ +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    coding::{BufMutExt, Encodable},
+27    config::{ServerConfig, TransportConfig},
+28    congestion::Controller,
+29    connection::{
+30        qlog::{QlogRecvPacket, QlogSentPacket, QlogSink},
+31        spaces::LostPacket,
+32        timer::{ConnTimer, PathTimer},
+33    },
+34    crypto::{self, KeyPair, Keys, PacketKey},
+35    frame::{self, Close, Datagram, FrameStruct, NewToken, ObservedAddr},
+36    iroh_hp,
+37    packet::{
+38        FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, LongType, Packet,
+39        PacketNumber, PartialDecode, SpaceId,
+40    },
+41    range_set::ArrayRangeSet,
+42    shared::{
+43        ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint,
+44        EndpointEvent, EndpointEventInner,
+45    },
+46    token::{ResetToken, Token, TokenPayload},
+47    transport_parameters::TransportParameters,
+48};
+49
+50mod ack_frequency;
+51use ack_frequency::AckFrequencyState;
+52
+53mod assembler;
+54pub use assembler::Chunk;
+55
+56mod cid_state;
+57use cid_state::CidState;
+58
+59mod datagrams;
+60use datagrams::DatagramState;
+61pub use datagrams::{Datagrams, SendDatagramError};
+62
+63mod mtud;
+64mod pacing;
+65
+66mod packet_builder;
+67use packet_builder::{PacketBuilder, PadDatagram};
+68
+69mod packet_crypto;
+70use packet_crypto::{PrevCrypto, ZeroRttCrypto};
+71
+72mod paths;
+73pub use paths::{ClosedPath, PathEvent, PathId, PathStatus, RttEstimator, SetPathStatusError};
+74use paths::{PathData, PathState};
+75
+76pub(crate) mod qlog;
+77pub(crate) mod send_buffer;
+78
+79mod spaces;
+80#[cfg(fuzzing)]
+81pub use spaces::Retransmits;
+82#[cfg(not(fuzzing))]
+83use spaces::Retransmits;
+84use spaces::{PacketSpace, SendableFrames, SentPacket, ThinRetransmits};
+85
+86mod stats;
+87pub use stats::{ConnectionStats, FrameStats, PathStats, UdpStats};
+88
+89mod streams;
+90#[cfg(fuzzing)]
+91pub use streams::StreamsState;
+92#[cfg(not(fuzzing))]
+93use streams::StreamsState;
+94pub use streams::{
+95    Chunks, ClosedStream, FinishError, ReadError, ReadableError, RecvStream, SendStream,
+96    ShouldTransmit, StreamEvent, Streams, WriteError, Written,
+97};
+98
+99mod timer;
+100use timer::{Timer, TimerTable};
+101
+102mod transmit_buf;
+103use transmit_buf::TransmitBuf;
+104
+105mod state;
+106
+107#[cfg(not(fuzzing))]
+108use state::State;
+109#[cfg(fuzzing)]
+110pub use state::State;
+111use state::StateType;
+112
+113/// Protocol state and logic for a single QUIC connection
+114///
+115/// Objects of this type receive [`ConnectionEvent`]s and emit [`EndpointEvent`]s and application
+116/// [`Event`]s to make progress. To handle timeouts, a `Connection` returns timer updates and
+117/// expects timeouts through various methods. A number of simple getter methods are exposed
+118/// to allow callers to inspect some of the connection state.
+119///
+120/// `Connection` has roughly 4 types of methods:
+121///
+122/// - A. Simple getters, taking `&self`
+123/// - B. Handlers for incoming events from the network or system, named `handle_*`.
+124/// - C. State machine mutators, for incoming commands from the application. For convenience we
+125///   refer to this as "performing I/O" below, however as per the design of this library none of the
+126///   functions actually perform system-level I/O. For example, [`read`](RecvStream::read) and
+127///   [`write`](SendStream::write), but also things like [`reset`](SendStream::reset).
+128/// - D. Polling functions for outgoing events or actions for the caller to
+129///   take, named `poll_*`.
+130///
+131/// The simplest way to use this API correctly is to call (B) and (C) whenever
+132/// appropriate, then after each of those calls, as soon as feasible call all
+133/// polling methods (D) and deal with their outputs appropriately, e.g. by
+134/// passing it to the application or by making a system-level I/O call. You
+135/// should call the polling functions in this order:
+136///
+137/// 1. [`poll_transmit`](Self::poll_transmit)
+138/// 2. [`poll_timeout`](Self::poll_timeout)
+139/// 3. [`poll_endpoint_events`](Self::poll_endpoint_events)
+140/// 4. [`poll`](Self::poll)
+141///
+142/// Currently the only actual dependency is from (2) to (1), however additional
+143/// dependencies may be added in future, so the above order is recommended.
+144///
+145/// (A) may be called whenever desired.
+146///
+147/// Care should be made to ensure that the input events represent monotonically
+148/// increasing time. Specifically, calling [`handle_timeout`](Self::handle_timeout)
+149/// with events of the same [`Instant`] may be interleaved in any order with a
+150/// call to [`handle_event`](Self::handle_event) at that same instant; however
+151/// events or timeouts with different instants must not be interleaved.
+152pub struct Connection {
+153    endpoint_config: Arc<EndpointConfig>,
+154    config: Arc<TransportConfig>,
+155    rng: StdRng,
+156    crypto: Box<dyn crypto::Session>,
+157    /// The CID we initially chose, for use during the handshake
+158    handshake_cid: ConnectionId,
+159    /// The CID the peer initially chose, for use during the handshake
+160    rem_handshake_cid: ConnectionId,
+161    /// The [`PathData`] for each path
+162    ///
+163    /// This needs to be ordered because [`Connection::poll_transmit`] needs to
+164    /// deterministically select the next PathId to send on.
+165    // TODO(flub): well does it really? But deterministic is nice for now.
+166    paths: BTreeMap<PathId, PathState>,
+167    /// Incremented every time we see a new path
+168    ///
+169    /// Stored separately from `path.generation` to account for aborted migrations
+170    path_counter: u64,
+171    /// Whether MTU detection is supported in this environment
+172    allow_mtud: bool,
+173    state: State,
+174    side: ConnectionSide,
+175    /// Whether or not 0-RTT was enabled during the handshake. Does not imply acceptance.
+176    zero_rtt_enabled: bool,
+177    /// Set if 0-RTT is supported, then cleared when no longer needed.
+178    zero_rtt_crypto: Option<ZeroRttCrypto>,
+179    key_phase: bool,
+180    /// How many packets are in the current key phase. Used only for `Data` space.
+181    key_phase_size: u64,
+182    /// Transport parameters set by the peer
+183    peer_params: TransportParameters,
+184    /// Source ConnectionId of the first packet received from the peer
+185    orig_rem_cid: ConnectionId,
+186    /// Destination ConnectionId sent by the client on the first Initial
+187    initial_dst_cid: ConnectionId,
+188    /// The value that the server included in the Source Connection ID field of a Retry packet, if
+189    /// one was received
+190    retry_src_cid: Option<ConnectionId>,
+191    /// Events returned by [`Connection::poll`]
+192    events: VecDeque<Event>,
+193    endpoint_events: VecDeque<EndpointEventInner>,
+194    /// Whether the spin bit is in use for this connection
+195    spin_enabled: bool,
+196    /// Outgoing spin bit state
+197    spin: bool,
+198    /// Packet number spaces: initial, handshake, 1-RTT
+199    spaces: [PacketSpace; 3],
+200    /// Highest usable [`SpaceId`]
+201    highest_space: SpaceId,
+202    /// 1-RTT keys used prior to a key update
+203    prev_crypto: Option<PrevCrypto>,
+204    /// 1-RTT keys to be used for the next key update
+205    ///
+206    /// These are generated in advance to prevent timing attacks and/or DoS by third-party attackers
+207    /// spoofing key updates.
+208    next_crypto: Option<KeyPair<Box<dyn PacketKey>>>,
+209    accepted_0rtt: bool,
+210    /// Whether the idle timer should be reset the next time an ack-eliciting packet is transmitted.
+211    permit_idle_reset: bool,
+212    /// Negotiated idle timeout
+213    idle_timeout: Option<Duration>,
+214    timers: TimerTable,
+215    /// Number of packets received which could not be authenticated
+216    authentication_failures: u64,
+217
+218    //
+219    // Queued non-retransmittable 1-RTT data
+220    //
+221    /// If the CONNECTION_CLOSE frame needs to be sent
+222    close: bool,
+223
+224    //
+225    // ACK frequency
+226    //
+227    ack_frequency: AckFrequencyState,
+228
+229    //
+230    // Congestion Control
+231    //
+232    /// Whether the most recently received packet had an ECN codepoint set
+233    receiving_ecn: bool,
+234    /// Number of packets authenticated
+235    total_authed_packets: u64,
+236    /// Whether the last `poll_transmit` call yielded no data because there was
+237    /// no outgoing application data.
+238    app_limited: bool,
+239
+240    //
+241    // ObservedAddr
+242    //
+243    /// Sequence number for the next observed address frame sent to the peer.
+244    next_observed_addr_seq_no: VarInt,
+245
+246    streams: StreamsState,
+247    /// Surplus remote CIDs for future use on new paths
+248    ///
+249    /// These are given out before multiple paths exist, also for paths that will never
+250    /// exist.  So if multipath is supported the number of paths here will be higher than
+251    /// the actual number of paths in use.
+252    rem_cids: FxHashMap<PathId, CidQueue>,
+253    /// Attributes of CIDs generated by local endpoint
+254    ///
+255    /// Any path that is allowed to be opened is present in this map, as well as the already
+256    /// opened paths. However since CIDs are issued async by the endpoint driver via
+257    /// connection events it can not be used to know if CIDs have been issued for a path or
+258    /// not. See [`Connection::max_path_id_with_cids`] for this.
+259    local_cid_state: FxHashMap<PathId, CidState>,
+260    /// State of the unreliable datagram extension
+261    datagrams: DatagramState,
+262    /// Connection level statistics
+263    stats: ConnectionStats,
+264    /// Path level statistics
+265    path_stats: FxHashMap<PathId, PathStats>,
+266    /// QUIC version used for the connection.
+267    version: u32,
+268
+269    //
+270    // Multipath
+271    //
+272    /// Maximum number of concurrent paths
+273    ///
+274    /// Initially set from the [`TransportConfig::max_concurrent_multipath_paths`]. Even
+275    /// when multipath is disabled this will be set to 1, it is not used in that case
+276    /// though.
+277    max_concurrent_paths: NonZeroU32,
+278    /// Local maximum [`PathId`] to be used
+279    ///
+280    /// This is initially set to [`TransportConfig::get_initial_max_path_id`] when multipath
+281    /// is negotiated, or to [`PathId::ZERO`] otherwise. This is essentially the value of
+282    /// the highest MAX_PATH_ID frame sent.
+283    ///
+284    /// Any path with an ID equal or below this [`PathId`] is either:
+285    ///
+286    /// - Abandoned, if it is also in [`Connection::abandoned_paths`].
+287    /// - Open, in this case it is present in [`Connection::paths`]
+288    /// - Not yet opened, if it is in neither of these two places.
+289    ///
+290    /// Note that for not-yet-open there may or may not be any CIDs issued. See
+291    /// [`Connection::max_path_id_with_cids`].
+292    local_max_path_id: PathId,
+293    /// Remote's maximum [`PathId`] to be used
+294    ///
+295    /// This is initially set to the peer's [`TransportParameters::initial_max_path_id`] when
+296    /// multipath is negotiated, or to [`PathId::ZERO`] otherwise. A peer may increase this limit
+297    /// by sending [`Frame::MaxPathId`] frames.
+298    remote_max_path_id: PathId,
+299    /// The greatest [`PathId`] we have issued CIDs for
+300    ///
+301    /// CIDs are only issued for `min(local_max_path_id, remote_max_path_id)`. It is not
+302    /// possible to use [`Connection::local_cid_state`] to know if CIDs have been issued
+303    /// since they are issued asynchronously by the endpoint driver.
+304    max_path_id_with_cids: PathId,
+305    /// The paths already abandoned
+306    ///
+307    /// They may still have some state left in [`Connection::paths`] or
+308    /// [`Connection::local_cid_state`] since some of this has to be kept around for some
+309    /// time after a path is abandoned.
+310    // TODO(flub): Make this a more efficient data structure.  Like ranges of abandoned
+311    //    paths.  Or a set together with a minimum.  Or something.
+312    abandoned_paths: FxHashSet<PathId>,
+313
+314    iroh_hp: iroh_hp::State,
+315    qlog: QlogSink,
+316}
+317
+318impl Connection {
+319    pub(crate) fn new(
+320        endpoint_config: Arc<EndpointConfig>,
+321        config: Arc<TransportConfig>,
+322        init_cid: ConnectionId,
+323        loc_cid: ConnectionId,
+324        rem_cid: ConnectionId,
+325        network_path: FourTuple,
+326        crypto: Box<dyn crypto::Session>,
+327        cid_gen: &dyn ConnectionIdGenerator,
+328        now: Instant,
+329        version: u32,
+330        allow_mtud: bool,
+331        rng_seed: [u8; 32],
+332        side_args: SideArgs,
+333        qlog: QlogSink,
+334    ) -> Self {
+335        let pref_addr_cid = side_args.pref_addr_cid();
+336        let path_validated = side_args.path_validated();
+337        let connection_side = ConnectionSide::from(side_args);
+338        let side = connection_side.side();
+339        let mut rng = StdRng::from_seed(rng_seed);
+340        let initial_space = {
+341            let mut space = PacketSpace::new(now, SpaceId::Initial, &mut rng);
+342            space.crypto = Some(crypto.initial_keys(init_cid, side));
+343            space
+344        };
+345        let handshake_space = PacketSpace::new(now, SpaceId::Handshake, &mut rng);
+346        #[cfg(test)]
+347        let data_space = match config.deterministic_packet_numbers {
+348            true => PacketSpace::new_deterministic(now, SpaceId::Data),
+349            false => PacketSpace::new(now, SpaceId::Data, &mut rng),
+350        };
+351        #[cfg(not(test))]
+352        let data_space = PacketSpace::new(now, SpaceId::Data, &mut rng);
+353        let state = State::handshake(state::Handshake {
+354            rem_cid_set: side.is_server(),
+355            expected_token: Bytes::new(),
+356            client_hello: None,
+357            allow_server_migration: side.is_client(),
+358        });
+359        let local_cid_state = FxHashMap::from_iter([(
+360            PathId::ZERO,
+361            CidState::new(
+362                cid_gen.cid_len(),
+363                cid_gen.cid_lifetime(),
+364                now,
+365                if pref_addr_cid.is_some() { 2 } else { 1 },
+366            ),
+367        )]);
+368
+369        let mut path = PathData::new(network_path, allow_mtud, None, 0, now, &config);
+370        // TODO(@divma): consider if we want to delay this until the path is validated
+371        path.open = true;
+372        let mut this = Self {
+373            endpoint_config,
+374            crypto,
+375            handshake_cid: loc_cid,
+376            rem_handshake_cid: rem_cid,
+377            local_cid_state,
+378            paths: BTreeMap::from_iter([(
+379                PathId::ZERO,
+380                PathState {
+381                    data: path,
+382                    prev: None,
+383                },
+384            )]),
+385            path_counter: 0,
+386            allow_mtud,
+387            state,
+388            side: connection_side,
+389            zero_rtt_enabled: false,
+390            zero_rtt_crypto: None,
+391            key_phase: false,
+392            // A small initial key phase size ensures peers that don't handle key updates correctly
+393            // fail sooner rather than later. It's okay for both peers to do this, as the first one
+394            // to perform an update will reset the other's key phase size in `update_keys`, and a
+395            // simultaneous key update by both is just like a regular key update with a really fast
+396            // response. Inspired by quic-go's similar behavior of performing the first key update
+397            // at the 100th short-header packet.
+398            key_phase_size: rng.random_range(10..1000),
+399            peer_params: TransportParameters::default(),
+400            orig_rem_cid: rem_cid,
+401            initial_dst_cid: init_cid,
+402            retry_src_cid: None,
+403            events: VecDeque::new(),
+404            endpoint_events: VecDeque::new(),
+405            spin_enabled: config.allow_spin && rng.random_ratio(7, 8),
+406            spin: false,
+407            spaces: [initial_space, handshake_space, data_space],
+408            highest_space: SpaceId::Initial,
+409            prev_crypto: None,
+410            next_crypto: None,
+411            accepted_0rtt: false,
+412            permit_idle_reset: true,
+413            idle_timeout: match config.max_idle_timeout {
+414                None | Some(VarInt(0)) => None,
+415                Some(dur) => Some(Duration::from_millis(dur.0)),
+416            },
+417            timers: TimerTable::default(),
+418            authentication_failures: 0,
+419            close: false,
+420
+421            ack_frequency: AckFrequencyState::new(get_max_ack_delay(
+422                &TransportParameters::default(),
+423            )),
+424
+425            app_limited: false,
+426            receiving_ecn: false,
+427            total_authed_packets: 0,
+428
+429            next_observed_addr_seq_no: 0u32.into(),
+430
+431            streams: StreamsState::new(
+432                side,
+433                config.max_concurrent_uni_streams,
+434                config.max_concurrent_bidi_streams,
+435                config.send_window,
+436                config.receive_window,
+437                config.stream_receive_window,
+438            ),
+439            datagrams: DatagramState::default(),
+440            config,
+441            rem_cids: FxHashMap::from_iter([(PathId::ZERO, CidQueue::new(rem_cid))]),
+442            rng,
+443            stats: ConnectionStats::default(),
+444            path_stats: Default::default(),
+445            version,
+446
+447            // peer params are not yet known, so multipath is not enabled
+448            max_concurrent_paths: NonZeroU32::MIN,
+449            local_max_path_id: PathId::ZERO,
+450            remote_max_path_id: PathId::ZERO,
+451            max_path_id_with_cids: PathId::ZERO,
+452            abandoned_paths: Default::default(),
+453
+454            // iroh's nat traversal
+455            iroh_hp: Default::default(),
+456            qlog,
+457        };
+458        if path_validated {
+459            this.on_path_validated(PathId::ZERO);
+460        }
+461        if side.is_client() {
+462            // Kick off the connection
+463            this.write_crypto();
+464            this.init_0rtt(now);
+465        }
+466        this.qlog
+467            .emit_tuple_assigned(PathId::ZERO, network_path, now);
+468        this
+469    }
+470
+471    /// Returns the next time at which `handle_timeout` should be called
+472    ///
+473    /// The value returned may change after:
+474    /// - the application performed some I/O on the connection
+475    /// - a call was made to `handle_event`
+476    /// - a call to `poll_transmit` returned `Some`
+477    /// - a call was made to `handle_timeout`
+478    #[must_use]
+479    pub fn poll_timeout(&mut self) -> Option<Instant> {
+480        self.timers.peek()
+481    }
+482
+483    /// Returns application-facing events
+484    ///
+485    /// Connections should be polled for events after:
+486    /// - a call was made to `handle_event`
+487    /// - a call was made to `handle_timeout`
+488    #[must_use]
+489    pub fn poll(&mut self) -> Option<Event> {
+490        if let Some(x) = self.events.pop_front() {
+491            return Some(x);
+492        }
+493
+494        if let Some(event) = self.streams.poll() {
+495            return Some(Event::Stream(event));
+496        }
+497
+498        if let Some(reason) = self.state.take_error() {
+499            return Some(Event::ConnectionLost { reason });
+500        }
+501
+502        None
+503    }
+504
+505    /// Return endpoint-facing events
+506    #[must_use]
+507    pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent> {
+508        self.endpoint_events.pop_front().map(EndpointEvent)
+509    }
+510
+511    /// Provide control over streams
+512    #[must_use]
+513    pub fn streams(&mut self) -> Streams<'_> {
+514        Streams {
+515            state: &mut self.streams,
+516            conn_state: &self.state,
+517        }
+518    }
+519
+520    /// Provide control over streams
+521    #[must_use]
+522    pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_> {
+523        assert!(id.dir() == Dir::Bi || id.initiator() != self.side.side());
+524        RecvStream {
+525            id,
+526            state: &mut self.streams,
+527            pending: &mut self.spaces[SpaceId::Data].pending,
+528        }
+529    }
+530
+531    /// Provide control over streams
+532    #[must_use]
+533    pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_> {
+534        assert!(id.dir() == Dir::Bi || id.initiator() == self.side.side());
+535        SendStream {
+536            id,
+537            state: &mut self.streams,
+538            pending: &mut self.spaces[SpaceId::Data].pending,
+539            conn_state: &self.state,
+540        }
+541    }
+542
+543    /// Opens a new path only if no path on the same network path currently exists.
+544    ///
+545    /// This comparison will use [`FourTuple::is_probably_same_path`] on the given `network_path`
+546    /// and pass it existing path's network paths.
+547    ///
+548    /// This means that you can pass `local_ip: None` to make the comparison only compare
+549    /// remote addresses.
+550    ///
+551    /// This avoids having to guess which local interface will be used to communicate with the
+552    /// remote, should it not be known yet. We assume that if we already have a path to the remote,
+553    /// the OS is likely to use the same interface to talk to said remote.
+554    ///
+555    /// See also [`open_path`]. Returns `(path_id, true)` if the path already existed. `(path_id,
+556    /// false)` if was opened.
+557    ///
+558    /// [`open_path`]: Connection::open_path
+559    pub fn open_path_ensure(
+560        &mut self,
+561        network_path: FourTuple,
+562        initial_status: PathStatus,
+563        now: Instant,
+564    ) -> Result<(PathId, bool), PathError> {
+565        Ok(
+566            match self
+567                .paths
+568                .iter()
+569                .find(|(_id, path)| network_path.is_probably_same_path(&path.data.network_path))
+570            {
+571                Some((path_id, _state)) => (*path_id, true),
+572                None => (self.open_path(network_path, initial_status, now)?, false),
+573            },
+574        )
+575    }
+576
+577    /// Opens a new path
+578    ///
+579    /// Further errors might occur and they will be emitted in [`PathEvent::LocallyClosed`] events.
+580    /// When the path is opened it will be reported as an [`PathEvent::Opened`].
+581    pub fn open_path(
+582        &mut self,
+583        network_path: FourTuple,
+584        initial_status: PathStatus,
+585        now: Instant,
+586    ) -> Result<PathId, PathError> {
+587        if !self.is_multipath_negotiated() {
+588            return Err(PathError::MultipathNotNegotiated);
+589        }
+590        if self.side().is_server() {
+591            return Err(PathError::ServerSideNotAllowed);
+592        }
+593
+594        let max_abandoned = self.abandoned_paths.iter().max().copied();
+595        let max_used = self.paths.keys().last().copied();
+596        let path_id = max_abandoned
+597            .max(max_used)
+598            .unwrap_or(PathId::ZERO)
+599            .saturating_add(1u8);
+600
+601        if Some(path_id) > self.max_path_id() {
+602            return Err(PathError::MaxPathIdReached);
+603        }
+604        if path_id > self.remote_max_path_id {
+605            self.spaces[SpaceId::Data].pending.paths_blocked = true;
+606            return Err(PathError::MaxPathIdReached);
+607        }
+608        if self.rem_cids.get(&path_id).map(CidQueue::active).is_none() {
+609            self.spaces[SpaceId::Data]
+610                .pending
+611                .path_cids_blocked
+612                .push(path_id);
+613            return Err(PathError::RemoteCidsExhausted);
+614        }
+615
+616        let path = self.ensure_path(path_id, network_path, now, None);
+617        path.status.local_update(initial_status);
+618
+619        Ok(path_id)
+620    }
+621
+622    /// Closes a path by sending a PATH_ABANDON frame
+623    ///
+624    /// This will not allow closing the last path. It does allow closing paths which have
+625    /// not yet been opened, as e.g. is the case when receiving a PATH_ABANDON from the peer
+626    /// for a path that was never opened locally.
+627    pub fn close_path(
+628        &mut self,
+629        now: Instant,
+630        path_id: PathId,
+631        error_code: VarInt,
+632    ) -> Result<(), ClosePathError> {
+633        if self.abandoned_paths.contains(&path_id)
+634            || Some(path_id) > self.max_path_id()
+635            || !self.paths.contains_key(&path_id)
+636        {
+637            return Err(ClosePathError::ClosedPath);
+638        }
+639        if self
+640            .paths
+641            .iter()
+642            // Would there be any remaining, non-abandoned, validated paths
+643            .any(|(id, path)| {
+644                *id != path_id && !self.abandoned_paths.contains(id) && path.data.validated
+645            })
+646            .not()
+647        {
+648            return Err(ClosePathError::LastOpenPath);
+649        }
+650
+651        // Send PATH_ABANDON
+652        self.spaces[SpaceId::Data]
+653            .pending
+654            .path_abandon
+655            .insert(path_id, error_code.into());
+656
+657        // Remove pending NEW CIDs for this path
+658        let pending_space = &mut self.spaces[SpaceId::Data].pending;
+659        pending_space.new_cids.retain(|cid| cid.path_id != path_id);
+660        pending_space.path_cids_blocked.retain(|&id| id != path_id);
+661        pending_space.path_status.retain(|&id| id != path_id);
+662
+663        // Cleanup retransmits across ALL paths (CIDs for path_id may have been transmitted on other paths)
+664        for space in self.spaces[SpaceId::Data].iter_paths_mut() {
+665            for sent_packet in space.sent_packets.values_mut() {
+666                if let Some(retransmits) = sent_packet.retransmits.get_mut() {
+667                    retransmits.new_cids.retain(|cid| cid.path_id != path_id);
+668                    retransmits.path_cids_blocked.retain(|&id| id != path_id);
+669                    retransmits.path_status.retain(|&id| id != path_id);
+670                }
+671            }
+672        }
+673
+674        // Consider remotely issued CIDs as retired.
+675        // Technically we don't have to do this just yet.  We only need to do this *after*
+676        // the ABANDON_PATH frame is sent, allowing us to still send it on the
+677        // to-be-abandoned path.  However it is recommended to send it on another path, and
+678        // we do not allow abandoning the last path anyway.
+679        self.rem_cids.remove(&path_id);
+680        self.endpoint_events
+681            .push_back(EndpointEventInner::RetireResetToken(path_id));
+682
+683        let pto = self.pto_max_path(SpaceId::Data, false);
+684
+685        let path = self.paths.get_mut(&path_id).expect("checked above");
+686
+687        // We record the time after which receiving data on this path generates a transport error.
+688        path.data.last_allowed_receive = Some(now + 3 * pto);
+689        self.abandoned_paths.insert(path_id);
+690
+691        self.set_max_path_id(now, self.local_max_path_id.saturating_add(1u8));
+692
+693        // The peer MUST respond with a corresponding PATH_ABANDON frame.
+694        // If we receive packets on the abandoned path after 3 * PTO we trigger a transport error.
+695        // In any case, we completely discard the path after 6 * PTO whether we receive a
+696        // PATH_ABANDON or not.
+697        self.timers.set(
+698            Timer::PerPath(path_id, PathTimer::DiscardPath),
+699            now + 6 * pto,
+700            self.qlog.with_time(now),
+701        );
+702        Ok(())
+703    }
+704
+705    /// Gets the [`PathData`] for a known [`PathId`].
+706    ///
+707    /// Will panic if the path_id does not reference any known path.
+708    #[track_caller]
+709    fn path_data(&self, path_id: PathId) -> &PathData {
+710        if let Some(data) = self.paths.get(&path_id) {
+711            &data.data
+712        } else {
+713            panic!(
+714                "unknown path: {path_id}, currently known paths: {:?}",
+715                self.paths.keys().collect::<Vec<_>>()
+716            );
+717        }
+718    }
+719
+720    /// Gets a reference to the [`PathData`] for a [`PathId`]
+721    fn path(&self, path_id: PathId) -> Option<&PathData> {
+722        self.paths.get(&path_id).map(|path_state| &path_state.data)
+723    }
+724
+725    /// Gets a mutable reference to the [`PathData`] for a [`PathId`]
+726    fn path_mut(&mut self, path_id: PathId) -> Option<&mut PathData> {
+727        self.paths
+728            .get_mut(&path_id)
+729            .map(|path_state| &mut path_state.data)
+730    }
+731
+732    /// Returns all known paths.
+733    ///
+734    /// There is no guarantee any of these paths are open or usable.
+735    pub fn paths(&self) -> Vec<PathId> {
+736        self.paths.keys().copied().collect()
+737    }
+738
+739    /// Gets the local [`PathStatus`] for a known [`PathId`]
+740    pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath> {
+741        self.path(path_id)
+742            .map(PathData::local_status)
+743            .ok_or(ClosedPath { _private: () })
+744    }
+745
+746    /// Returns the path's network path represented as a 4-tuple.
+747    pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath> {
+748        self.path(path_id)
+749            .map(|path| path.network_path)
+750            .ok_or(ClosedPath { _private: () })
+751    }
+752
+753    /// Sets the [`PathStatus`] for a known [`PathId`]
+754    ///
+755    /// Returns the previous path status on success.
+756    pub fn set_path_status(
+757        &mut self,
+758        path_id: PathId,
+759        status: PathStatus,
+760    ) -> Result<PathStatus, SetPathStatusError> {
+761        if !self.is_multipath_negotiated() {
+762            return Err(SetPathStatusError::MultipathNotNegotiated);
+763        }
+764        let path = self
+765            .path_mut(path_id)
+766            .ok_or(SetPathStatusError::ClosedPath)?;
+767        let prev = match path.status.local_update(status) {
+768            Some(prev) => {
+769                self.spaces[SpaceId::Data]
+770                    .pending
+771                    .path_status
+772                    .insert(path_id);
+773                prev
+774            }
+775            None => path.local_status(),
+776        };
+777        Ok(prev)
+778    }
+779
+780    /// Returns the remote path status
+781    // TODO(flub): Probably should also be some kind of path event?  Not even sure if I like
+782    //    this as an API, but for now it allows me to write a test easily.
+783    // TODO(flub): Technically this should be a Result<Option<PathSTatus>>?
+784    pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus> {
+785        self.path(path_id).and_then(|path| path.remote_status())
+786    }
+787
+788    /// Sets the max_idle_timeout for a specific path
+789    ///
+790    /// See [`TransportConfig::default_path_max_idle_timeout`] for details.
+791    ///
+792    /// Returns the previous value of the setting.
+793    pub fn set_path_max_idle_timeout(
+794        &mut self,
+795        path_id: PathId,
+796        timeout: Option<Duration>,
+797    ) -> Result<Option<Duration>, ClosedPath> {
+798        let path = self
+799            .paths
+800            .get_mut(&path_id)
+801            .ok_or(ClosedPath { _private: () })?;
+802        Ok(std::mem::replace(&mut path.data.idle_timeout, timeout))
+803    }
+804
+805    /// Sets the keep_alive_interval for a specific path
+806    ///
+807    /// See [`TransportConfig::default_path_keep_alive_interval`] for details.
+808    ///
+809    /// Returns the previous value of the setting.
+810    pub fn set_path_keep_alive_interval(
+811        &mut self,
+812        path_id: PathId,
+813        interval: Option<Duration>,
+814    ) -> Result<Option<Duration>, ClosedPath> {
+815        let path = self
+816            .paths
+817            .get_mut(&path_id)
+818            .ok_or(ClosedPath { _private: () })?;
+819        Ok(std::mem::replace(&mut path.data.keep_alive, interval))
+820    }
+821
+822    /// Gets the [`PathData`] for a known [`PathId`].
+823    ///
+824    /// Will panic if the path_id does not reference any known path.
+825    #[track_caller]
+826    fn path_data_mut(&mut self, path_id: PathId) -> &mut PathData {
+827        &mut self.paths.get_mut(&path_id).expect("known path").data
+828    }
+829
+830    /// Find an open, validated path that's on the same network path as the given network path.
+831    ///
+832    /// Returns the first path matching, even if there's multiple.
+833    fn find_validated_path_on_network_path(
+834        &self,
+835        network_path: FourTuple,
+836    ) -> Option<(&PathId, &PathState)> {
+837        self.paths.iter().find(|(path_id, path_state)| {
+838            path_state.data.validated
+839                // Would this use the same network path, if network_path were used to send right now?
+840                && network_path.is_probably_same_path(&path_state.data.network_path)
+841                && !self.abandoned_paths.contains(path_id)
+842        })
+843        // TODO(@divma): we might want to ensure the path has been recently active to consider the
+844        // address validated
+845        // matheus23: Perhaps looking at !self.abandoned_paths.contains(path_id) is enough, given keep-alives?
+846    }
+847
+848    fn ensure_path(
+849        &mut self,
+850        path_id: PathId,
+851        network_path: FourTuple,
+852        now: Instant,
+853        pn: Option<u64>,
+854    ) -> &mut PathData {
+855        let valid_path = self.find_validated_path_on_network_path(network_path);
+856        let validated = valid_path.is_some();
+857        let initial_rtt = valid_path.map(|(_, path)| path.data.rtt.conservative());
+858        let vacant_entry = match self.paths.entry(path_id) {
+859            btree_map::Entry::Vacant(vacant_entry) => vacant_entry,
+860            btree_map::Entry::Occupied(occupied_entry) => {
+861                return &mut occupied_entry.into_mut().data;
+862            }
+863        };
+864
+865        debug!(%validated, %path_id, %network_path, "path added");
+866        let peer_max_udp_payload_size =
+867            u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX);
+868        self.path_counter = self.path_counter.wrapping_add(1);
+869        let mut data = PathData::new(
+870            network_path,
+871            self.allow_mtud,
+872            Some(peer_max_udp_payload_size),
+873            self.path_counter,
+874            now,
+875            &self.config,
+876        );
+877
+878        data.validated = validated;
+879        if let Some(initial_rtt) = initial_rtt {
+880            data.rtt.reset_initial_rtt(initial_rtt);
+881        }
+882
+883        let pto = self.ack_frequency.max_ack_delay_for_pto() + data.rtt.pto_base();
+884        self.timers.set(
+885            Timer::PerPath(path_id, PathTimer::PathOpen),
+886            now + 3 * pto,
+887            self.qlog.with_time(now),
+888        );
+889
+890        // for the path to be opened we need to send a packet on the path. Sending a challenge
+891        // guarantees this
+892        data.send_new_challenge = true;
+893
+894        let path = vacant_entry.insert(PathState { data, prev: None });
+895
+896        let mut pn_space = spaces::PacketNumberSpace::new(now, SpaceId::Data, &mut self.rng);
+897        if let Some(pn) = pn {
+898            pn_space.dedup.insert(pn);
+899        }
+900        self.spaces[SpaceId::Data]
+901            .number_spaces
+902            .insert(path_id, pn_space);
+903        self.qlog.emit_tuple_assigned(path_id, network_path, now);
+904        &mut path.data
+905    }
+906
+907    /// Returns packets to transmit
+908    ///
+909    /// Connections should be polled for transmit after:
+910    /// - the application performed some I/O on the connection
+911    /// - a call was made to `handle_event`
+912    /// - a call was made to `handle_timeout`
+913    ///
+914    /// `max_datagrams` specifies how many datagrams can be returned inside a
+915    /// single Transmit using GSO. This must be at least 1.
+916    #[must_use]
+917    pub fn poll_transmit(
+918        &mut self,
+919        now: Instant,
+920        max_datagrams: NonZeroUsize,
+921        buf: &mut Vec<u8>,
+922    ) -> Option<Transmit> {
+923        if let Some(probing) = self
+924            .iroh_hp
+925            .server_side_mut()
+926            .ok()
+927            .and_then(iroh_hp::ServerState::next_probe)
+928        {
+929            let destination = probing.remote();
+930            trace!(%destination, "RAND_DATA packet");
+931            let token: u64 = self.rng.random();
+932            buf.put_u64(token);
+933            probing.finish(token);
+934            return Some(Transmit {
+935                destination,
+936                ecn: None,
+937                size: 8,
+938                segment_size: None,
+939                src_ip: None,
+940            });
+941        }
+942
+943        let max_datagrams = match self.config.enable_segmentation_offload {
+944            false => NonZeroUsize::MIN,
+945            true => max_datagrams,
+946        };
+947
+948        // Each call to poll_transmit can only send datagrams to one destination, because
+949        // all datagrams in a GSO batch are for the same destination.  Therefore only
+950        // datagrams for one Path ID are produced for each poll_transmit call.
+951
+952        // TODO(flub): this is wishful thinking and not actually implemented, but perhaps it
+953        //   should be:
+954
+955        // First, if we have to send a close, select a path for that.
+956        // Next, all paths that have a PATH_CHALLENGE or PATH_RESPONSE pending.
+957
+958        // For all, open, validated and AVAILABLE paths:
+959        // - Is the path congestion blocked or pacing blocked?
+960        // - call maybe_queue_ to ensure a tail-loss probe would be sent?
+961        // - do we need to send a close message?
+962        // - call can_send
+963        // Once there's nothing more to send on the AVAILABLE paths, do the same for BACKUP paths
+964
+965        // Check whether we need to send a close message
+966        let close = match self.state.as_type() {
+967            StateType::Drained => {
+968                self.app_limited = true;
+969                return None;
+970            }
+971            StateType::Draining | StateType::Closed => {
+972                // self.close is only reset once the associated packet had been
+973                // encoded successfully
+974                if !self.close {
+975                    self.app_limited = true;
+976                    return None;
+977                }
+978                true
+979            }
+980            _ => false,
+981        };
+982
+983        // Check whether we need to send an ACK_FREQUENCY frame
+984        if let Some(config) = &self.config.ack_frequency_config {
+985            let rtt = self
+986                .paths
+987                .values()
+988                .map(|p| p.data.rtt.get())
+989                .min()
+990                .expect("one path exists");
+991            self.spaces[SpaceId::Data].pending.ack_frequency = self
+992                .ack_frequency
+993                .should_send_ack_frequency(rtt, config, &self.peer_params)
+994                && self.highest_space == SpaceId::Data
+995                && self.peer_supports_ack_frequency();
+996        }
+997
+998        // Whether this packet can be coalesced with another one in the same datagram.
+999        let mut coalesce = true;
+1000
+1001        // Whether the last packet in the datagram must be padded so the datagram takes up
+1002        // to at least MIN_INITIAL_SIZE, or to the maximum segment size if this is smaller.
+1003        let mut pad_datagram = PadDatagram::No;
+1004
+1005        // Whether congestion control stopped the next packet from being sent. Further
+1006        // packets could still be built, as e.g. tail-loss probes are not congestion
+1007        // limited.
+1008        let mut congestion_blocked = false;
+1009
+1010        // The packet number of the last built packet.
+1011        let mut last_packet_number = None;
+1012
+1013        let mut path_id = *self.paths.first_key_value().expect("one path must exist").0;
+1014
+1015        // If there is any open, validated and available path we only want to send frames to
+1016        // any backup path that must be sent on that backup path exclusively.
+1017        let have_available_path = self.paths.iter().any(|(id, path)| {
+1018            path.data.validated
+1019                && path.data.local_status() == PathStatus::Available
+1020                && self.rem_cids.contains_key(id)
+1021        });
+1022
+1023        // Setup for the first path_id
+1024        let mut transmit = TransmitBuf::new(
+1025            buf,
+1026            max_datagrams,
+1027            self.path_data(path_id).current_mtu().into(),
+1028        );
+1029        if let Some(challenge) = self.send_prev_path_challenge(now, &mut transmit, path_id) {
+1030            return Some(challenge);
+1031        }
+1032        let mut space_id = match path_id {
+1033            PathId::ZERO => SpaceId::Initial,
+1034            _ => SpaceId::Data,
+1035        };
+1036
+1037        loop {
+1038            // check if there is at least one active CID to use for sending
+1039            let Some(remote_cid) = self.rem_cids.get(&path_id).map(CidQueue::active) else {
+1040                let err = PathError::RemoteCidsExhausted;
+1041                if !self.abandoned_paths.contains(&path_id) {
+1042                    debug!(?err, %path_id, "no active CID for path");
+1043                    self.events.push_back(Event::Path(PathEvent::LocallyClosed {
+1044                        id: path_id,
+1045                        error: err,
+1046                    }));
+1047                    // Locally we should have refused to open this path, the remote should
+1048                    // have given us CIDs for this path before opening it.  So we can always
+1049                    // abandon this here.
+1050                    self.close_path(
+1051                        now,
+1052                        path_id,
+1053                        TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH.into(),
+1054                    )
+1055                    .ok();
+1056                    self.spaces[SpaceId::Data]
+1057                        .pending
+1058                        .path_cids_blocked
+1059                        .push(path_id);
+1060                } else {
+1061                    trace!(%path_id, "remote CIDs retired for abandoned path");
+1062                }
+1063
+1064                match self.paths.keys().find(|&&next| next > path_id) {
+1065                    Some(next_path_id) => {
+1066                        // See if this next path can send anything.
+1067                        path_id = *next_path_id;
+1068                        space_id = SpaceId::Data;
+1069
+1070                        // update per path state
+1071                        transmit.set_segment_size(self.path_data(path_id).current_mtu().into());
+1072                        if let Some(challenge) =
+1073                            self.send_prev_path_challenge(now, &mut transmit, path_id)
+1074                        {
+1075                            return Some(challenge);
+1076                        }
+1077
+1078                        continue;
+1079                    }
+1080                    None => {
+1081                        // Nothing more to send.
+1082                        trace!(
+1083                            ?space_id,
+1084                            %path_id,
+1085                            "no CIDs to send on path, no more paths"
+1086                        );
+1087                        break;
+1088                    }
+1089                }
+1090            };
+1091
+1092            // Determine if anything can be sent in this packet number space (SpaceId +
+1093            // PathId).
+1094            let max_packet_size = if transmit.datagram_remaining_mut() > 0 {
+1095                // We are trying to coalesce another packet into this datagram.
+1096                transmit.datagram_remaining_mut()
+1097            } else {
+1098                // A new datagram needs to be started.
+1099                transmit.segment_size()
+1100            };
+1101            let can_send = self.space_can_send(space_id, path_id, max_packet_size, close);
+1102            let path_should_send = {
+1103                let path_exclusive_only = space_id == SpaceId::Data
+1104                    && have_available_path
+1105                    && self.path_data(path_id).local_status() == PathStatus::Backup;
+1106                let path_should_send = if path_exclusive_only {
+1107                    can_send.path_exclusive
+1108                } else {
+1109                    !can_send.is_empty()
+1110                };
+1111                let needs_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0;
+1112                path_should_send || needs_loss_probe
+1113            };
+1114
+1115            if !path_should_send && space_id < SpaceId::Data {
+1116                if self.spaces[space_id].crypto.is_some() {
+1117                    trace!(?space_id, %path_id, "nothing to send in space");
+1118                }
+1119                space_id = space_id.next();
+1120                continue;
+1121            }
+1122
+1123            let send_blocked = if path_should_send && transmit.datagram_remaining_mut() == 0 {
+1124                // Only check congestion control if a new datagram is needed.
+1125                self.path_congestion_check(space_id, path_id, &transmit, &can_send, now)
+1126            } else {
+1127                PathBlocked::No
+1128            };
+1129            if send_blocked != PathBlocked::No {
+1130                trace!(?space_id, %path_id, ?send_blocked, "congestion blocked");
+1131                congestion_blocked = true;
+1132            }
+1133            if send_blocked != PathBlocked::No && space_id < SpaceId::Data {
+1134                // Higher spaces might still have tail-loss probes to send, which are not
+1135                // congestion blocked.
+1136                space_id = space_id.next();
+1137                continue;
+1138            }
+1139            if !path_should_send || send_blocked != PathBlocked::No {
+1140                // Nothing more to send on this path, check the next path if possible.
+1141
+1142                // If there are any datagrams in the transmit, packets for another path can
+1143                // not be built.
+1144                if transmit.num_datagrams() > 0 {
+1145                    break;
+1146                }
+1147
+1148                match self.paths.keys().find(|&&next| next > path_id) {
+1149                    Some(next_path_id) => {
+1150                        // See if this next path can send anything.
+1151                        trace!(
+1152                            ?space_id,
+1153                            %path_id,
+1154                            %next_path_id,
+1155                            "nothing to send on path"
+1156                        );
+1157                        path_id = *next_path_id;
+1158                        space_id = SpaceId::Data;
+1159
+1160                        // update per path state
+1161                        transmit.set_segment_size(self.path_data(path_id).current_mtu().into());
+1162                        if let Some(challenge) =
+1163                            self.send_prev_path_challenge(now, &mut transmit, path_id)
+1164                        {
+1165                            return Some(challenge);
+1166                        }
+1167
+1168                        continue;
+1169                    }
+1170                    None => {
+1171                        // Nothing more to send.
+1172                        trace!(
+1173                            ?space_id,
+1174                            %path_id,
+1175                            next_path_id=?None::<PathId>,
+1176                            "nothing to send on path"
+1177                        );
+1178                        break;
+1179                    }
+1180                }
+1181            }
+1182
+1183            // If the datagram is full, we need to start a new one.
+1184            if transmit.datagram_remaining_mut() == 0 {
+1185                if transmit.num_datagrams() >= transmit.max_datagrams().get() {
+1186                    // No more datagrams allowed
+1187                    break;
+1188                }
+1189
+1190                match self.spaces[space_id].for_path(path_id).loss_probes {
+1191                    0 => transmit.start_new_datagram(),
+1192                    _ => {
+1193                        // We need something to send for a tail-loss probe.
+1194                        let request_immediate_ack =
+1195                            space_id == SpaceId::Data && self.peer_supports_ack_frequency();
+1196                        self.spaces[space_id].maybe_queue_probe(
+1197                            path_id,
+1198                            request_immediate_ack,
+1199                            &self.streams,
+1200                        );
+1201
+1202                        self.spaces[space_id].for_path(path_id).loss_probes -= 1;
+1203
+1204                        // Clamp the datagram to at most the minimum MTU to ensure that loss
+1205                        // probes can get through and enable recovery even if the path MTU
+1206                        // has shrank unexpectedly.
+1207                        transmit.start_new_datagram_with_size(std::cmp::min(
+1208                            usize::from(INITIAL_MTU),
+1209                            transmit.segment_size(),
+1210                        ));
+1211                    }
+1212                }
+1213                trace!(count = transmit.num_datagrams(), "new datagram started");
+1214                coalesce = true;
+1215                pad_datagram = PadDatagram::No;
+1216            }
+1217
+1218            // If coalescing another packet into the existing datagram, there should
+1219            // still be enough space for a whole packet.
+1220            if transmit.datagram_start_offset() < transmit.len() {
+1221                debug_assert!(transmit.datagram_remaining_mut() >= MIN_PACKET_SPACE);
+1222            }
+1223
+1224            //
+1225            // From here on, we've determined that a packet will definitely be sent.
+1226            //
+1227
+1228            if self.spaces[SpaceId::Initial].crypto.is_some()
+1229                && space_id == SpaceId::Handshake
+1230                && self.side.is_client()
+1231            {
+1232                // A client stops both sending and processing Initial packets when it
+1233                // sends its first Handshake packet.
+1234                self.discard_space(now, SpaceId::Initial);
+1235            }
+1236            if let Some(ref mut prev) = self.prev_crypto {
+1237                prev.update_unacked = false;
+1238            }
+1239
+1240            let mut qlog = QlogSentPacket::default();
+1241            let mut builder = PacketBuilder::new(
+1242                now,
+1243                space_id,
+1244                path_id,
+1245                remote_cid,
+1246                &mut transmit,
+1247                can_send.other,
+1248                self,
+1249                &mut qlog,
+1250            )?;
+1251            last_packet_number = Some(builder.exact_number);
+1252            coalesce = coalesce && !builder.short_header;
+1253
+1254            if space_id == SpaceId::Initial && (self.side.is_client() || can_send.other) {
+1255                // https://www.rfc-editor.org/rfc/rfc9000.html#section-14.1
+1256                pad_datagram |= PadDatagram::ToMinMtu;
+1257            }
+1258            if space_id == SpaceId::Data && self.config.pad_to_mtu {
+1259                pad_datagram |= PadDatagram::ToSegmentSize;
+1260            }
+1261
+1262            if can_send.close {
+1263                trace!("sending CONNECTION_CLOSE");
+1264                // Encode ACKs before the ConnectionClose message, to give the receiver
+1265                // a better approximate on what data has been processed. This is
+1266                // especially important with ack delay, since the peer might not
+1267                // have gotten any other ACK for the data earlier on.
+1268                let mut sent_frames = SentFrames::default();
+1269                let is_multipath_negotiated = self.is_multipath_negotiated();
+1270                for path_id in self.spaces[space_id]
+1271                    .number_spaces
+1272                    .iter()
+1273                    .filter(|(_, pns)| !pns.pending_acks.ranges().is_empty())
+1274                    .map(|(&path_id, _)| path_id)
+1275                    .collect::<Vec<_>>()
+1276                {
+1277                    Self::populate_acks(
+1278                        now,
+1279                        self.receiving_ecn,
+1280                        &mut sent_frames,
+1281                        path_id,
+1282                        space_id,
+1283                        &mut self.spaces[space_id],
+1284                        is_multipath_negotiated,
+1285                        &mut builder.frame_space_mut(),
+1286                        &mut self.stats,
+1287                        &mut qlog,
+1288                    );
+1289                }
+1290
+1291                // Since there only 64 ACK frames there will always be enough space
+1292                // to encode the ConnectionClose frame too. However we still have the
+1293                // check here to prevent crashes if something changes.
+1294                debug_assert!(
+1295                    builder.frame_space_remaining() > frame::ConnectionClose::SIZE_BOUND,
+1296                    "ACKs should leave space for ConnectionClose"
+1297                );
+1298                if frame::ConnectionClose::SIZE_BOUND < builder.frame_space_remaining() {
+1299                    let max_frame_size = builder.frame_space_remaining();
+1300                    match self.state.as_type() {
+1301                        StateType::Closed => {
+1302                            let reason: Close =
+1303                                self.state.as_closed().expect("checked").clone().into();
+1304                            if space_id == SpaceId::Data || reason.is_transport_layer() {
+1305                                reason
+1306                                    .encoder(max_frame_size)
+1307                                    .encode(&mut builder.frame_space_mut());
+1308                                qlog.frame(&Frame::Close(reason));
+1309                            } else {
+1310                                let frame = frame::ConnectionClose {
+1311                                    error_code: TransportErrorCode::APPLICATION_ERROR,
+1312                                    frame_type: frame::MaybeFrame::None,
+1313                                    reason: Bytes::new(),
+1314                                };
+1315                                frame.encode(&mut builder.frame_space_mut(), max_frame_size);
+1316                                qlog.frame(&Frame::Close(frame::Close::Connection(frame)));
+1317                            }
+1318                        }
+1319                        StateType::Draining => {
+1320                            let frame = frame::ConnectionClose {
+1321                                error_code: TransportErrorCode::NO_ERROR,
+1322                                frame_type: frame::MaybeFrame::None,
+1323                                reason: Bytes::new(),
+1324                            };
+1325                            frame.encode(&mut builder.frame_space_mut(), max_frame_size);
+1326                            qlog.frame(&Frame::Close(frame::Close::Connection(frame)));
+1327                        }
+1328                        _ => unreachable!(
+1329                            "tried to make a close packet when the connection wasn't closed"
+1330                        ),
+1331                    };
+1332                }
+1333                builder.finish_and_track(now, self, path_id, sent_frames, pad_datagram, qlog);
+1334                if space_id == self.highest_space {
+1335                    // Don't send another close packet. Even with multipath we only send
+1336                    // CONNECTION_CLOSE on a single path since we expect our paths to work.
+1337                    self.close = false;
+1338                    // `CONNECTION_CLOSE` is the final packet
+1339                    break;
+1340                } else {
+1341                    // Send a close frame in every possible space for robustness, per
+1342                    // RFC9000 "Immediate Close during the Handshake". Don't bother trying
+1343                    // to send anything else.
+1344                    space_id = space_id.next();
+1345                    continue;
+1346                }
+1347            }
+1348
+1349            // Send an off-path PATH_RESPONSE. Prioritized over on-path data to ensure that
+1350            // path validation can occur while the link is saturated.
+1351            if space_id == SpaceId::Data && builder.buf.num_datagrams() == 1 {
+1352                let path = self.path_data_mut(path_id);
+1353                if let Some((token, network_path)) =
+1354                    path.path_responses.pop_off_path(path.network_path)
+1355                {
+1356                    // TODO(flub): We need to use the right CID!  We shouldn't use the same
+1357                    //    CID as the current active one for the path.  Though see also
+1358                    //    https://github.com/quinn-rs/quinn/issues/2184
+1359                    let response = frame::PathResponse(token);
+1360                    trace!(%response, "(off-path)");
+1361                    builder.frame_space_mut().write(response);
+1362                    qlog.frame(&Frame::PathResponse(response));
+1363                    self.stats.frame_tx.path_response += 1;
+1364                    builder.finish_and_track(
+1365                        now,
+1366                        self,
+1367                        path_id,
+1368                        SentFrames {
+1369                            non_retransmits: true,
+1370                            ..SentFrames::default()
+1371                        },
+1372                        PadDatagram::ToMinMtu,
+1373                        qlog,
+1374                    );
+1375                    self.stats.udp_tx.on_sent(1, transmit.len());
+1376                    return Some(Transmit {
+1377                        destination: network_path.remote,
+1378                        size: transmit.len(),
+1379                        ecn: None,
+1380                        segment_size: None,
+1381                        src_ip: network_path.local_ip,
+1382                    });
+1383                }
+1384            }
+1385
+1386            let sent_frames = {
+1387                let path_exclusive_only = have_available_path
+1388                    && self.path_data(path_id).local_status() == PathStatus::Backup;
+1389                let pn = builder.exact_number;
+1390                self.populate_packet(
+1391                    now,
+1392                    space_id,
+1393                    path_id,
+1394                    path_exclusive_only,
+1395                    &mut builder.frame_space_mut(),
+1396                    pn,
+1397                    &mut qlog,
+1398                )
+1399            };
+1400
+1401            // ACK-only packets should only be sent when explicitly allowed. If we write them due to
+1402            // any other reason, there is a bug which leads to one component announcing write
+1403            // readiness while not writing any data. This degrades performance. The condition is
+1404            // only checked if the full MTU is available and when potentially large fixed-size
+1405            // frames aren't queued, so that lack of space in the datagram isn't the reason for just
+1406            // writing ACKs.
+1407            debug_assert!(
+1408                !(sent_frames.is_ack_only(&self.streams)
+1409                    && !can_send.acks
+1410                    && can_send.other
+1411                    && builder.buf.segment_size()
+1412                        == self.path_data(path_id).current_mtu() as usize
+1413                    && self.datagrams.outgoing.is_empty()),
+1414                "SendableFrames was {can_send:?}, but only ACKs have been written"
+1415            );
+1416            if sent_frames.requires_padding {
+1417                pad_datagram |= PadDatagram::ToMinMtu;
+1418            }
+1419
+1420            for (path_id, _pn) in sent_frames.largest_acked.iter() {
+1421                self.spaces[space_id]
+1422                    .for_path(*path_id)
+1423                    .pending_acks
+1424                    .acks_sent();
+1425                self.timers.stop(
+1426                    Timer::PerPath(*path_id, PathTimer::MaxAckDelay),
+1427                    self.qlog.with_time(now),
+1428                );
+1429            }
+1430
+1431            // Now we need to finish the packet.  Before we do so we need to know if we will
+1432            // be coalescing the next packet into this one, or will be ending the datagram
+1433            // as well.  Because if this is the last packet in the datagram more padding
+1434            // might be needed because of the packet type, or to fill the GSO segment size.
+1435
+1436            // Are we allowed to coalesce AND is there enough space for another *packet* in
+1437            // this datagram AND is there another packet to send in this or the next space?
+1438            if coalesce
+1439                && builder
+1440                    .buf
+1441                    .datagram_remaining_mut()
+1442                    .saturating_sub(builder.predict_packet_end())
+1443                    > MIN_PACKET_SPACE
+1444                && self
+1445                    .next_send_space(space_id, path_id, builder.buf, close)
+1446                    .is_some()
+1447            {
+1448                // We can append/coalesce the next packet into the current
+1449                // datagram. Finish the current packet without adding extra padding.
+1450                builder.finish_and_track(now, self, path_id, sent_frames, PadDatagram::No, qlog);
+1451            } else {
+1452                // We need a new datagram for the next packet.  Finish the current
+1453                // packet with padding.
+1454                if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) {
+1455                    // If too many padding bytes would be required to continue the
+1456                    // GSO batch after this packet, end the GSO batch here. Ensures
+1457                    // that fixed-size frames with heterogeneous sizes
+1458                    // (e.g. application datagrams) won't inadvertently waste large
+1459                    // amounts of bandwidth. The exact threshold is a bit arbitrary
+1460                    // and might benefit from further tuning, though there's no
+1461                    // universally optimal value.
+1462                    const MAX_PADDING: usize = 32;
+1463                    if builder.buf.datagram_remaining_mut()
+1464                        > builder.predict_packet_end() + MAX_PADDING
+1465                    {
+1466                        trace!(
+1467                            "GSO truncated by demand for {} padding bytes",
+1468                            builder.buf.datagram_remaining_mut() - builder.predict_packet_end()
+1469                        );
+1470                        builder.finish_and_track(
+1471                            now,
+1472                            self,
+1473                            path_id,
+1474                            sent_frames,
+1475                            PadDatagram::No,
+1476                            qlog,
+1477                        );
+1478                        break;
+1479                    }
+1480
+1481                    // Pad the current datagram to GSO segment size so it can be
+1482                    // included in the GSO batch.
+1483                    builder.finish_and_track(
+1484                        now,
+1485                        self,
+1486                        path_id,
+1487                        sent_frames,
+1488                        PadDatagram::ToSegmentSize,
+1489                        qlog,
+1490                    );
+1491                } else {
+1492                    builder.finish_and_track(now, self, path_id, sent_frames, pad_datagram, qlog);
+1493                }
+1494                if transmit.num_datagrams() == 1 {
+1495                    transmit.clip_datagram_size();
+1496                }
+1497            }
+1498        }
+1499
+1500        if let Some(last_packet_number) = last_packet_number {
+1501            // Note that when sending in multiple packet spaces the last packet number will
+1502            // be the one from the highest packet space.
+1503            self.path_data_mut(path_id).congestion.on_sent(
+1504                now,
+1505                transmit.len() as u64,
+1506                last_packet_number,
+1507            );
+1508        }
+1509
+1510        self.qlog.emit_recovery_metrics(
+1511            path_id,
+1512            &mut self.paths.get_mut(&path_id).unwrap().data,
+1513            now,
+1514        );
+1515
+1516        self.app_limited = transmit.is_empty() && !congestion_blocked;
+1517
+1518        // Send MTU probe if necessary
+1519        if transmit.is_empty() && self.state.is_established() {
+1520            // MTU probing happens only in Data space.
+1521            let space_id = SpaceId::Data;
+1522            path_id = *self.paths.first_key_value().expect("one path must exist").0;
+1523            let probe_data = loop {
+1524                // We MTU probe all paths for which all of the following is true:
+1525                // - We have an active destination CID for the path.
+1526                // - The remote address *and* path are validated.
+1527                // - The path is not abandoned.
+1528                // - The MTU Discovery subsystem wants to probe the path.
+1529                let active_cid = self.rem_cids.get(&path_id).map(CidQueue::active);
+1530                let eligible = self.path_data(path_id).validated
+1531                    && !self.path_data(path_id).is_validating_path()
+1532                    && !self.abandoned_paths.contains(&path_id);
+1533                let probe_size = eligible
+1534                    .then(|| {
+1535                        let next_pn = self.spaces[space_id].for_path(path_id).peek_tx_number();
+1536                        self.path_data_mut(path_id).mtud.poll_transmit(now, next_pn)
+1537                    })
+1538                    .flatten();
+1539                match (active_cid, probe_size) {
+1540                    (Some(active_cid), Some(probe_size)) => {
+1541                        // Let's send an MTUD probe!
+1542                        break Some((active_cid, probe_size));
+1543                    }
+1544                    _ => {
+1545                        // Find the next path to check if it needs an MTUD probe.
+1546                        match self.paths.keys().find(|&&next| next > path_id) {
+1547                            Some(next) => {
+1548                                path_id = *next;
+1549                                continue;
+1550                            }
+1551                            None => break None,
+1552                        }
+1553                    }
+1554                }
+1555            };
+1556            if let Some((active_cid, probe_size)) = probe_data {
+1557                // We are definitely sending a DPLPMTUD probe.
+1558                debug_assert_eq!(transmit.num_datagrams(), 0);
+1559                transmit.start_new_datagram_with_size(probe_size as usize);
+1560
+1561                let mut qlog = QlogSentPacket::default();
+1562                let mut builder = PacketBuilder::new(
+1563                    now,
+1564                    space_id,
+1565                    path_id,
+1566                    active_cid,
+1567                    &mut transmit,
+1568                    true,
+1569                    self,
+1570                    &mut qlog,
+1571                )?;
+1572
+1573                // We implement MTU probes as ping packets padded up to the probe size
+1574                trace!(?probe_size, "writing MTUD probe");
+1575                trace!("PING");
+1576                builder.frame_space_mut().write(frame::FrameType::Ping);
+1577                qlog.frame(&Frame::Ping);
+1578                self.stats.frame_tx.ping += 1;
+1579
+1580                // If supported by the peer, we want no delays to the probe's ACK
+1581                if self.peer_supports_ack_frequency() {
+1582                    trace!("IMMEDIATE_ACK");
+1583                    builder
+1584                        .frame_space_mut()
+1585                        .write(frame::FrameType::ImmediateAck);
+1586                    self.stats.frame_tx.immediate_ack += 1;
+1587                    qlog.frame(&Frame::ImmediateAck);
+1588                }
+1589
+1590                let sent_frames = SentFrames {
+1591                    non_retransmits: true,
+1592                    ..Default::default()
+1593                };
+1594                builder.finish_and_track(
+1595                    now,
+1596                    self,
+1597                    path_id,
+1598                    sent_frames,
+1599                    PadDatagram::ToSize(probe_size),
+1600                    qlog,
+1601                );
+1602
+1603                self.path_stats
+1604                    .entry(path_id)
+1605                    .or_default()
+1606                    .sent_plpmtud_probes += 1;
+1607            }
+1608        }
+1609
+1610        if transmit.is_empty() {
+1611            return None;
+1612        }
+1613
+1614        let network_path = self.path_data(path_id).network_path;
+1615        trace!(
+1616            segment_size = transmit.segment_size(),
+1617            last_datagram_len = transmit.len() % transmit.segment_size(),
+1618            %network_path,
+1619            "sending {} bytes in {} datagrams",
+1620            transmit.len(),
+1621            transmit.num_datagrams()
+1622        );
+1623        self.path_data_mut(path_id)
+1624            .inc_total_sent(transmit.len() as u64);
+1625
+1626        self.stats
+1627            .udp_tx
+1628            .on_sent(transmit.num_datagrams() as u64, transmit.len());
+1629
+1630        Some(Transmit {
+1631            destination: network_path.remote,
+1632            size: transmit.len(),
+1633            ecn: if self.path_data(path_id).sending_ecn {
+1634                Some(EcnCodepoint::Ect0)
+1635            } else {
+1636                None
+1637            },
+1638            segment_size: match transmit.num_datagrams() {
+1639                1 => None,
+1640                _ => Some(transmit.segment_size()),
+1641            },
+1642            src_ip: network_path.local_ip,
+1643        })
+1644    }
+1645
+1646    /// Returns the [`SpaceId`] of the next packet space which has data to send
+1647    ///
+1648    /// This takes into account the space available to frames in the next datagram.
+1649    // TODO(flub): This duplication is not nice.
+1650    fn next_send_space(
+1651        &mut self,
+1652        current_space_id: SpaceId,
+1653        path_id: PathId,
+1654        buf: &TransmitBuf<'_>,
+1655        close: bool,
+1656    ) -> Option<SpaceId> {
+1657        // Number of bytes available for frames if this is a 1-RTT packet. We're guaranteed
+1658        // to be able to send an individual frame at least this large in the next 1-RTT
+1659        // packet. This could be generalized to support every space, but it's only needed to
+1660        // handle large fixed-size frames, which only exist in 1-RTT (application
+1661        // datagrams). We don't account for coalesced packets potentially occupying space
+1662        // because frames can always spill into the next datagram.
+1663        let mut space_id = current_space_id;
+1664        loop {
+1665            let can_send = self.space_can_send(space_id, path_id, buf.segment_size(), close);
+1666            if !can_send.is_empty() || (close && self.spaces[space_id].crypto.is_some()) {
+1667                return Some(space_id);
+1668            }
+1669            space_id = match space_id {
+1670                SpaceId::Initial => SpaceId::Handshake,
+1671                SpaceId::Handshake => SpaceId::Data,
+1672                SpaceId::Data => break,
+1673            }
+1674        }
+1675        None
+1676    }
+1677
+1678    /// Checks if creating a new datagram would be blocked by congestion control
+1679    fn path_congestion_check(
+1680        &mut self,
+1681        space_id: SpaceId,
+1682        path_id: PathId,
+1683        transmit: &TransmitBuf<'_>,
+1684        can_send: &SendableFrames,
+1685        now: Instant,
+1686    ) -> PathBlocked {
+1687        // Anti-amplification is only based on `total_sent`, which gets updated after
+1688        // the transmit is sent. Therefore we pass the amount of bytes for datagrams
+1689        // that are already created, as well as 1 byte for starting another datagram. If
+1690        // there is any anti-amplification budget left, we always allow a full MTU to be
+1691        // sent (see https://github.com/quinn-rs/quinn/issues/1082).
+1692        if self.side().is_server()
+1693            && self
+1694                .path_data(path_id)
+1695                .anti_amplification_blocked(transmit.len() as u64 + 1)
+1696        {
+1697            trace!(?space_id, %path_id, "blocked by anti-amplification");
+1698            return PathBlocked::AntiAmplification;
+1699        }
+1700
+1701        // Congestion control check.
+1702        // Tail loss probes must not be blocked by congestion, or a deadlock could arise.
+1703        let bytes_to_send = transmit.segment_size() as u64;
+1704        let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0;
+1705
+1706        if can_send.other && !need_loss_probe && !can_send.close {
+1707            let path = self.path_data(path_id);
+1708            if path.in_flight.bytes + bytes_to_send >= path.congestion.window() {
+1709                trace!(?space_id, %path_id, "blocked by congestion control");
+1710                return PathBlocked::Congestion;
+1711            }
+1712        }
+1713
+1714        // Pacing check.
+1715        if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) {
+1716            self.timers.set(
+1717                Timer::PerPath(path_id, PathTimer::Pacing),
+1718                delay,
+1719                self.qlog.with_time(now),
+1720            );
+1721            // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though
+1722            // they are not congestion controlled.
+1723            trace!(?space_id, %path_id, "blocked by pacing");
+1724            return PathBlocked::Pacing;
+1725        }
+1726
+1727        PathBlocked::No
+1728    }
+1729
+1730    /// Send PATH_CHALLENGE for a previous path if necessary
+1731    ///
+1732    /// QUIC-TRANSPORT section 9.3.3
+1733    /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding>
+1734    fn send_prev_path_challenge(
+1735        &mut self,
+1736        now: Instant,
+1737        buf: &mut TransmitBuf<'_>,
+1738        path_id: PathId,
+1739    ) -> Option<Transmit> {
+1740        let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?;
+1741        // TODO (matheus23): We could use !prev_path.is_validating() here instead to
+1742        // (possibly) also re-send challenges when they get lost.
+1743        if !prev_path.send_new_challenge {
+1744            return None;
+1745        };
+1746        prev_path.send_new_challenge = false;
+1747        let network_path = prev_path.network_path;
+1748        let token = self.rng.random();
+1749        let info = paths::SentChallengeInfo {
+1750            sent_instant: now,
+1751            network_path,
+1752        };
+1753        prev_path.challenges_sent.insert(token, info);
+1754        debug_assert_eq!(
+1755            self.highest_space,
+1756            SpaceId::Data,
+1757            "PATH_CHALLENGE queued without 1-RTT keys"
+1758        );
+1759        buf.start_new_datagram_with_size(MIN_INITIAL_SIZE as usize);
+1760
+1761        // Use the previous CID to avoid linking the new path with the previous path. We
+1762        // don't bother accounting for possible retirement of that prev_cid because this is
+1763        // sent once, immediately after migration, when the CID is known to be valid. Even
+1764        // if a post-migration packet caused the CID to be retired, it's fair to pretend
+1765        // this is sent first.
+1766        debug_assert_eq!(buf.datagram_start_offset(), 0);
+1767        let mut qlog = QlogSentPacket::default();
+1768        let mut builder = PacketBuilder::new(
+1769            now,
+1770            SpaceId::Data,
+1771            path_id,
+1772            *prev_cid,
+1773            buf,
+1774            false,
+1775            self,
+1776            &mut qlog,
+1777        )?;
+1778        let challenge = frame::PathChallenge(token);
+1779        trace!(%challenge, "validating previous path");
+1780        qlog.frame(&Frame::PathChallenge(challenge));
+1781        builder.frame_space_mut().write(challenge);
+1782        self.stats.frame_tx.path_challenge += 1;
+1783
+1784        // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame
+1785        // to at least the smallest allowed maximum datagram size of 1200 bytes,
+1786        // unless the anti-amplification limit for the path does not permit
+1787        // sending a datagram of this size
+1788        builder.pad_to(MIN_INITIAL_SIZE);
+1789
+1790        builder.finish(self, now, qlog);
+1791        self.stats.udp_tx.on_sent(1, buf.len());
+1792
+1793        Some(Transmit {
+1794            destination: network_path.remote,
+1795            size: buf.len(),
+1796            ecn: None,
+1797            segment_size: None,
+1798            src_ip: network_path.local_ip,
+1799        })
+1800    }
+1801
+1802    /// Indicate what types of frames are ready to send for the given space
+1803    ///
+1804    /// *packet_size* is the number of bytes available to build the next packet.  *close*
+1805    /// *indicates whether a CONNECTION_CLOSE frame needs to be sent.
+1806    fn space_can_send(
+1807        &mut self,
+1808        space_id: SpaceId,
+1809        path_id: PathId,
+1810        packet_size: usize,
+1811        close: bool,
+1812    ) -> SendableFrames {
+1813        let pn = self.spaces[SpaceId::Data]
+1814            .for_path(path_id)
+1815            .peek_tx_number();
+1816        let frame_space_1rtt = packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id));
+1817        if self.spaces[space_id].crypto.is_none()
+1818            && (space_id != SpaceId::Data
+1819                || self.zero_rtt_crypto.is_none()
+1820                || self.side.is_server())
+1821        {
+1822            // No keys available for this space
+1823            return SendableFrames::empty();
+1824        }
+1825        let mut can_send = self.spaces[space_id].can_send(path_id, &self.streams);
+1826        if space_id == SpaceId::Data {
+1827            can_send |= self.can_send_1rtt(path_id, frame_space_1rtt);
+1828        }
+1829
+1830        can_send.close = close && self.spaces[space_id].crypto.is_some();
+1831
+1832        can_send
+1833    }
+1834
+1835    /// Process `ConnectionEvent`s generated by the associated `Endpoint`
+1836    ///
+1837    /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals
+1838    /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be
+1839    /// extracted through the relevant methods.
+1840    pub fn handle_event(&mut self, event: ConnectionEvent) {
+1841        use ConnectionEventInner::*;
+1842        match event.0 {
+1843            Datagram(DatagramConnectionEvent {
+1844                now,
+1845                network_path,
+1846                path_id,
+1847                ecn,
+1848                first_decode,
+1849                remaining,
+1850            }) => {
+1851                let span = trace_span!("pkt", %path_id);
+1852                let _guard = span.enter();
+1853
+1854                if self.update_network_path_or_discard(network_path, path_id) {
+1855                    // A return value of true indicates we should discard this packet.
+1856                    return;
+1857                }
+1858
+1859                let was_anti_amplification_blocked = self
+1860                    .path(path_id)
+1861                    .map(|path| path.anti_amplification_blocked(1))
+1862                    .unwrap_or(true); // if we don't know about this path it's eagerly considered as unvalidated
+1863                // TODO(@divma): revisit this
+1864
+1865                self.stats.udp_rx.datagrams += 1;
+1866                self.stats.udp_rx.bytes += first_decode.len() as u64;
+1867                let data_len = first_decode.len();
+1868
+1869                self.handle_decode(now, network_path, path_id, ecn, first_decode);
+1870                // The current `path` might have changed inside `handle_decode` since the packet
+1871                // could have triggered a migration. The packet might also belong to an unknown
+1872                // path and have been rejected. Make sure the data received is accounted for the
+1873                // most recent path by accessing `path` after `handle_decode`.
+1874                if let Some(path) = self.path_mut(path_id) {
+1875                    path.inc_total_recvd(data_len as u64);
+1876                }
+1877
+1878                if let Some(data) = remaining {
+1879                    self.stats.udp_rx.bytes += data.len() as u64;
+1880                    self.handle_coalesced(now, network_path, path_id, ecn, data);
+1881                }
+1882
+1883                if let Some(path) = self.paths.get_mut(&path_id) {
+1884                    self.qlog
+1885                        .emit_recovery_metrics(path_id, &mut path.data, now);
+1886                }
+1887
+1888                if was_anti_amplification_blocked {
+1889                    // A prior attempt to set the loss detection timer may have failed due to
+1890                    // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if
+1891                    // the server's first flight is lost.
+1892                    self.set_loss_detection_timer(now, path_id);
+1893                }
+1894            }
+1895            NewIdentifiers(ids, now, cid_len, cid_lifetime) => {
+1896                let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default();
+1897                debug_assert!(ids.iter().all(|issued| issued.path_id == path_id));
+1898                let cid_state = self
+1899                    .local_cid_state
+1900                    .entry(path_id)
+1901                    .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0));
+1902                cid_state.new_cids(&ids, now);
+1903
+1904                ids.into_iter().rev().for_each(|frame| {
+1905                    self.spaces[SpaceId::Data].pending.new_cids.push(frame);
+1906                });
+1907                // Always update Timer::PushNewCid
+1908                self.reset_cid_retirement(now);
+1909            }
+1910        }
+1911    }
+1912
+1913    /// Updates the network path for `path_id`.
+1914    ///
+1915    /// Returns true if a packet coming in for this `path_id` over given `network_path` should be discarded.
+1916    /// Returns false if the path was updated and the packet doesn't need to be discarded.
+1917    fn update_network_path_or_discard(&mut self, network_path: FourTuple, path_id: PathId) -> bool {
+1918        let remote_may_migrate = self.side.remote_may_migrate(&self.state);
+1919        let local_ip_may_migrate = self.side.is_client();
+1920        // If this packet could initiate a migration and we're a client or a server that
+1921        // forbids migration, drop the datagram. This could be relaxed to heuristically
+1922        // permit NAT-rebinding-like migration.
+1923        if let Some(known_path) = self.path_mut(path_id) {
+1924            if network_path.remote != known_path.network_path.remote && !remote_may_migrate {
+1925                trace!(
+1926                    %path_id,
+1927                    %network_path,
+1928                    %known_path.network_path,
+1929                    "discarding packet from unrecognized peer"
+1930                );
+1931                return true;
+1932            }
+1933
+1934            if known_path.network_path.local_ip.is_some()
+1935                && network_path.local_ip.is_some()
+1936                && known_path.network_path.local_ip != network_path.local_ip
+1937                && !local_ip_may_migrate
+1938            {
+1939                trace!(
+1940                    %path_id,
+1941                    %network_path,
+1942                    %known_path.network_path,
+1943                    "discarding packet sent to incorrect interface"
+1944                );
+1945                return true;
+1946            }
+1947            // If the datagram indicates that we've changed our local IP, we update it.
+1948            // This is alluded to in Section 5.2 of the Multipath RFC draft 18:
+1949            // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#name-using-multiple-paths-on-the
+1950            // > Client receives the packet, recognizes a path migration, updates the source address of path 2 to 192.0.2.1.
+1951            if let Some(local_ip) = network_path.local_ip {
+1952                if known_path
+1953                    .network_path
+1954                    .local_ip
+1955                    .is_some_and(|ip| ip != local_ip)
+1956                {
+1957                    debug!(
+1958                        %path_id,
+1959                        %network_path,
+1960                        %known_path.network_path,
+1961                        "path's local address seemingly migrated"
+1962                    );
+1963                }
+1964                // We update the address without path validation on the client side.
+1965                // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#section-5.1
+1966                // > Servers observing a 4-tuple change will perform path validation (see Section 9 of [QUIC-TRANSPORT]).
+1967                // This sounds like it's *only* the server endpoints that do this.
+1968                // TODO(matheus23): We should still consider doing a proper migration on the client side in the future.
+1969                // For now, this preserves the behavior of this code pre 4-tuple tracking.
+1970                known_path.network_path.local_ip = Some(local_ip);
+1971            }
+1972        }
+1973        false
+1974    }
+1975
+1976    /// Process timer expirations
+1977    ///
+1978    /// Executes protocol logic, potentially preparing signals (including application `Event`s,
+1979    /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant
+1980    /// methods.
+1981    ///
+1982    /// It is most efficient to call this immediately after the system clock reaches the latest
+1983    /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply
+1984    /// no-op and therefore are safe.
+1985    pub fn handle_timeout(&mut self, now: Instant) {
+1986        while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) {
+1987            // TODO(@divma): remove `at` when the unicorn is born
+1988            trace!(?timer, at=?now, "timeout");
+1989            match timer {
+1990                Timer::Conn(timer) => match timer {
+1991                    ConnTimer::Close => {
+1992                        self.state.move_to_drained(None);
+1993                        self.endpoint_events.push_back(EndpointEventInner::Drained);
+1994                    }
+1995                    ConnTimer::Idle => {
+1996                        self.kill(ConnectionError::TimedOut);
+1997                    }
+1998                    ConnTimer::KeepAlive => {
+1999                        trace!("sending keep-alive");
+2000                        self.ping();
+2001                    }
+2002                    ConnTimer::KeyDiscard => {
+2003                        self.zero_rtt_crypto = None;
+2004                        self.prev_crypto = None;
+2005                    }
+2006                    ConnTimer::PushNewCid => {
+2007                        while let Some((path_id, when)) = self.next_cid_retirement() {
+2008                            if when > now {
+2009                                break;
+2010                            }
+2011                            match self.local_cid_state.get_mut(&path_id) {
+2012                                None => error!(%path_id, "No local CID state for path"),
+2013                                Some(cid_state) => {
+2014                                    // Update `retire_prior_to` field in NEW_CONNECTION_ID frame
+2015                                    let num_new_cid = cid_state.on_cid_timeout().into();
+2016                                    if !self.state.is_closed() {
+2017                                        trace!(
+2018                                            "push a new CID to peer RETIRE_PRIOR_TO field {}",
+2019                                            cid_state.retire_prior_to()
+2020                                        );
+2021                                        self.endpoint_events.push_back(
+2022                                            EndpointEventInner::NeedIdentifiers(
+2023                                                path_id,
+2024                                                now,
+2025                                                num_new_cid,
+2026                                            ),
+2027                                        );
+2028                                    }
+2029                                }
+2030                            }
+2031                        }
+2032                    }
+2033                },
+2034                // TODO: add path_id as span somehow
+2035                Timer::PerPath(path_id, timer) => {
+2036                    let span = trace_span!("per-path timer fired", %path_id, ?timer);
+2037                    let _guard = span.enter();
+2038                    match timer {
+2039                        PathTimer::PathIdle => {
+2040                            self.close_path(now, path_id, TransportErrorCode::NO_ERROR.into())
+2041                                .ok();
+2042                        }
+2043
+2044                        PathTimer::PathKeepAlive => {
+2045                            trace!("sending keep-alive on path");
+2046                            self.ping_path(path_id).ok();
+2047                        }
+2048                        PathTimer::LossDetection => {
+2049                            self.on_loss_detection_timeout(now, path_id);
+2050                            self.qlog.emit_recovery_metrics(
+2051                                path_id,
+2052                                &mut self.paths.get_mut(&path_id).unwrap().data,
+2053                                now,
+2054                            );
+2055                        }
+2056                        PathTimer::PathValidation => {
+2057                            let Some(path) = self.paths.get_mut(&path_id) else {
+2058                                continue;
+2059                            };
+2060                            self.timers.stop(
+2061                                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+2062                                self.qlog.with_time(now),
+2063                            );
+2064                            debug!("path validation failed");
+2065                            if let Some((_, prev)) = path.prev.take() {
+2066                                path.data = prev;
+2067                            }
+2068                            path.data.challenges_sent.clear();
+2069                            path.data.send_new_challenge = false;
+2070                        }
+2071                        PathTimer::PathChallengeLost => {
+2072                            let Some(path) = self.paths.get_mut(&path_id) else {
+2073                                continue;
+2074                            };
+2075                            trace!("path challenge deemed lost");
+2076                            path.data.send_new_challenge = true;
+2077                        }
+2078                        PathTimer::PathOpen => {
+2079                            let Some(path) = self.paths.get_mut(&path_id) else {
+2080                                continue;
+2081                            };
+2082                            path.data.challenges_sent.clear();
+2083                            path.data.send_new_challenge = false;
+2084                            self.timers.stop(
+2085                                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+2086                                self.qlog.with_time(now),
+2087                            );
+2088                            debug!("new path validation failed");
+2089                            if let Err(err) = self.close_path(
+2090                                now,
+2091                                path_id,
+2092                                TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(),
+2093                            ) {
+2094                                warn!(?err, "failed closing path");
+2095                            }
+2096
+2097                            self.events.push_back(Event::Path(PathEvent::LocallyClosed {
+2098                                id: path_id,
+2099                                error: PathError::ValidationFailed,
+2100                            }));
+2101                        }
+2102                        PathTimer::Pacing => trace!("pacing timer expired"),
+2103                        PathTimer::MaxAckDelay => {
+2104                            trace!("max ack delay reached");
+2105                            // This timer is only armed in the Data space
+2106                            self.spaces[SpaceId::Data]
+2107                                .for_path(path_id)
+2108                                .pending_acks
+2109                                .on_max_ack_delay_timeout()
+2110                        }
+2111                        PathTimer::DiscardPath => {
+2112                            // The path was abandoned and 3*PTO has expired since.  Clean up all
+2113                            // remaining state and install stateless reset token.
+2114                            self.timers.stop_per_path(path_id, self.qlog.with_time(now));
+2115                            if let Some(loc_cid_state) = self.local_cid_state.remove(&path_id) {
+2116                                let (min_seq, max_seq) = loc_cid_state.active_seq();
+2117                                for seq in min_seq..=max_seq {
+2118                                    self.endpoint_events.push_back(
+2119                                        EndpointEventInner::RetireConnectionId(
+2120                                            now, path_id, seq, false,
+2121                                        ),
+2122                                    );
+2123                                }
+2124                            }
+2125                            self.discard_path(path_id, now);
+2126                        }
+2127                    }
+2128                }
+2129            }
+2130        }
+2131    }
+2132
+2133    /// Close a connection immediately
+2134    ///
+2135    /// This does not ensure delivery of outstanding data. It is the application's responsibility to
+2136    /// call this only when all important communications have been completed, e.g. by calling
+2137    /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding
+2138    /// [`StreamEvent::Finished`] event.
+2139    ///
+2140    /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been
+2141    /// delivered. There may still be data from the peer that has not been received.
+2142    ///
+2143    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+2144    pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) {
+2145        self.close_inner(
+2146            now,
+2147            Close::Application(frame::ApplicationClose { error_code, reason }),
+2148        )
+2149    }
+2150
+2151    fn close_inner(&mut self, now: Instant, reason: Close) {
+2152        let was_closed = self.state.is_closed();
+2153        if !was_closed {
+2154            self.close_common();
+2155            self.set_close_timer(now);
+2156            self.close = true;
+2157            self.state.move_to_closed_local(reason);
+2158        }
+2159    }
+2160
+2161    /// Control datagrams
+2162    pub fn datagrams(&mut self) -> Datagrams<'_> {
+2163        Datagrams { conn: self }
+2164    }
+2165
+2166    /// Returns connection statistics
+2167    pub fn stats(&mut self) -> ConnectionStats {
+2168        self.stats.clone()
+2169    }
+2170
+2171    /// Returns path statistics
+2172    pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> {
+2173        let path = self.paths.get(&path_id)?;
+2174        let stats = self.path_stats.entry(path_id).or_default();
+2175        stats.rtt = path.data.rtt.get();
+2176        stats.cwnd = path.data.congestion.window();
+2177        stats.current_mtu = path.data.mtud.current_mtu();
+2178        Some(*stats)
+2179    }
+2180
+2181    /// Ping the remote endpoint
+2182    ///
+2183    /// Causes an ACK-eliciting packet to be transmitted on the connection.
+2184    pub fn ping(&mut self) {
+2185        // TODO(flub): This is very brute-force: it pings *all* the paths.  Instead it would
+2186        //    be nice if we could only send a single packet for this.
+2187        for path_data in self.spaces[self.highest_space].number_spaces.values_mut() {
+2188            path_data.ping_pending = true;
+2189        }
+2190    }
+2191
+2192    /// Ping the remote endpoint over a specific path
+2193    ///
+2194    /// Causes an ACK-eliciting packet to be transmitted on the path.
+2195    pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> {
+2196        let path_data = self.spaces[self.highest_space]
+2197            .number_spaces
+2198            .get_mut(&path)
+2199            .ok_or(ClosedPath { _private: () })?;
+2200        path_data.ping_pending = true;
+2201        Ok(())
+2202    }
+2203
+2204    /// Update traffic keys spontaneously
+2205    ///
+2206    /// This can be useful for testing key updates, as they otherwise only happen infrequently.
+2207    pub fn force_key_update(&mut self) {
+2208        if !self.state.is_established() {
+2209            debug!("ignoring forced key update in illegal state");
+2210            return;
+2211        }
+2212        if self.prev_crypto.is_some() {
+2213            // We already just updated, or are currently updating, the keys. Concurrent key updates
+2214            // are illegal.
+2215            debug!("ignoring redundant forced key update");
+2216            return;
+2217        }
+2218        self.update_keys(None, false);
+2219    }
+2220
+2221    // Compatibility wrapper for quinn < 0.11.7. Remove for 0.12.
+2222    #[doc(hidden)]
+2223    #[deprecated]
+2224    pub fn initiate_key_update(&mut self) {
+2225        self.force_key_update();
+2226    }
+2227
+2228    /// Get a session reference
+2229    pub fn crypto_session(&self) -> &dyn crypto::Session {
+2230        &*self.crypto
+2231    }
+2232
+2233    /// Whether the connection is in the process of being established
+2234    ///
+2235    /// If this returns `false`, the connection may be either established or closed, signaled by the
+2236    /// emission of a `Connected` or `ConnectionLost` message respectively.
+2237    pub fn is_handshaking(&self) -> bool {
+2238        self.state.is_handshake()
+2239    }
+2240
+2241    /// Whether the connection is closed
+2242    ///
+2243    /// Closed connections cannot transport any further data. A connection becomes closed when
+2244    /// either peer application intentionally closes it, or when either transport layer detects an
+2245    /// error such as a time-out or certificate validation failure.
+2246    ///
+2247    /// A `ConnectionLost` event is emitted with details when the connection becomes closed.
+2248    pub fn is_closed(&self) -> bool {
+2249        self.state.is_closed()
+2250    }
+2251
+2252    /// Whether there is no longer any need to keep the connection around
+2253    ///
+2254    /// Closed connections become drained after a brief timeout to absorb any remaining in-flight
+2255    /// packets from the peer. All drained connections have been closed.
+2256    pub fn is_drained(&self) -> bool {
+2257        self.state.is_drained()
+2258    }
+2259
+2260    /// For clients, if the peer accepted the 0-RTT data packets
+2261    ///
+2262    /// The value is meaningless until after the handshake completes.
+2263    pub fn accepted_0rtt(&self) -> bool {
+2264        self.accepted_0rtt
+2265    }
+2266
+2267    /// Whether 0-RTT is/was possible during the handshake
+2268    pub fn has_0rtt(&self) -> bool {
+2269        self.zero_rtt_enabled
+2270    }
+2271
+2272    /// Whether there are any pending retransmits
+2273    pub fn has_pending_retransmits(&self) -> bool {
+2274        !self.spaces[SpaceId::Data].pending.is_empty(&self.streams)
+2275    }
+2276
+2277    /// Look up whether we're the client or server of this Connection
+2278    pub fn side(&self) -> Side {
+2279        self.side.side()
+2280    }
+2281
+2282    /// Get the address observed by the remote over the given path
+2283    pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> {
+2284        self.path(path_id)
+2285            .map(|path_data| {
+2286                path_data
+2287                    .last_observed_addr_report
+2288                    .as_ref()
+2289                    .map(|observed| observed.socket_addr())
+2290            })
+2291            .ok_or(ClosedPath { _private: () })
+2292    }
+2293
+2294    /// Current best estimate of this connection's latency (round-trip-time)
+2295    pub fn rtt(&self, path_id: PathId) -> Option<Duration> {
+2296        self.path(path_id).map(|d| d.rtt.get())
+2297    }
+2298
+2299    /// Current state of this connection's congestion controller, for debugging purposes
+2300    pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> {
+2301        self.path(path_id).map(|d| d.congestion.as_ref())
+2302    }
+2303
+2304    /// Modify the number of remotely initiated streams that may be concurrently open
+2305    ///
+2306    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+2307    /// `count`s increase both minimum and worst-case memory consumption.
+2308    pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) {
+2309        self.streams.set_max_concurrent(dir, count);
+2310        // If the limit was reduced, then a flow control update previously deemed insignificant may
+2311        // now be significant.
+2312        let pending = &mut self.spaces[SpaceId::Data].pending;
+2313        self.streams.queue_max_stream_id(pending);
+2314    }
+2315
+2316    /// Modify the number of open paths allowed when multipath is enabled
+2317    ///
+2318    /// When reducing the number of concurrent paths this will only affect delaying sending
+2319    /// new MAX_PATH_ID frames until fewer than this number of paths are possible.  To
+2320    /// actively reduce paths they must be closed using [`Connection::close_path`], which
+2321    /// can also be used to close not-yet-opened paths.
+2322    ///
+2323    /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable
+2324    /// multipath and will fail.
+2325    pub fn set_max_concurrent_paths(
+2326        &mut self,
+2327        now: Instant,
+2328        count: NonZeroU32,
+2329    ) -> Result<(), MultipathNotNegotiated> {
+2330        if !self.is_multipath_negotiated() {
+2331            return Err(MultipathNotNegotiated { _private: () });
+2332        }
+2333        self.max_concurrent_paths = count;
+2334
+2335        let in_use_count = self
+2336            .local_max_path_id
+2337            .next()
+2338            .saturating_sub(self.abandoned_paths.len() as u32)
+2339            .as_u32();
+2340        let extra_needed = count.get().saturating_sub(in_use_count);
+2341        let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed);
+2342
+2343        self.set_max_path_id(now, new_max_path_id);
+2344
+2345        Ok(())
+2346    }
+2347
+2348    /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths
+2349    fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) {
+2350        if max_path_id <= self.local_max_path_id {
+2351            return;
+2352        }
+2353
+2354        self.local_max_path_id = max_path_id;
+2355        self.spaces[SpaceId::Data].pending.max_path_id = true;
+2356
+2357        self.issue_first_path_cids(now);
+2358    }
+2359
+2360    /// Current number of remotely initiated streams that may be concurrently open
+2361    ///
+2362    /// If the target for this limit is reduced using [`set_max_concurrent_streams`](Self::set_max_concurrent_streams),
+2363    /// it will not change immediately, even if fewer streams are open. Instead, it will
+2364    /// decrement by one for each time a remotely initiated stream of matching directionality is closed.
+2365    pub fn max_concurrent_streams(&self, dir: Dir) -> u64 {
+2366        self.streams.max_concurrent(dir)
+2367    }
+2368
+2369    /// See [`TransportConfig::send_window()`]
+2370    pub fn set_send_window(&mut self, send_window: u64) {
+2371        self.streams.set_send_window(send_window);
+2372    }
+2373
+2374    /// See [`TransportConfig::receive_window()`]
+2375    pub fn set_receive_window(&mut self, receive_window: VarInt) {
+2376        if self.streams.set_receive_window(receive_window) {
+2377            self.spaces[SpaceId::Data].pending.max_data = true;
+2378        }
+2379    }
+2380
+2381    /// Whether the Multipath for QUIC extension is enabled.
+2382    ///
+2383    /// Multipath is only enabled after the handshake is completed and if it was enabled by both
+2384    /// peers.
+2385    pub fn is_multipath_negotiated(&self) -> bool {
+2386        !self.is_handshaking()
+2387            && self.config.max_concurrent_multipath_paths.is_some()
+2388            && self.peer_params.initial_max_path_id.is_some()
+2389    }
+2390
+2391    fn on_ack_received(
+2392        &mut self,
+2393        now: Instant,
+2394        space: SpaceId,
+2395        ack: frame::Ack,
+2396    ) -> Result<(), TransportError> {
+2397        // All ACKs are referencing path 0
+2398        let path = PathId::ZERO;
+2399        self.inner_on_ack_received(now, space, path, ack)
+2400    }
+2401
+2402    fn on_path_ack_received(
+2403        &mut self,
+2404        now: Instant,
+2405        space: SpaceId,
+2406        path_ack: frame::PathAck,
+2407    ) -> Result<(), TransportError> {
+2408        let (ack, path) = path_ack.into_ack();
+2409        self.inner_on_ack_received(now, space, path, ack)
+2410    }
+2411
+2412    /// Handles an ACK frame acknowledging packets sent on *path*.
+2413    fn inner_on_ack_received(
+2414        &mut self,
+2415        now: Instant,
+2416        space: SpaceId,
+2417        path: PathId,
+2418        ack: frame::Ack,
+2419    ) -> Result<(), TransportError> {
+2420        if self.abandoned_paths.contains(&path) {
+2421            // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.4.3-3
+2422            // > PATH_ACK frames received with an abandoned path ID are silently ignored, as specified in Section 4.
+2423            trace!("silently ignoring PATH_ACK on abandoned path");
+2424            return Ok(());
+2425        }
+2426        if ack.largest >= self.spaces[space].for_path(path).next_packet_number {
+2427            return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked"));
+2428        }
+2429        let new_largest = {
+2430            let space = &mut self.spaces[space].for_path(path);
+2431            if space.largest_acked_packet.is_none_or(|pn| ack.largest > pn) {
+2432                space.largest_acked_packet = Some(ack.largest);
+2433                if let Some(info) = space.sent_packets.get(ack.largest) {
+2434                    // This should always succeed, but a misbehaving peer might ACK a packet we
+2435                    // haven't sent. At worst, that will result in us spuriously reducing the
+2436                    // congestion window.
+2437                    space.largest_acked_packet_sent = info.time_sent;
+2438                }
+2439                true
+2440            } else {
+2441                false
+2442            }
+2443        };
+2444
+2445        if self.detect_spurious_loss(&ack, space, path) {
+2446            self.path_data_mut(path)
+2447                .congestion
+2448                .on_spurious_congestion_event();
+2449        }
+2450
+2451        // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks.
+2452        let mut newly_acked = ArrayRangeSet::new();
+2453        for range in ack.iter() {
+2454            self.spaces[space].for_path(path).check_ack(range.clone())?;
+2455            for (pn, _) in self.spaces[space]
+2456                .for_path(path)
+2457                .sent_packets
+2458                .iter_range(range)
+2459            {
+2460                newly_acked.insert_one(pn);
+2461            }
+2462        }
+2463
+2464        if newly_acked.is_empty() {
+2465            return Ok(());
+2466        }
+2467
+2468        let mut ack_eliciting_acked = false;
+2469        for packet in newly_acked.elts() {
+2470            if let Some(info) = self.spaces[space].for_path(path).take(packet) {
+2471                for (acked_path_id, acked_pn) in info.largest_acked.iter() {
+2472                    // Assume ACKs for all packets below the largest acknowledged in
+2473                    // `packet` have been received. This can cause the peer to spuriously
+2474                    // retransmit if some of our earlier ACKs were lost, but allows for
+2475                    // simpler state tracking. See discussion at
+2476                    // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking
+2477                    if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) {
+2478                        pns.pending_acks.subtract_below(*acked_pn);
+2479                    }
+2480                }
+2481                ack_eliciting_acked |= info.ack_eliciting;
+2482
+2483                // Notify MTU discovery that a packet was acked, because it might be an MTU probe
+2484                let path_data = self.path_data_mut(path);
+2485                let mtu_updated = path_data.mtud.on_acked(space, packet, info.size);
+2486                if mtu_updated {
+2487                    path_data
+2488                        .congestion
+2489                        .on_mtu_update(path_data.mtud.current_mtu());
+2490                }
+2491
+2492                // Notify ack frequency that a packet was acked, because it might contain an ACK_FREQUENCY frame
+2493                self.ack_frequency.on_acked(path, packet);
+2494
+2495                self.on_packet_acked(now, path, info);
+2496            }
+2497        }
+2498
+2499        let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet;
+2500        let app_limited = self.app_limited;
+2501        let path_data = self.path_data_mut(path);
+2502        let in_flight = path_data.in_flight.bytes;
+2503
+2504        path_data
+2505            .congestion
+2506            .on_end_acks(now, in_flight, app_limited, largest_ackd);
+2507
+2508        if new_largest && ack_eliciting_acked {
+2509            let ack_delay = if space != SpaceId::Data {
+2510                Duration::from_micros(0)
+2511            } else {
+2512                cmp::min(
+2513                    self.ack_frequency.peer_max_ack_delay,
+2514                    Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0),
+2515                )
+2516            };
+2517            let rtt = now.saturating_duration_since(
+2518                self.spaces[space].for_path(path).largest_acked_packet_sent,
+2519            );
+2520
+2521            let next_pn = self.spaces[space].for_path(path).next_packet_number;
+2522            let path_data = self.path_data_mut(path);
+2523            // TODO(@divma): should be a method of path, should be contained in a single place
+2524            path_data.rtt.update(ack_delay, rtt);
+2525            if path_data.first_packet_after_rtt_sample.is_none() {
+2526                path_data.first_packet_after_rtt_sample = Some((space, next_pn));
+2527            }
+2528        }
+2529
+2530        // Must be called before crypto/pto_count are clobbered
+2531        self.detect_lost_packets(now, space, path, true);
+2532
+2533        if self.peer_completed_address_validation(path) {
+2534            self.path_data_mut(path).pto_count = 0;
+2535        }
+2536
+2537        // Explicit congestion notification
+2538        // TODO(@divma): this code is a good example of logic that should be contained in a single
+2539        // place but it's split between the path data and the packet number space data, we should
+2540        // find a way to make this work without two lookups
+2541        if self.path_data(path).sending_ecn {
+2542            if let Some(ecn) = ack.ecn {
+2543                // We only examine ECN counters from ACKs that we are certain we received in transmit
+2544                // order, allowing us to compute an increase in ECN counts to compare against the number
+2545                // of newly acked packets that remains well-defined in the presence of arbitrary packet
+2546                // reordering.
+2547                if new_largest {
+2548                    let sent = self.spaces[space].for_path(path).largest_acked_packet_sent;
+2549                    self.process_ecn(now, space, path, newly_acked.len() as u64, ecn, sent);
+2550                }
+2551            } else {
+2552                // We always start out sending ECN, so any ack that doesn't acknowledge it disables it.
+2553                debug!("ECN not acknowledged by peer");
+2554                self.path_data_mut(path).sending_ecn = false;
+2555            }
+2556        }
+2557
+2558        self.set_loss_detection_timer(now, path);
+2559        Ok(())
+2560    }
+2561
+2562    fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool {
+2563        let lost_packets = &mut self.spaces[space].for_path(path).lost_packets;
+2564
+2565        if lost_packets.is_empty() {
+2566            return false;
+2567        }
+2568
+2569        for range in ack.iter() {
+2570            let spurious_losses: Vec<u64> = lost_packets
+2571                .iter_range(range.clone())
+2572                .map(|(pn, _info)| pn)
+2573                .collect();
+2574
+2575            for pn in spurious_losses {
+2576                lost_packets.remove(pn);
+2577            }
+2578        }
+2579
+2580        // If this ACK frame acknowledged all deemed lost packets,
+2581        // then we have raised a spurious congestion event in the past.
+2582        // We cannot conclude when there are remaining packets,
+2583        // but future ACK frames might indicate a spurious loss detection.
+2584        lost_packets.is_empty()
+2585    }
+2586
+2587    /// Drain lost packets that we reasonably think will never arrive
+2588    ///
+2589    /// The current criterion is copied from `msquic`:
+2590    /// discard packets that were sent earlier than 2 probe timeouts ago.
+2591    fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) {
+2592        let two_pto = 2 * self.path_data(path).rtt.pto_base();
+2593
+2594        let lost_packets = &mut self.spaces[space].for_path(path).lost_packets;
+2595        lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto);
+2596    }
+2597
+2598    /// Process a new ECN block from an in-order ACK
+2599    fn process_ecn(
+2600        &mut self,
+2601        now: Instant,
+2602        space: SpaceId,
+2603        path: PathId,
+2604        newly_acked: u64,
+2605        ecn: frame::EcnCounts,
+2606        largest_sent_time: Instant,
+2607    ) {
+2608        match self.spaces[space]
+2609            .for_path(path)
+2610            .detect_ecn(newly_acked, ecn)
+2611        {
+2612            Err(e) => {
+2613                debug!("halting ECN due to verification failure: {}", e);
+2614
+2615                self.path_data_mut(path).sending_ecn = false;
+2616                // Wipe out the existing value because it might be garbage and could interfere with
+2617                // future attempts to use ECN on new paths.
+2618                self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO;
+2619            }
+2620            Ok(false) => {}
+2621            Ok(true) => {
+2622                self.path_stats.entry(path).or_default().congestion_events += 1;
+2623                self.path_data_mut(path).congestion.on_congestion_event(
+2624                    now,
+2625                    largest_sent_time,
+2626                    false,
+2627                    true,
+2628                    0,
+2629                );
+2630            }
+2631        }
+2632    }
+2633
+2634    // Not timing-aware, so it's safe to call this for inferred acks, such as arise from
+2635    // high-latency handshakes
+2636    fn on_packet_acked(&mut self, now: Instant, path_id: PathId, info: SentPacket) {
+2637        self.paths
+2638            .get_mut(&path_id)
+2639            .expect("known path")
+2640            .remove_in_flight(&info);
+2641        let app_limited = self.app_limited;
+2642        let path = self.path_data_mut(path_id);
+2643        if info.ack_eliciting && !path.is_validating_path() {
+2644            // Only pass ACKs to the congestion controller if we are not validating the current
+2645            // path, so as to ignore any ACKs from older paths still coming in.
+2646            let rtt = path.rtt;
+2647            path.congestion
+2648                .on_ack(now, info.time_sent, info.size.into(), app_limited, &rtt);
+2649        }
+2650
+2651        // Update state for confirmed delivery of frames
+2652        if let Some(retransmits) = info.retransmits.get() {
+2653            for (id, _) in retransmits.reset_stream.iter() {
+2654                self.streams.reset_acked(*id);
+2655            }
+2656        }
+2657
+2658        for frame in info.stream_frames {
+2659            self.streams.received_ack_of(frame);
+2660        }
+2661    }
+2662
+2663    fn set_key_discard_timer(&mut self, now: Instant, space: SpaceId) {
+2664        let start = if self.zero_rtt_crypto.is_some() {
+2665            now
+2666        } else {
+2667            self.prev_crypto
+2668                .as_ref()
+2669                .expect("no previous keys")
+2670                .end_packet
+2671                .as_ref()
+2672                .expect("update not acknowledged yet")
+2673                .1
+2674        };
+2675
+2676        // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO off all paths.
+2677        self.timers.set(
+2678            Timer::Conn(ConnTimer::KeyDiscard),
+2679            start + self.pto_max_path(space, false) * 3,
+2680            self.qlog.with_time(now),
+2681        );
+2682    }
+2683
+2684    /// Handle a [`PathTimer::LossDetection`] timeout.
+2685    ///
+2686    /// This timer expires for two reasons:
+2687    /// - An ACK-eliciting packet we sent should be considered lost.
+2688    /// - The PTO may have expired and a tail-loss probe needs to be scheduled.
+2689    ///
+2690    /// The former needs us to schedule re-transmission of the lost data.
+2691    ///
+2692    /// The latter means we have not received an ACK for an ack-eliciting packet we sent
+2693    /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting
+2694    /// packet, to try and elicit new acknowledgements. These new acknowledgements will
+2695    /// indicate whether the previously sent packets were lost or not.
+2696    fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) {
+2697        if let Some((_, pn_space)) = self.loss_time_and_space(path_id) {
+2698            // Time threshold loss Detection
+2699            self.detect_lost_packets(now, pn_space, path_id, false);
+2700            self.set_loss_detection_timer(now, path_id);
+2701            return;
+2702        }
+2703
+2704        let (_, space) = match self.pto_time_and_space(now, path_id) {
+2705            Some(x) => x,
+2706            None => {
+2707                error!(%path_id, "PTO expired while unset");
+2708                return;
+2709            }
+2710        };
+2711        trace!(
+2712            in_flight = self.path_data(path_id).in_flight.bytes,
+2713            count = self.path_data(path_id).pto_count,
+2714            ?space,
+2715            %path_id,
+2716            "PTO fired"
+2717        );
+2718
+2719        let count = match self.path_data(path_id).in_flight.ack_eliciting {
+2720            // A PTO when we're not expecting any ACKs must be due to handshake anti-amplification
+2721            // deadlock preventions
+2722            0 => {
+2723                debug_assert!(!self.peer_completed_address_validation(path_id));
+2724                1
+2725            }
+2726            // Conventional loss probe
+2727            _ => 2,
+2728        };
+2729        let pns = self.spaces[space].for_path(path_id);
+2730        pns.loss_probes = pns.loss_probes.saturating_add(count);
+2731        let path_data = self.path_data_mut(path_id);
+2732        path_data.pto_count = path_data.pto_count.saturating_add(1);
+2733        self.set_loss_detection_timer(now, path_id);
+2734    }
+2735
+2736    /// Detect any lost packets
+2737    ///
+2738    /// There are two cases in which we detects lost packets:
+2739    ///
+2740    /// - We received an ACK packet.
+2741    /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet
+2742    ///   that was followed by an acknowledged packet. The loss timer for this
+2743    ///   un-acknowledged packet expired and we need to detect that packet as lost.
+2744    ///
+2745    /// Packets are lost if they are both (See RFC9002 §6.1):
+2746    ///
+2747    /// - Unacknowledged, in flight and sent prior to an acknowledged packet.
+2748    /// - Old enough by either:
+2749    ///   - Having a packet number [`TransportConfig::packet_threshold`] lower then the last
+2750    ///     acknowledged packet.
+2751    ///   - Being sent [`TransportConfig::time_threshold`] * RTT in the past.
+2752    fn detect_lost_packets(
+2753        &mut self,
+2754        now: Instant,
+2755        pn_space: SpaceId,
+2756        path_id: PathId,
+2757        due_to_ack: bool,
+2758    ) {
+2759        let mut lost_packets = Vec::<u64>::new();
+2760        let mut lost_mtu_probe = None;
+2761        let mut in_persistent_congestion = false;
+2762        let mut size_of_lost_packets = 0u64;
+2763        self.spaces[pn_space].for_path(path_id).loss_time = None;
+2764
+2765        // Find all the lost packets, populating all variables initialised above.
+2766
+2767        let path = self.path_data(path_id);
+2768        let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe();
+2769        let loss_delay = path
+2770            .rtt
+2771            .conservative()
+2772            .mul_f32(self.config.time_threshold)
+2773            .max(TIMER_GRANULARITY);
+2774        let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample;
+2775
+2776        let largest_acked_packet = self.spaces[pn_space]
+2777            .for_path(path_id)
+2778            .largest_acked_packet
+2779            .expect("detect_lost_packets only to be called if path received at least one ACK");
+2780        let packet_threshold = self.config.packet_threshold as u64;
+2781
+2782        // InPersistentCongestion: Determine if all packets in the time period before the newest
+2783        // lost packet, including the edges, are marked lost. PTO computation must always
+2784        // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1).
+2785        let congestion_period = self
+2786            .pto(SpaceId::Data, path_id)
+2787            .saturating_mul(self.config.persistent_congestion_threshold);
+2788        let mut persistent_congestion_start: Option<Instant> = None;
+2789        let mut prev_packet = None;
+2790        let space = self.spaces[pn_space].for_path(path_id);
+2791
+2792        for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet) {
+2793            if prev_packet != Some(packet.wrapping_sub(1)) {
+2794                // An intervening packet was acknowledged
+2795                persistent_congestion_start = None;
+2796            }
+2797
+2798            // Packets sent before now - loss_delay are deemed lost.
+2799            // However, we avoid subtraction as it can panic and there's no
+2800            // saturating equivalent of this subtraction operation with a Duration.
+2801            let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay;
+2802            if packet_too_old || largest_acked_packet >= packet + packet_threshold {
+2803                // The packet should be declared lost.
+2804                if Some(packet) == in_flight_mtu_probe {
+2805                    // Lost MTU probes are not included in `lost_packets`, because they
+2806                    // should not trigger a congestion control response
+2807                    lost_mtu_probe = in_flight_mtu_probe;
+2808                } else {
+2809                    lost_packets.push(packet);
+2810                    size_of_lost_packets += info.size as u64;
+2811                    if info.ack_eliciting && due_to_ack {
+2812                        match persistent_congestion_start {
+2813                            // Two ACK-eliciting packets lost more than
+2814                            // congestion_period apart, with no ACKed packets in between
+2815                            Some(start) if info.time_sent - start > congestion_period => {
+2816                                in_persistent_congestion = true;
+2817                            }
+2818                            // Persistent congestion must start after the first RTT sample
+2819                            None if first_packet_after_rtt_sample
+2820                                .is_some_and(|x| x < (pn_space, packet)) =>
+2821                            {
+2822                                persistent_congestion_start = Some(info.time_sent);
+2823                            }
+2824                            _ => {}
+2825                        }
+2826                    }
+2827                }
+2828            } else {
+2829                // The packet should not yet be declared lost.
+2830                if space.loss_time.is_none() {
+2831                    // Since we iterate in order the lowest packet number's loss time will
+2832                    // always be the earliest.
+2833                    space.loss_time = Some(info.time_sent + loss_delay);
+2834                }
+2835                persistent_congestion_start = None;
+2836            }
+2837
+2838            prev_packet = Some(packet);
+2839        }
+2840
+2841        self.handle_lost_packets(
+2842            pn_space,
+2843            path_id,
+2844            now,
+2845            lost_packets,
+2846            lost_mtu_probe,
+2847            loss_delay,
+2848            in_persistent_congestion,
+2849            size_of_lost_packets,
+2850        );
+2851    }
+2852
+2853    /// Drops the path state, declaring any remaining in-flight packets as lost
+2854    fn discard_path(&mut self, path_id: PathId, now: Instant) {
+2855        trace!(%path_id, "dropping path state");
+2856        let path = self.path_data(path_id);
+2857        let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe();
+2858
+2859        let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes;
+2860        let lost_pns: Vec<_> = self.spaces[SpaceId::Data]
+2861            .for_path(path_id)
+2862            .sent_packets
+2863            .iter()
+2864            .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe)
+2865            .map(|(pn, info)| {
+2866                size_of_lost_packets += info.size as u64;
+2867                pn
+2868            })
+2869            .collect();
+2870
+2871        if !lost_pns.is_empty() {
+2872            trace!(
+2873                %path_id,
+2874                count = lost_pns.len(),
+2875                lost_bytes = size_of_lost_packets,
+2876                "packets lost on path abandon"
+2877            );
+2878            self.handle_lost_packets(
+2879                SpaceId::Data,
+2880                path_id,
+2881                now,
+2882                lost_pns,
+2883                in_flight_mtu_probe,
+2884                Duration::ZERO,
+2885                false,
+2886                size_of_lost_packets,
+2887            );
+2888        }
+2889        self.paths.remove(&path_id);
+2890        self.spaces[SpaceId::Data].number_spaces.remove(&path_id);
+2891
+2892        let path_stats = self.path_stats.remove(&path_id).unwrap_or_default();
+2893        self.events.push_back(
+2894            PathEvent::Abandoned {
+2895                id: path_id,
+2896                path_stats,
+2897            }
+2898            .into(),
+2899        );
+2900    }
+2901
+2902    fn handle_lost_packets(
+2903        &mut self,
+2904        pn_space: SpaceId,
+2905        path_id: PathId,
+2906        now: Instant,
+2907        lost_packets: Vec<u64>,
+2908        lost_mtu_probe: Option<u64>,
+2909        loss_delay: Duration,
+2910        in_persistent_congestion: bool,
+2911        size_of_lost_packets: u64,
+2912    ) {
+2913        debug_assert!(
+2914            {
+2915                let mut sorted = lost_packets.clone();
+2916                sorted.sort();
+2917                sorted == lost_packets
+2918            },
+2919            "lost_packets must be sorted"
+2920        );
+2921
+2922        self.drain_lost_packets(now, pn_space, path_id);
+2923
+2924        // OnPacketsLost
+2925        if let Some(largest_lost) = lost_packets.last().cloned() {
+2926            let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes;
+2927            let largest_lost_sent = self.spaces[pn_space]
+2928                .for_path(path_id)
+2929                .sent_packets
+2930                .get(largest_lost)
+2931                .unwrap()
+2932                .time_sent;
+2933            let path_stats = self.path_stats.entry(path_id).or_default();
+2934            path_stats.lost_packets += lost_packets.len() as u64;
+2935            path_stats.lost_bytes += size_of_lost_packets;
+2936            trace!(
+2937                %path_id,
+2938                count = lost_packets.len(),
+2939                lost_bytes = size_of_lost_packets,
+2940                "packets lost",
+2941            );
+2942
+2943            for &packet in &lost_packets {
+2944                let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else {
+2945                    continue;
+2946                };
+2947                self.qlog
+2948                    .emit_packet_lost(packet, &info, loss_delay, pn_space, now);
+2949                self.paths
+2950                    .get_mut(&path_id)
+2951                    .unwrap()
+2952                    .remove_in_flight(&info);
+2953
+2954                for frame in info.stream_frames {
+2955                    self.streams.retransmit(frame);
+2956                }
+2957                self.spaces[pn_space].pending |= info.retransmits;
+2958                self.path_data_mut(path_id)
+2959                    .mtud
+2960                    .on_non_probe_lost(packet, info.size);
+2961
+2962                self.spaces[pn_space].for_path(path_id).lost_packets.insert(
+2963                    packet,
+2964                    LostPacket {
+2965                        time_sent: info.time_sent,
+2966                    },
+2967                );
+2968            }
+2969
+2970            let path = self.path_data_mut(path_id);
+2971            if path.mtud.black_hole_detected(now) {
+2972                path.congestion.on_mtu_update(path.mtud.current_mtu());
+2973                if let Some(max_datagram_size) = self.datagrams().max_size() {
+2974                    self.datagrams.drop_oversized(max_datagram_size);
+2975                }
+2976                self.path_stats
+2977                    .entry(path_id)
+2978                    .or_default()
+2979                    .black_holes_detected += 1;
+2980            }
+2981
+2982            // Don't apply congestion penalty for lost ack-only packets
+2983            let lost_ack_eliciting =
+2984                old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes;
+2985
+2986            if lost_ack_eliciting {
+2987                self.path_stats
+2988                    .entry(path_id)
+2989                    .or_default()
+2990                    .congestion_events += 1;
+2991                self.path_data_mut(path_id).congestion.on_congestion_event(
+2992                    now,
+2993                    largest_lost_sent,
+2994                    in_persistent_congestion,
+2995                    false,
+2996                    size_of_lost_packets,
+2997                );
+2998            }
+2999        }
+3000
+3001        // Handle a lost MTU probe
+3002        if let Some(packet) = lost_mtu_probe {
+3003            let info = self.spaces[SpaceId::Data]
+3004                .for_path(path_id)
+3005                .take(packet)
+3006                .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and
+3007            // therefore must not have been removed yet
+3008            self.paths
+3009                .get_mut(&path_id)
+3010                .unwrap()
+3011                .remove_in_flight(&info);
+3012            self.path_data_mut(path_id).mtud.on_probe_lost();
+3013            self.path_stats
+3014                .entry(path_id)
+3015                .or_default()
+3016                .lost_plpmtud_probes += 1;
+3017        }
+3018    }
+3019
+3020    /// Returns the earliest time packets should be declared lost for all spaces on a path.
+3021    ///
+3022    /// If a path has an acknowledged packet with any prior un-acknowledged packets, the
+3023    /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed.
+3024    /// The time returned is when this packet should be declared lost.
+3025    fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> {
+3026        SpaceId::iter()
+3027            .filter_map(|id| {
+3028                self.spaces[id]
+3029                    .number_spaces
+3030                    .get(&path_id)
+3031                    .and_then(|pns| pns.loss_time)
+3032                    .map(|time| (time, id))
+3033            })
+3034            .min_by_key(|&(time, _)| time)
+3035    }
+3036
+3037    /// Returns the earliest next PTO should fire for all spaces on a path.
+3038    fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> {
+3039        let path = self.path(path_id)?;
+3040        let pto_count = path.pto_count;
+3041        let backoff = 2u32.pow(pto_count.min(MAX_BACKOFF_EXPONENT));
+3042        let mut duration = path.rtt.pto_base() * backoff;
+3043
+3044        if path_id == PathId::ZERO
+3045            && path.in_flight.ack_eliciting == 0
+3046            && !self.peer_completed_address_validation(PathId::ZERO)
+3047        {
+3048            // Address Validation during Connection Establishment:
+3049            // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a
+3050            // deadlock if an Initial or Handshake packet from the server is lost and the
+3051            // server can not send more due to its anti-amplification limit the client must
+3052            // send another packet on PTO.
+3053            let space = match self.highest_space {
+3054                SpaceId::Handshake => SpaceId::Handshake,
+3055                _ => SpaceId::Initial,
+3056            };
+3057
+3058            return Some((now + duration, space));
+3059        }
+3060
+3061        let mut result = None;
+3062        for space in SpaceId::iter() {
+3063            let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else {
+3064                continue;
+3065            };
+3066
+3067            if !pns.has_in_flight() {
+3068                continue;
+3069            }
+3070            if space == SpaceId::Data {
+3071                // Skip ApplicationData until handshake completes.
+3072                if self.is_handshaking() {
+3073                    return result;
+3074                }
+3075                // Include max_ack_delay and backoff for ApplicationData.
+3076                duration += self.ack_frequency.max_ack_delay_for_pto() * backoff;
+3077            }
+3078            let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else {
+3079                continue;
+3080            };
+3081            let pto = last_ack_eliciting + duration;
+3082            if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) {
+3083                if path.anti_amplification_blocked(1) {
+3084                    // Nothing would be able to be sent.
+3085                    continue;
+3086                }
+3087                if path.in_flight.ack_eliciting == 0 {
+3088                    // Nothing ack-eliciting, no PTO to arm/fire.
+3089                    continue;
+3090                }
+3091                result = Some((pto, space));
+3092            }
+3093        }
+3094        result
+3095    }
+3096
+3097    fn peer_completed_address_validation(&self, path: PathId) -> bool {
+3098        // TODO(flub): This logic needs updating for multipath
+3099        if self.side.is_server() || self.state.is_closed() {
+3100            return true;
+3101        }
+3102        // The server is guaranteed to have validated our address if any of our handshake or 1-RTT
+3103        // packets are acknowledged or we've seen HANDSHAKE_DONE and discarded handshake keys.
+3104        self.spaces[SpaceId::Handshake]
+3105            .path_space(PathId::ZERO)
+3106            .and_then(|pns| pns.largest_acked_packet)
+3107            .is_some()
+3108            || self.spaces[SpaceId::Data]
+3109                .path_space(path)
+3110                .and_then(|pns| pns.largest_acked_packet)
+3111                .is_some()
+3112            || (self.spaces[SpaceId::Data].crypto.is_some()
+3113                && self.spaces[SpaceId::Handshake].crypto.is_none())
+3114    }
+3115
+3116    /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed
+3117    ///
+3118    /// The timer must fire if either:
+3119    /// - An ack-eliciting packet we sent needs to be declared lost.
+3120    /// - A tail-loss probe needs to be sent.
+3121    ///
+3122    /// See [`Connection::on_loss_detection_timeout`] for details.
+3123    fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) {
+3124        if self.state.is_closed() {
+3125            // No loss detection takes place on closed connections, and `close_common` already
+3126            // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a
+3127            // reordered packet being handled by state-insensitive code.
+3128            return;
+3129        }
+3130
+3131        if let Some((loss_time, _)) = self.loss_time_and_space(path_id) {
+3132            // Time threshold loss detection.
+3133            self.timers.set(
+3134                Timer::PerPath(path_id, PathTimer::LossDetection),
+3135                loss_time,
+3136                self.qlog.with_time(now),
+3137            );
+3138            return;
+3139        }
+3140
+3141        // Determine which PN space to arm PTO for.
+3142        // Calculate PTO duration
+3143        if let Some((timeout, _)) = self.pto_time_and_space(now, path_id) {
+3144            self.timers.set(
+3145                Timer::PerPath(path_id, PathTimer::LossDetection),
+3146                timeout,
+3147                self.qlog.with_time(now),
+3148            );
+3149        } else {
+3150            self.timers.stop(
+3151                Timer::PerPath(path_id, PathTimer::LossDetection),
+3152                self.qlog.with_time(now),
+3153            );
+3154        }
+3155    }
+3156
+3157    /// The maximum probe timeout across all paths
+3158    ///
+3159    /// If `is_closing` is set to `true` it will filter out paths that have not yet been used.
+3160    ///
+3161    /// See [`Connection::pto`]
+3162    fn pto_max_path(&self, space: SpaceId, is_closing: bool) -> Duration {
+3163        match space {
+3164            SpaceId::Initial | SpaceId::Handshake => self.pto(space, PathId::ZERO),
+3165            SpaceId::Data => self
+3166                .paths
+3167                .iter()
+3168                .filter_map(|(path_id, state)| {
+3169                    if is_closing && state.data.total_sent == 0 && state.data.total_recvd == 0 {
+3170                        // If we are closing and haven't sent anything yet, do not include
+3171                        None
+3172                    } else {
+3173                        let pto = self.pto(space, *path_id);
+3174                        Some(pto)
+3175                    }
+3176                })
+3177                .max()
+3178                .expect("there should be one at least path"),
+3179        }
+3180    }
+3181
+3182    /// Probe Timeout
+3183    ///
+3184    /// The PTO is logically the time in which you'd expect to receive an acknowledgement
+3185    /// for a packet. So approximately RTT + max_ack_delay.
+3186    fn pto(&self, space: SpaceId, path_id: PathId) -> Duration {
+3187        let max_ack_delay = match space {
+3188            SpaceId::Initial | SpaceId::Handshake => Duration::ZERO,
+3189            SpaceId::Data => self.ack_frequency.max_ack_delay_for_pto(),
+3190        };
+3191        self.path_data(path_id).rtt.pto_base() + max_ack_delay
+3192    }
+3193
+3194    fn on_packet_authenticated(
+3195        &mut self,
+3196        now: Instant,
+3197        space_id: SpaceId,
+3198        path_id: PathId,
+3199        ecn: Option<EcnCodepoint>,
+3200        packet: Option<u64>,
+3201        spin: bool,
+3202        is_1rtt: bool,
+3203    ) {
+3204        self.total_authed_packets += 1;
+3205        if let Some(last_allowed_receive) = self
+3206            .paths
+3207            .get(&path_id)
+3208            .and_then(|path| path.data.last_allowed_receive)
+3209        {
+3210            if now > last_allowed_receive {
+3211                warn!("received data on path which we abandoned more than 3 * PTO ago");
+3212                // The peer failed to respond with a PATH_ABANDON in time.
+3213                if !self.state.is_closed() {
+3214                    // TODO(flub): What should the error code be?
+3215                    self.state.move_to_closed(TransportError::NO_ERROR(
+3216                        "peer failed to respond with PATH_ABANDON in time",
+3217                    ));
+3218                    self.close_common();
+3219                    self.set_close_timer(now);
+3220                    self.close = true;
+3221                }
+3222                return;
+3223            }
+3224        }
+3225
+3226        self.reset_keep_alive(path_id, now);
+3227        self.reset_idle_timeout(now, space_id, path_id);
+3228        self.permit_idle_reset = true;
+3229        self.receiving_ecn |= ecn.is_some();
+3230        if let Some(x) = ecn {
+3231            let space = &mut self.spaces[space_id];
+3232            space.for_path(path_id).ecn_counters += x;
+3233
+3234            if x.is_ce() {
+3235                space
+3236                    .for_path(path_id)
+3237                    .pending_acks
+3238                    .set_immediate_ack_required();
+3239            }
+3240        }
+3241
+3242        let packet = match packet {
+3243            Some(x) => x,
+3244            None => return,
+3245        };
+3246        match &self.side {
+3247            ConnectionSide::Client { .. } => {
+3248                // If we received a handshake packet that authenticated, then we're talking to
+3249                // the real server.  From now on we should no longer allow the server to migrate
+3250                // its address.
+3251                if space_id == SpaceId::Handshake {
+3252                    if let Some(hs) = self.state.as_handshake_mut() {
+3253                        hs.allow_server_migration = false;
+3254                    }
+3255                }
+3256            }
+3257            ConnectionSide::Server { .. } => {
+3258                if self.spaces[SpaceId::Initial].crypto.is_some() && space_id == SpaceId::Handshake
+3259                {
+3260                    // A server stops sending and processing Initial packets when it receives its first Handshake packet.
+3261                    self.discard_space(now, SpaceId::Initial);
+3262                }
+3263                if self.zero_rtt_crypto.is_some() && is_1rtt {
+3264                    // Discard 0-RTT keys soon after receiving a 1-RTT packet
+3265                    self.set_key_discard_timer(now, space_id)
+3266                }
+3267            }
+3268        }
+3269        let space = self.spaces[space_id].for_path(path_id);
+3270        space.pending_acks.insert_one(packet, now);
+3271        if packet >= space.rx_packet.unwrap_or_default() {
+3272            space.rx_packet = Some(packet);
+3273            // Update outgoing spin bit, inverting iff we're the client
+3274            self.spin = self.side.is_client() ^ spin;
+3275        }
+3276    }
+3277
+3278    /// Resets the idle timeout timers
+3279    ///
+3280    /// Without multipath there is only the connection-wide idle timeout. When multipath is
+3281    /// enabled there is an additional per-path idle timeout.
+3282    fn reset_idle_timeout(&mut self, now: Instant, space: SpaceId, path_id: PathId) {
+3283        // First reset the global idle timeout.
+3284        if let Some(timeout) = self.idle_timeout {
+3285            if self.state.is_closed() {
+3286                self.timers
+3287                    .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now));
+3288            } else {
+3289                let dt = cmp::max(timeout, 3 * self.pto_max_path(space, false));
+3290                self.timers.set(
+3291                    Timer::Conn(ConnTimer::Idle),
+3292                    now + dt,
+3293                    self.qlog.with_time(now),
+3294                );
+3295            }
+3296        }
+3297
+3298        // Now handle the per-path state
+3299        if let Some(timeout) = self.path_data(path_id).idle_timeout {
+3300            if self.state.is_closed() {
+3301                self.timers.stop(
+3302                    Timer::PerPath(path_id, PathTimer::PathIdle),
+3303                    self.qlog.with_time(now),
+3304                );
+3305            } else {
+3306                let dt = cmp::max(timeout, 3 * self.pto(space, path_id));
+3307                self.timers.set(
+3308                    Timer::PerPath(path_id, PathTimer::PathIdle),
+3309                    now + dt,
+3310                    self.qlog.with_time(now),
+3311                );
+3312            }
+3313        }
+3314    }
+3315
+3316    /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers
+3317    fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) {
+3318        if !self.state.is_established() {
+3319            return;
+3320        }
+3321
+3322        if let Some(interval) = self.config.keep_alive_interval {
+3323            self.timers.set(
+3324                Timer::Conn(ConnTimer::KeepAlive),
+3325                now + interval,
+3326                self.qlog.with_time(now),
+3327            );
+3328        }
+3329
+3330        if let Some(interval) = self.path_data(path_id).keep_alive {
+3331            self.timers.set(
+3332                Timer::PerPath(path_id, PathTimer::PathKeepAlive),
+3333                now + interval,
+3334                self.qlog.with_time(now),
+3335            );
+3336        }
+3337    }
+3338
+3339    /// Sets the timer for when a previously issued CID should be retired next
+3340    fn reset_cid_retirement(&mut self, now: Instant) {
+3341        if let Some((_path, t)) = self.next_cid_retirement() {
+3342            self.timers.set(
+3343                Timer::Conn(ConnTimer::PushNewCid),
+3344                t,
+3345                self.qlog.with_time(now),
+3346            );
+3347        }
+3348    }
+3349
+3350    /// The next time when a previously issued CID should be retired
+3351    fn next_cid_retirement(&self) -> Option<(PathId, Instant)> {
+3352        self.local_cid_state
+3353            .iter()
+3354            .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t)))
+3355            .min_by_key(|(_path_id, timeout)| *timeout)
+3356    }
+3357
+3358    /// Handle the already-decrypted first packet from the client
+3359    ///
+3360    /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more
+3361    /// efficient.
+3362    pub(crate) fn handle_first_packet(
+3363        &mut self,
+3364        now: Instant,
+3365        network_path: FourTuple,
+3366        ecn: Option<EcnCodepoint>,
+3367        packet_number: u64,
+3368        packet: InitialPacket,
+3369        remaining: Option<BytesMut>,
+3370    ) -> Result<(), ConnectionError> {
+3371        let span = trace_span!("first recv");
+3372        let _guard = span.enter();
+3373        debug_assert!(self.side.is_server());
+3374        let len = packet.header_data.len() + packet.payload.len();
+3375        let path_id = PathId::ZERO;
+3376        self.path_data_mut(path_id).total_recvd = len as u64;
+3377
+3378        if let Some(hs) = self.state.as_handshake_mut() {
+3379            hs.expected_token = packet.header.token.clone();
+3380        } else {
+3381            unreachable!("first packet must be delivered in Handshake state");
+3382        }
+3383
+3384        // The first packet is always on PathId::ZERO
+3385        self.on_packet_authenticated(
+3386            now,
+3387            SpaceId::Initial,
+3388            path_id,
+3389            ecn,
+3390            Some(packet_number),
+3391            false,
+3392            false,
+3393        );
+3394
+3395        let packet: Packet = packet.into();
+3396
+3397        let mut qlog = QlogRecvPacket::new(len);
+3398        qlog.header(&packet.header, Some(packet_number), path_id);
+3399
+3400        self.process_decrypted_packet(
+3401            now,
+3402            network_path,
+3403            path_id,
+3404            Some(packet_number),
+3405            packet,
+3406            &mut qlog,
+3407        )?;
+3408        self.qlog.emit_packet_received(qlog, now);
+3409        if let Some(data) = remaining {
+3410            self.handle_coalesced(now, network_path, path_id, ecn, data);
+3411        }
+3412
+3413        self.qlog.emit_recovery_metrics(
+3414            path_id,
+3415            &mut self.paths.get_mut(&path_id).unwrap().data,
+3416            now,
+3417        );
+3418
+3419        Ok(())
+3420    }
+3421
+3422    fn init_0rtt(&mut self, now: Instant) {
+3423        let (header, packet) = match self.crypto.early_crypto() {
+3424            Some(x) => x,
+3425            None => return,
+3426        };
+3427        if self.side.is_client() {
+3428            match self.crypto.transport_parameters() {
+3429                Ok(params) => {
+3430                    let params = params
+3431                        .expect("crypto layer didn't supply transport parameters with ticket");
+3432                    // Certain values must not be cached
+3433                    let params = TransportParameters {
+3434                        initial_src_cid: None,
+3435                        original_dst_cid: None,
+3436                        preferred_address: None,
+3437                        retry_src_cid: None,
+3438                        stateless_reset_token: None,
+3439                        min_ack_delay: None,
+3440                        ack_delay_exponent: TransportParameters::default().ack_delay_exponent,
+3441                        max_ack_delay: TransportParameters::default().max_ack_delay,
+3442                        initial_max_path_id: None,
+3443                        ..params
+3444                    };
+3445                    self.set_peer_params(params);
+3446                    self.qlog.emit_peer_transport_params_restored(self, now);
+3447                }
+3448                Err(e) => {
+3449                    error!("session ticket has malformed transport parameters: {}", e);
+3450                    return;
+3451                }
+3452            }
+3453        }
+3454        trace!("0-RTT enabled");
+3455        self.zero_rtt_enabled = true;
+3456        self.zero_rtt_crypto = Some(ZeroRttCrypto { header, packet });
+3457    }
+3458
+3459    fn read_crypto(
+3460        &mut self,
+3461        space: SpaceId,
+3462        crypto: &frame::Crypto,
+3463        payload_len: usize,
+3464    ) -> Result<(), TransportError> {
+3465        let expected = if !self.state.is_handshake() {
+3466            SpaceId::Data
+3467        } else if self.highest_space == SpaceId::Initial {
+3468            SpaceId::Initial
+3469        } else {
+3470            // On the server, self.highest_space can be Data after receiving the client's first
+3471            // flight, but we expect Handshake CRYPTO until the handshake is complete.
+3472            SpaceId::Handshake
+3473        };
+3474        // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT
+3475        // packets are illegal, and we don't process 1-RTT packets until the handshake is
+3476        // complete. Therefore, we will never see CRYPTO data from a later-than-expected space.
+3477        debug_assert!(space <= expected, "received out-of-order CRYPTO data");
+3478
+3479        let end = crypto.offset + crypto.data.len() as u64;
+3480        if space < expected && end > self.spaces[space].crypto_stream.bytes_read() {
+3481            warn!(
+3482                "received new {:?} CRYPTO data when expecting {:?}",
+3483                space, expected
+3484            );
+3485            return Err(TransportError::PROTOCOL_VIOLATION(
+3486                "new data at unexpected encryption level",
+3487            ));
+3488        }
+3489
+3490        let space = &mut self.spaces[space];
+3491        let max = end.saturating_sub(space.crypto_stream.bytes_read());
+3492        if max > self.config.crypto_buffer_size as u64 {
+3493            return Err(TransportError::CRYPTO_BUFFER_EXCEEDED(""));
+3494        }
+3495
+3496        space
+3497            .crypto_stream
+3498            .insert(crypto.offset, crypto.data.clone(), payload_len);
+3499        while let Some(chunk) = space.crypto_stream.read(usize::MAX, true) {
+3500            trace!("consumed {} CRYPTO bytes", chunk.bytes.len());
+3501            if self.crypto.read_handshake(&chunk.bytes)? {
+3502                self.events.push_back(Event::HandshakeDataReady);
+3503            }
+3504        }
+3505
+3506        Ok(())
+3507    }
+3508
+3509    fn write_crypto(&mut self) {
+3510        loop {
+3511            let space = self.highest_space;
+3512            let mut outgoing = Vec::new();
+3513            if let Some(crypto) = self.crypto.write_handshake(&mut outgoing) {
+3514                match space {
+3515                    SpaceId::Initial => {
+3516                        self.upgrade_crypto(SpaceId::Handshake, crypto);
+3517                    }
+3518                    SpaceId::Handshake => {
+3519                        self.upgrade_crypto(SpaceId::Data, crypto);
+3520                    }
+3521                    _ => unreachable!("got updated secrets during 1-RTT"),
+3522                }
+3523            }
+3524            if outgoing.is_empty() {
+3525                if space == self.highest_space {
+3526                    break;
+3527                } else {
+3528                    // Keys updated, check for more data to send
+3529                    continue;
+3530                }
+3531            }
+3532            let offset = self.spaces[space].crypto_offset;
+3533            let outgoing = Bytes::from(outgoing);
+3534            if let Some(hs) = self.state.as_handshake_mut() {
+3535                if space == SpaceId::Initial && offset == 0 && self.side.is_client() {
+3536                    hs.client_hello = Some(outgoing.clone());
+3537                }
+3538            }
+3539            self.spaces[space].crypto_offset += outgoing.len() as u64;
+3540            trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space);
+3541            self.spaces[space].pending.crypto.push_back(frame::Crypto {
+3542                offset,
+3543                data: outgoing,
+3544            });
+3545        }
+3546    }
+3547
+3548    /// Switch to stronger cryptography during handshake
+3549    fn upgrade_crypto(&mut self, space: SpaceId, crypto: Keys) {
+3550        debug_assert!(
+3551            self.spaces[space].crypto.is_none(),
+3552            "already reached packet space {space:?}"
+3553        );
+3554        trace!("{:?} keys ready", space);
+3555        if space == SpaceId::Data {
+3556            // Precompute the first key update
+3557            self.next_crypto = Some(
+3558                self.crypto
+3559                    .next_1rtt_keys()
+3560                    .expect("handshake should be complete"),
+3561            );
+3562        }
+3563
+3564        self.spaces[space].crypto = Some(crypto);
+3565        debug_assert!(space as usize > self.highest_space as usize);
+3566        self.highest_space = space;
+3567        if space == SpaceId::Data && self.side.is_client() {
+3568            // Discard 0-RTT keys because 1-RTT keys are available.
+3569            self.zero_rtt_crypto = None;
+3570        }
+3571    }
+3572
+3573    fn discard_space(&mut self, now: Instant, space_id: SpaceId) {
+3574        debug_assert!(space_id != SpaceId::Data);
+3575        trace!("discarding {:?} keys", space_id);
+3576        if space_id == SpaceId::Initial {
+3577            // No longer needed
+3578            if let ConnectionSide::Client { token, .. } = &mut self.side {
+3579                *token = Bytes::new();
+3580            }
+3581        }
+3582        let space = &mut self.spaces[space_id];
+3583        space.crypto = None;
+3584        let pns = space.for_path(PathId::ZERO);
+3585        pns.time_of_last_ack_eliciting_packet = None;
+3586        pns.loss_time = None;
+3587        pns.loss_probes = 0;
+3588        let sent_packets = mem::take(&mut pns.sent_packets);
+3589        let path = self.paths.get_mut(&PathId::ZERO).unwrap();
+3590        for (_, packet) in sent_packets.into_iter() {
+3591            path.data.remove_in_flight(&packet);
+3592        }
+3593
+3594        self.set_loss_detection_timer(now, PathId::ZERO)
+3595    }
+3596
+3597    fn handle_coalesced(
+3598        &mut self,
+3599        now: Instant,
+3600        network_path: FourTuple,
+3601        path_id: PathId,
+3602        ecn: Option<EcnCodepoint>,
+3603        data: BytesMut,
+3604    ) {
+3605        self.path_data_mut(path_id)
+3606            .inc_total_recvd(data.len() as u64);
+3607        let mut remaining = Some(data);
+3608        let cid_len = self
+3609            .local_cid_state
+3610            .values()
+3611            .map(|cid_state| cid_state.cid_len())
+3612            .next()
+3613            .expect("one cid_state must exist");
+3614        while let Some(data) = remaining {
+3615            match PartialDecode::new(
+3616                data,
+3617                &FixedLengthConnectionIdParser::new(cid_len),
+3618                &[self.version],
+3619                self.endpoint_config.grease_quic_bit,
+3620            ) {
+3621                Ok((partial_decode, rest)) => {
+3622                    remaining = rest;
+3623                    self.handle_decode(now, network_path, path_id, ecn, partial_decode);
+3624                }
+3625                Err(e) => {
+3626                    trace!("malformed header: {}", e);
+3627                    return;
+3628                }
+3629            }
+3630        }
+3631    }
+3632
+3633    fn handle_decode(
+3634        &mut self,
+3635        now: Instant,
+3636        network_path: FourTuple,
+3637        path_id: PathId,
+3638        ecn: Option<EcnCodepoint>,
+3639        partial_decode: PartialDecode,
+3640    ) {
+3641        let qlog = QlogRecvPacket::new(partial_decode.len());
+3642        if let Some(decoded) = packet_crypto::unprotect_header(
+3643            partial_decode,
+3644            &self.spaces,
+3645            self.zero_rtt_crypto.as_ref(),
+3646            self.peer_params.stateless_reset_token,
+3647        ) {
+3648            self.handle_packet(
+3649                now,
+3650                network_path,
+3651                path_id,
+3652                ecn,
+3653                decoded.packet,
+3654                decoded.stateless_reset,
+3655                qlog,
+3656            );
+3657        }
+3658    }
+3659
+3660    fn handle_packet(
+3661        &mut self,
+3662        now: Instant,
+3663        network_path: FourTuple,
+3664        path_id: PathId,
+3665        ecn: Option<EcnCodepoint>,
+3666        packet: Option<Packet>,
+3667        stateless_reset: bool,
+3668        mut qlog: QlogRecvPacket,
+3669    ) {
+3670        self.stats.udp_rx.ios += 1;
+3671        if let Some(ref packet) = packet {
+3672            trace!(
+3673                "got {:?} packet ({} bytes) from {} using id {}",
+3674                packet.header.space(),
+3675                packet.payload.len() + packet.header_data.len(),
+3676                network_path,
+3677                packet.header.dst_cid(),
+3678            );
+3679        }
+3680
+3681        if self.is_handshaking() {
+3682            if path_id != PathId::ZERO {
+3683                debug!(%network_path, %path_id, "discarding multipath packet during handshake");
+3684                return;
+3685            }
+3686            if network_path != self.path_data_mut(path_id).network_path {
+3687                if let Some(hs) = self.state.as_handshake() {
+3688                    if hs.allow_server_migration {
+3689                        trace!(%network_path, prev = %self.path_data(path_id).network_path, "server migrated to new remote");
+3690                        self.path_data_mut(path_id).network_path = network_path;
+3691                        self.qlog.emit_tuple_assigned(path_id, network_path, now);
+3692                    } else {
+3693                        debug!("discarding packet with unexpected remote during handshake");
+3694                        return;
+3695                    }
+3696                } else {
+3697                    debug!("discarding packet with unexpected remote during handshake");
+3698                    return;
+3699                }
+3700            }
+3701        }
+3702
+3703        let was_closed = self.state.is_closed();
+3704        let was_drained = self.state.is_drained();
+3705
+3706        let decrypted = match packet {
+3707            None => Err(None),
+3708            Some(mut packet) => self
+3709                .decrypt_packet(now, path_id, &mut packet)
+3710                .map(move |number| (packet, number)),
+3711        };
+3712        let result = match decrypted {
+3713            _ if stateless_reset => {
+3714                debug!("got stateless reset");
+3715                Err(ConnectionError::Reset)
+3716            }
+3717            Err(Some(e)) => {
+3718                warn!("illegal packet: {}", e);
+3719                Err(e.into())
+3720            }
+3721            Err(None) => {
+3722                debug!("failed to authenticate packet");
+3723                self.authentication_failures += 1;
+3724                let integrity_limit = self.spaces[self.highest_space]
+3725                    .crypto
+3726                    .as_ref()
+3727                    .unwrap()
+3728                    .packet
+3729                    .local
+3730                    .integrity_limit();
+3731                if self.authentication_failures > integrity_limit {
+3732                    Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into())
+3733                } else {
+3734                    return;
+3735                }
+3736            }
+3737            Ok((packet, number)) => {
+3738                qlog.header(&packet.header, number, path_id);
+3739                let span = match number {
+3740                    Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn),
+3741                    None => trace_span!("recv", space = ?packet.header.space()),
+3742                };
+3743                let _guard = span.enter();
+3744
+3745                let dedup = self.spaces[packet.header.space()]
+3746                    .path_space_mut(path_id)
+3747                    .map(|pns| &mut pns.dedup);
+3748                if number.zip(dedup).is_some_and(|(n, d)| d.insert(n)) {
+3749                    debug!("discarding possible duplicate packet");
+3750                    self.qlog.emit_packet_received(qlog, now);
+3751                    return;
+3752                } else if self.state.is_handshake() && packet.header.is_short() {
+3753                    // TODO: SHOULD buffer these to improve reordering tolerance.
+3754                    trace!("dropping short packet during handshake");
+3755                    self.qlog.emit_packet_received(qlog, now);
+3756                    return;
+3757                } else {
+3758                    if let Header::Initial(InitialHeader { ref token, .. }) = packet.header {
+3759                        if let Some(hs) = self.state.as_handshake() {
+3760                            if self.side.is_server() && token != &hs.expected_token {
+3761                                // Clients must send the same retry token in every Initial. Initial
+3762                                // packets can be spoofed, so we discard rather than killing the
+3763                                // connection.
+3764                                warn!("discarding Initial with invalid retry token");
+3765                                self.qlog.emit_packet_received(qlog, now);
+3766                                return;
+3767                            }
+3768                        }
+3769                    }
+3770
+3771                    if !self.state.is_closed() {
+3772                        let spin = match packet.header {
+3773                            Header::Short { spin, .. } => spin,
+3774                            _ => false,
+3775                        };
+3776
+3777                        if self.side().is_server() && !self.abandoned_paths.contains(&path_id) {
+3778                            // Only the client is allowed to open paths
+3779                            self.ensure_path(path_id, network_path, now, number);
+3780                        }
+3781                        if self.paths.contains_key(&path_id) {
+3782                            self.on_packet_authenticated(
+3783                                now,
+3784                                packet.header.space(),
+3785                                path_id,
+3786                                ecn,
+3787                                number,
+3788                                spin,
+3789                                packet.header.is_1rtt(),
+3790                            );
+3791                        }
+3792                    }
+3793
+3794                    let res = self.process_decrypted_packet(
+3795                        now,
+3796                        network_path,
+3797                        path_id,
+3798                        number,
+3799                        packet,
+3800                        &mut qlog,
+3801                    );
+3802
+3803                    self.qlog.emit_packet_received(qlog, now);
+3804                    res
+3805                }
+3806            }
+3807        };
+3808
+3809        // State transitions for error cases
+3810        if let Err(conn_err) = result {
+3811            match conn_err {
+3812                ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason),
+3813                ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason),
+3814                ConnectionError::Reset
+3815                | ConnectionError::TransportError(TransportError {
+3816                    code: TransportErrorCode::AEAD_LIMIT_REACHED,
+3817                    ..
+3818                }) => {
+3819                    self.state.move_to_drained(Some(conn_err));
+3820                }
+3821                ConnectionError::TimedOut => {
+3822                    unreachable!("timeouts aren't generated by packet processing");
+3823                }
+3824                ConnectionError::TransportError(err) => {
+3825                    debug!("closing connection due to transport error: {}", err);
+3826                    self.state.move_to_closed(err);
+3827                }
+3828                ConnectionError::VersionMismatch => {
+3829                    self.state.move_to_draining(Some(conn_err));
+3830                }
+3831                ConnectionError::LocallyClosed => {
+3832                    unreachable!("LocallyClosed isn't generated by packet processing");
+3833                }
+3834                ConnectionError::CidsExhausted => {
+3835                    unreachable!("CidsExhausted isn't generated by packet processing");
+3836                }
+3837            };
+3838        }
+3839
+3840        if !was_closed && self.state.is_closed() {
+3841            self.close_common();
+3842            if !self.state.is_drained() {
+3843                self.set_close_timer(now);
+3844            }
+3845        }
+3846        if !was_drained && self.state.is_drained() {
+3847            self.endpoint_events.push_back(EndpointEventInner::Drained);
+3848            // Close timer may have been started previously, e.g. if we sent a close and got a
+3849            // stateless reset in response
+3850            self.timers
+3851                .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now));
+3852        }
+3853
+3854        // Transmit CONNECTION_CLOSE if necessary
+3855        if matches!(self.state.as_type(), StateType::Closed) {
+3856            // If there is no PathData for this PathId the packet was for a brand new
+3857            // path. It was a valid packet however, so the remote is valid and we want to
+3858            // send CONNECTION_CLOSE.
+3859            let path_remote = self
+3860                .paths
+3861                .get(&path_id)
+3862                .map(|p| p.data.network_path)
+3863                .unwrap_or(network_path);
+3864            self.close = network_path == path_remote;
+3865        }
+3866    }
+3867
+3868    fn process_decrypted_packet(
+3869        &mut self,
+3870        now: Instant,
+3871        network_path: FourTuple,
+3872        path_id: PathId,
+3873        number: Option<u64>,
+3874        packet: Packet,
+3875        qlog: &mut QlogRecvPacket,
+3876    ) -> Result<(), ConnectionError> {
+3877        if !self.paths.contains_key(&path_id) {
+3878            // There is a chance this is a server side, first (for this path) packet, which would
+3879            // be a protocol violation. It's more likely, however, that this is a packet of a
+3880            // pruned path
+3881            trace!(%path_id, ?number, "discarding packet for unknown path");
+3882            return Ok(());
+3883        }
+3884        let state = match self.state.as_type() {
+3885            StateType::Established => {
+3886                match packet.header.space() {
+3887                    SpaceId::Data => self.process_payload(
+3888                        now,
+3889                        network_path,
+3890                        path_id,
+3891                        number.unwrap(),
+3892                        packet,
+3893                        qlog,
+3894                    )?,
+3895                    _ if packet.header.has_frames() => {
+3896                        self.process_early_payload(now, path_id, packet, qlog)?
+3897                    }
+3898                    _ => {
+3899                        trace!("discarding unexpected pre-handshake packet");
+3900                    }
+3901                }
+3902                return Ok(());
+3903            }
+3904            StateType::Closed => {
+3905                for result in frame::Iter::new(packet.payload.freeze())? {
+3906                    let frame = match result {
+3907                        Ok(frame) => frame,
+3908                        Err(err) => {
+3909                            debug!("frame decoding error: {err:?}");
+3910                            continue;
+3911                        }
+3912                    };
+3913                    qlog.frame(&frame);
+3914
+3915                    if let Frame::Padding = frame {
+3916                        continue;
+3917                    };
+3918
+3919                    self.stats.frame_rx.record(&frame);
+3920
+3921                    if let Frame::Close(_error) = frame {
+3922                        self.state.move_to_draining(None);
+3923                        break;
+3924                    }
+3925                }
+3926                return Ok(());
+3927            }
+3928            StateType::Draining | StateType::Drained => return Ok(()),
+3929            StateType::Handshake => self.state.as_handshake_mut().expect("checked"),
+3930        };
+3931
+3932        match packet.header {
+3933            Header::Retry {
+3934                src_cid: rem_cid, ..
+3935            } => {
+3936                debug_assert_eq!(path_id, PathId::ZERO);
+3937                if self.side.is_server() {
+3938                    return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into());
+3939                }
+3940
+3941                let is_valid_retry = self
+3942                    .rem_cids
+3943                    .get(&path_id)
+3944                    .map(|cids| cids.active())
+3945                    .map(|orig_dst_cid| {
+3946                        self.crypto.is_valid_retry(
+3947                            orig_dst_cid,
+3948                            &packet.header_data,
+3949                            &packet.payload,
+3950                        )
+3951                    })
+3952                    .unwrap_or_default();
+3953                if self.total_authed_packets > 1
+3954                            || packet.payload.len() <= 16 // token + 16 byte tag
+3955                            || !is_valid_retry
+3956                {
+3957                    trace!("discarding invalid Retry");
+3958                    // - After the client has received and processed an Initial or Retry
+3959                    //   packet from the server, it MUST discard any subsequent Retry
+3960                    //   packets that it receives.
+3961                    // - A client MUST discard a Retry packet with a zero-length Retry Token
+3962                    //   field.
+3963                    // - Clients MUST discard Retry packets that have a Retry Integrity Tag
+3964                    //   that cannot be validated
+3965                    return Ok(());
+3966                }
+3967
+3968                trace!("retrying with CID {}", rem_cid);
+3969                let client_hello = state.client_hello.take().unwrap();
+3970                self.retry_src_cid = Some(rem_cid);
+3971                self.rem_cids
+3972                    .get_mut(&path_id)
+3973                    .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false")
+3974                    .update_initial_cid(rem_cid);
+3975                self.rem_handshake_cid = rem_cid;
+3976
+3977                let space = &mut self.spaces[SpaceId::Initial];
+3978                if let Some(info) = space.for_path(PathId::ZERO).take(0) {
+3979                    self.on_packet_acked(now, PathId::ZERO, info);
+3980                };
+3981
+3982                self.discard_space(now, SpaceId::Initial); // Make sure we clean up after
+3983                // any retransmitted Initials
+3984                self.spaces[SpaceId::Initial] = {
+3985                    let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng);
+3986                    space.crypto = Some(self.crypto.initial_keys(rem_cid, self.side.side()));
+3987                    space.crypto_offset = client_hello.len() as u64;
+3988                    space.for_path(path_id).next_packet_number = self.spaces[SpaceId::Initial]
+3989                        .for_path(path_id)
+3990                        .next_packet_number;
+3991                    space.pending.crypto.push_back(frame::Crypto {
+3992                        offset: 0,
+3993                        data: client_hello,
+3994                    });
+3995                    space
+3996                };
+3997
+3998                // Retransmit all 0-RTT data
+3999                let zero_rtt = mem::take(
+4000                    &mut self.spaces[SpaceId::Data]
+4001                        .for_path(PathId::ZERO)
+4002                        .sent_packets,
+4003                );
+4004                for (_, info) in zero_rtt.into_iter() {
+4005                    self.paths
+4006                        .get_mut(&PathId::ZERO)
+4007                        .unwrap()
+4008                        .remove_in_flight(&info);
+4009                    self.spaces[SpaceId::Data].pending |= info.retransmits;
+4010                }
+4011                self.streams.retransmit_all_for_0rtt();
+4012
+4013                let token_len = packet.payload.len() - 16;
+4014                let ConnectionSide::Client { ref mut token, .. } = self.side else {
+4015                    unreachable!("we already short-circuited if we're server");
+4016                };
+4017                *token = packet.payload.freeze().split_to(token_len);
+4018
+4019                self.state = State::handshake(state::Handshake {
+4020                    expected_token: Bytes::new(),
+4021                    rem_cid_set: false,
+4022                    client_hello: None,
+4023                    allow_server_migration: true,
+4024                });
+4025                Ok(())
+4026            }
+4027            Header::Long {
+4028                ty: LongType::Handshake,
+4029                src_cid: rem_cid,
+4030                dst_cid: loc_cid,
+4031                ..
+4032            } => {
+4033                debug_assert_eq!(path_id, PathId::ZERO);
+4034                if rem_cid != self.rem_handshake_cid {
+4035                    debug!(
+4036                        "discarding packet with mismatched remote CID: {} != {}",
+4037                        self.rem_handshake_cid, rem_cid
+4038                    );
+4039                    return Ok(());
+4040                }
+4041                self.on_path_validated(path_id);
+4042
+4043                self.process_early_payload(now, path_id, packet, qlog)?;
+4044                if self.state.is_closed() {
+4045                    return Ok(());
+4046                }
+4047
+4048                if self.crypto.is_handshaking() {
+4049                    trace!("handshake ongoing");
+4050                    return Ok(());
+4051                }
+4052
+4053                if self.side.is_client() {
+4054                    // Client-only because server params were set from the client's Initial
+4055                    let params = self.crypto.transport_parameters()?.ok_or_else(|| {
+4056                        TransportError::new(
+4057                            TransportErrorCode::crypto(0x6d),
+4058                            "transport parameters missing".to_owned(),
+4059                        )
+4060                    })?;
+4061
+4062                    if self.has_0rtt() {
+4063                        if !self.crypto.early_data_accepted().unwrap() {
+4064                            debug_assert!(self.side.is_client());
+4065                            debug!("0-RTT rejected");
+4066                            self.accepted_0rtt = false;
+4067                            self.streams.zero_rtt_rejected();
+4068
+4069                            // Discard already-queued frames
+4070                            self.spaces[SpaceId::Data].pending = Retransmits::default();
+4071
+4072                            // Discard 0-RTT packets
+4073                            let sent_packets = mem::take(
+4074                                &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets,
+4075                            );
+4076                            for (_, packet) in sent_packets.into_iter() {
+4077                                self.paths
+4078                                    .get_mut(&path_id)
+4079                                    .unwrap()
+4080                                    .remove_in_flight(&packet);
+4081                            }
+4082                        } else {
+4083                            self.accepted_0rtt = true;
+4084                            params.validate_resumption_from(&self.peer_params)?;
+4085                        }
+4086                    }
+4087                    if let Some(token) = params.stateless_reset_token {
+4088                        // TODO(matheus23): Reset token for a remote, or for a 4-tuple?
+4089                        let remote = self.path_data(path_id).network_path.remote;
+4090                        self.endpoint_events
+4091                            .push_back(EndpointEventInner::ResetToken(path_id, remote, token));
+4092                    }
+4093                    self.handle_peer_params(params, loc_cid, rem_cid, now)?;
+4094                    self.issue_first_cids(now);
+4095                } else {
+4096                    // Server-only
+4097                    self.spaces[SpaceId::Data].pending.handshake_done = true;
+4098                    self.discard_space(now, SpaceId::Handshake);
+4099                    self.events.push_back(Event::HandshakeConfirmed);
+4100                    trace!("handshake confirmed");
+4101                }
+4102
+4103                self.events.push_back(Event::Connected);
+4104                self.state.move_to_established();
+4105                trace!("established");
+4106
+4107                // Multipath can only be enabled after the state has reached Established.
+4108                // So this can not happen any earlier.
+4109                self.issue_first_path_cids(now);
+4110                Ok(())
+4111            }
+4112            Header::Initial(InitialHeader {
+4113                src_cid: rem_cid,
+4114                dst_cid: loc_cid,
+4115                ..
+4116            }) => {
+4117                debug_assert_eq!(path_id, PathId::ZERO);
+4118                if !state.rem_cid_set {
+4119                    trace!("switching remote CID to {}", rem_cid);
+4120                    let mut state = state.clone();
+4121                    self.rem_cids
+4122                        .get_mut(&path_id)
+4123                        .expect("PathId::ZERO not yet abandoned")
+4124                        .update_initial_cid(rem_cid);
+4125                    self.rem_handshake_cid = rem_cid;
+4126                    self.orig_rem_cid = rem_cid;
+4127                    state.rem_cid_set = true;
+4128                    self.state.move_to_handshake(state);
+4129                } else if rem_cid != self.rem_handshake_cid {
+4130                    debug!(
+4131                        "discarding packet with mismatched remote CID: {} != {}",
+4132                        self.rem_handshake_cid, rem_cid
+4133                    );
+4134                    return Ok(());
+4135                }
+4136
+4137                let starting_space = self.highest_space;
+4138                self.process_early_payload(now, path_id, packet, qlog)?;
+4139
+4140                if self.side.is_server()
+4141                    && starting_space == SpaceId::Initial
+4142                    && self.highest_space != SpaceId::Initial
+4143                {
+4144                    let params = self.crypto.transport_parameters()?.ok_or_else(|| {
+4145                        TransportError::new(
+4146                            TransportErrorCode::crypto(0x6d),
+4147                            "transport parameters missing".to_owned(),
+4148                        )
+4149                    })?;
+4150                    self.handle_peer_params(params, loc_cid, rem_cid, now)?;
+4151                    self.issue_first_cids(now);
+4152                    self.init_0rtt(now);
+4153                }
+4154                Ok(())
+4155            }
+4156            Header::Long {
+4157                ty: LongType::ZeroRtt,
+4158                ..
+4159            } => {
+4160                self.process_payload(now, network_path, path_id, number.unwrap(), packet, qlog)?;
+4161                Ok(())
+4162            }
+4163            Header::VersionNegotiate { .. } => {
+4164                if self.total_authed_packets > 1 {
+4165                    return Ok(());
+4166                }
+4167                let supported = packet
+4168                    .payload
+4169                    .chunks(4)
+4170                    .any(|x| match <[u8; 4]>::try_from(x) {
+4171                        Ok(version) => self.version == u32::from_be_bytes(version),
+4172                        Err(_) => false,
+4173                    });
+4174                if supported {
+4175                    return Ok(());
+4176                }
+4177                debug!("remote doesn't support our version");
+4178                Err(ConnectionError::VersionMismatch)
+4179            }
+4180            Header::Short { .. } => unreachable!(
+4181                "short packets received during handshake are discarded in handle_packet"
+4182            ),
+4183        }
+4184    }
+4185
+4186    /// Process an Initial or Handshake packet payload
+4187    fn process_early_payload(
+4188        &mut self,
+4189        now: Instant,
+4190        path_id: PathId,
+4191        packet: Packet,
+4192        #[allow(unused)] qlog: &mut QlogRecvPacket,
+4193    ) -> Result<(), TransportError> {
+4194        debug_assert_ne!(packet.header.space(), SpaceId::Data);
+4195        debug_assert_eq!(path_id, PathId::ZERO);
+4196        let payload_len = packet.payload.len();
+4197        let mut ack_eliciting = false;
+4198        for result in frame::Iter::new(packet.payload.freeze())? {
+4199            let frame = result?;
+4200            qlog.frame(&frame);
+4201            let span = match frame {
+4202                Frame::Padding => continue,
+4203                _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)),
+4204            };
+4205
+4206            self.stats.frame_rx.record(&frame);
+4207
+4208            let _guard = span.as_ref().map(|x| x.enter());
+4209            ack_eliciting |= frame.is_ack_eliciting();
+4210
+4211            // Process frames
+4212            if frame.is_1rtt() && packet.header.space() != SpaceId::Data {
+4213                return Err(TransportError::PROTOCOL_VIOLATION(
+4214                    "illegal frame type in handshake",
+4215                ));
+4216            }
+4217
+4218            match frame {
+4219                Frame::Padding | Frame::Ping => {}
+4220                Frame::Crypto(frame) => {
+4221                    self.read_crypto(packet.header.space(), &frame, payload_len)?;
+4222                }
+4223                Frame::Ack(ack) => {
+4224                    self.on_ack_received(now, packet.header.space(), ack)?;
+4225                }
+4226                Frame::PathAck(ack) => {
+4227                    span.as_ref()
+4228                        .map(|span| span.record("path", tracing::field::debug(&ack.path_id)));
+4229                    self.on_path_ack_received(now, packet.header.space(), ack)?;
+4230                }
+4231                Frame::Close(reason) => {
+4232                    self.state.move_to_draining(Some(reason.into()));
+4233                    return Ok(());
+4234                }
+4235                _ => {
+4236                    let mut err =
+4237                        TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake");
+4238                    err.frame = frame::MaybeFrame::Known(frame.ty());
+4239                    return Err(err);
+4240                }
+4241            }
+4242        }
+4243
+4244        if ack_eliciting {
+4245            // In the initial and handshake spaces, ACKs must be sent immediately
+4246            self.spaces[packet.header.space()]
+4247                .for_path(path_id)
+4248                .pending_acks
+4249                .set_immediate_ack_required();
+4250        }
+4251
+4252        self.write_crypto();
+4253        Ok(())
+4254    }
+4255
+4256    /// Processes the packet payload, always in the data space.
+4257    fn process_payload(
+4258        &mut self,
+4259        now: Instant,
+4260        network_path: FourTuple,
+4261        path_id: PathId,
+4262        number: u64,
+4263        packet: Packet,
+4264        #[allow(unused)] qlog: &mut QlogRecvPacket,
+4265    ) -> Result<(), TransportError> {
+4266        let payload = packet.payload.freeze();
+4267        let mut is_probing_packet = true;
+4268        let mut close = None;
+4269        let payload_len = payload.len();
+4270        let mut ack_eliciting = false;
+4271        // if this packet triggers a path migration and includes a observed address frame, it's
+4272        // stored here
+4273        let mut migration_observed_addr = None;
+4274        for result in frame::Iter::new(payload)? {
+4275            let frame = result?;
+4276            qlog.frame(&frame);
+4277            let span = match frame {
+4278                Frame::Padding => continue,
+4279                _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty),
+4280            };
+4281
+4282            self.stats.frame_rx.record(&frame);
+4283            // Crypto, Stream and Datagram frames are special cased in order no pollute
+4284            // the log with payload data
+4285            match &frame {
+4286                Frame::Crypto(f) => {
+4287                    trace!(offset = f.offset, len = f.data.len(), "got crypto frame");
+4288                }
+4289                Frame::Stream(f) => {
+4290                    trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got stream frame");
+4291                }
+4292                Frame::Datagram(f) => {
+4293                    trace!(len = f.data.len(), "got datagram frame");
+4294                }
+4295                f => {
+4296                    trace!("got frame {f}");
+4297                }
+4298            }
+4299
+4300            let _guard = span.enter();
+4301            if packet.header.is_0rtt() {
+4302                match frame {
+4303                    Frame::Crypto(_) | Frame::Close(Close::Application(_)) => {
+4304                        return Err(TransportError::PROTOCOL_VIOLATION(
+4305                            "illegal frame type in 0-RTT",
+4306                        ));
+4307                    }
+4308                    _ => {
+4309                        if frame.is_1rtt() {
+4310                            return Err(TransportError::PROTOCOL_VIOLATION(
+4311                                "illegal frame type in 0-RTT",
+4312                            ));
+4313                        }
+4314                    }
+4315                }
+4316            }
+4317            ack_eliciting |= frame.is_ack_eliciting();
+4318
+4319            // Check whether this could be a probing packet
+4320            match frame {
+4321                Frame::Padding
+4322                | Frame::PathChallenge(_)
+4323                | Frame::PathResponse(_)
+4324                | Frame::NewConnectionId(_)
+4325                | Frame::ObservedAddr(_) => {}
+4326                _ => {
+4327                    is_probing_packet = false;
+4328                }
+4329            }
+4330
+4331            match frame {
+4332                Frame::Crypto(frame) => {
+4333                    self.read_crypto(SpaceId::Data, &frame, payload_len)?;
+4334                }
+4335                Frame::Stream(frame) => {
+4336                    if self.streams.received(frame, payload_len)?.should_transmit() {
+4337                        self.spaces[SpaceId::Data].pending.max_data = true;
+4338                    }
+4339                }
+4340                Frame::Ack(ack) => {
+4341                    self.on_ack_received(now, SpaceId::Data, ack)?;
+4342                }
+4343                Frame::PathAck(ack) => {
+4344                    span.record("path", tracing::field::debug(&ack.path_id));
+4345                    self.on_path_ack_received(now, SpaceId::Data, ack)?;
+4346                }
+4347                Frame::Padding | Frame::Ping => {}
+4348                Frame::Close(reason) => {
+4349                    close = Some(reason);
+4350                }
+4351                Frame::PathChallenge(challenge) => {
+4352                    let path = &mut self
+4353                        .path_mut(path_id)
+4354                        .expect("payload is processed only after the path becomes known");
+4355                    path.path_responses.push(number, challenge.0, network_path);
+4356                    // At this point, update_network_path_or_discard was already called, so
+4357                    // we don't need to be lenient about `local_ip` possibly mis-matching.
+4358                    if network_path == path.network_path {
+4359                        // PATH_CHALLENGE on active path, possible off-path packet forwarding
+4360                        // attack. Send a non-probing packet to recover the active path.
+4361                        // TODO(flub): No longer true! We now path_challege also to validate
+4362                        //    the path if the path is new, without an RFC9000-style
+4363                        //    migration involved. This means we add in an extra
+4364                        //    IMMEDIATE_ACK on some challenges. It isn't really wrong to do
+4365                        //    so, but it still is something untidy. We should instead
+4366                        //    suppress this when we know the remote is still validating the
+4367                        //    path.
+4368                        match self.peer_supports_ack_frequency() {
+4369                            true => self.immediate_ack(path_id),
+4370                            false => {
+4371                                self.ping_path(path_id).ok();
+4372                            }
+4373                        }
+4374                    }
+4375                }
+4376                Frame::PathResponse(response) => {
+4377                    let path = self
+4378                        .paths
+4379                        .get_mut(&path_id)
+4380                        .expect("payload is processed only after the path becomes known");
+4381
+4382                    use PathTimer::*;
+4383                    use paths::OnPathResponseReceived::*;
+4384                    match path
+4385                        .data
+4386                        .on_path_response_received(now, response.0, network_path)
+4387                    {
+4388                        OnPath { was_open } => {
+4389                            let qlog = self.qlog.with_time(now);
+4390
+4391                            self.timers
+4392                                .stop(Timer::PerPath(path_id, PathValidation), qlog.clone());
+4393                            self.timers
+4394                                .stop(Timer::PerPath(path_id, PathOpen), qlog.clone());
+4395
+4396                            let next_challenge = path
+4397                                .data
+4398                                .earliest_expiring_challenge()
+4399                                .map(|time| time + self.ack_frequency.max_ack_delay_for_pto());
+4400                            self.timers.set_or_stop(
+4401                                Timer::PerPath(path_id, PathChallengeLost),
+4402                                next_challenge,
+4403                                qlog,
+4404                            );
+4405
+4406                            if !was_open {
+4407                                self.events
+4408                                    .push_back(Event::Path(PathEvent::Opened { id: path_id }));
+4409                                if let Some(observed) = path.data.last_observed_addr_report.as_ref()
+4410                                {
+4411                                    self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+4412                                        id: path_id,
+4413                                        addr: observed.socket_addr(),
+4414                                    }));
+4415                                }
+4416                            }
+4417                            if let Some((_, ref mut prev)) = path.prev {
+4418                                prev.challenges_sent.clear();
+4419                                prev.send_new_challenge = false;
+4420                            }
+4421                        }
+4422                        OffPath => {
+4423                            debug!("Response to off-path PathChallenge!");
+4424                            let next_challenge = path
+4425                                .data
+4426                                .earliest_expiring_challenge()
+4427                                .map(|time| time + self.ack_frequency.max_ack_delay_for_pto());
+4428                            self.timers.set_or_stop(
+4429                                Timer::PerPath(path_id, PathChallengeLost),
+4430                                next_challenge,
+4431                                self.qlog.with_time(now),
+4432                            );
+4433                        }
+4434                        Invalid { expected } => {
+4435                            debug!(%response, %network_path, %expected, "ignoring invalid PATH_RESPONSE")
+4436                        }
+4437                        Unknown => debug!(%response, "ignoring invalid PATH_RESPONSE"),
+4438                    }
+4439                }
+4440                Frame::MaxData(bytes) => {
+4441                    self.streams.received_max_data(bytes);
+4442                }
+4443                Frame::MaxStreamData { id, offset } => {
+4444                    self.streams.received_max_stream_data(id, offset)?;
+4445                }
+4446                Frame::MaxStreams { dir, count } => {
+4447                    self.streams.received_max_streams(dir, count)?;
+4448                }
+4449                Frame::ResetStream(frame) => {
+4450                    if self.streams.received_reset(frame)?.should_transmit() {
+4451                        self.spaces[SpaceId::Data].pending.max_data = true;
+4452                    }
+4453                }
+4454                Frame::DataBlocked { offset } => {
+4455                    debug!(offset, "peer claims to be blocked at connection level");
+4456                }
+4457                Frame::StreamDataBlocked { id, offset } => {
+4458                    if id.initiator() == self.side.side() && id.dir() == Dir::Uni {
+4459                        debug!("got STREAM_DATA_BLOCKED on send-only {}", id);
+4460                        return Err(TransportError::STREAM_STATE_ERROR(
+4461                            "STREAM_DATA_BLOCKED on send-only stream",
+4462                        ));
+4463                    }
+4464                    debug!(
+4465                        stream = %id,
+4466                        offset, "peer claims to be blocked at stream level"
+4467                    );
+4468                }
+4469                Frame::StreamsBlocked { dir, limit } => {
+4470                    if limit > MAX_STREAM_COUNT {
+4471                        return Err(TransportError::FRAME_ENCODING_ERROR(
+4472                            "unrepresentable stream limit",
+4473                        ));
+4474                    }
+4475                    debug!(
+4476                        "peer claims to be blocked opening more than {} {} streams",
+4477                        limit, dir
+4478                    );
+4479                }
+4480                Frame::StopSending(frame::StopSending { id, error_code }) => {
+4481                    if id.initiator() != self.side.side() {
+4482                        if id.dir() == Dir::Uni {
+4483                            debug!("got STOP_SENDING on recv-only {}", id);
+4484                            return Err(TransportError::STREAM_STATE_ERROR(
+4485                                "STOP_SENDING on recv-only stream",
+4486                            ));
+4487                        }
+4488                    } else if self.streams.is_local_unopened(id) {
+4489                        return Err(TransportError::STREAM_STATE_ERROR(
+4490                            "STOP_SENDING on unopened stream",
+4491                        ));
+4492                    }
+4493                    self.streams.received_stop_sending(id, error_code);
+4494                }
+4495                Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => {
+4496                    if let Some(ref path_id) = path_id {
+4497                        span.record("path", tracing::field::debug(&path_id));
+4498                    }
+4499                    let path_id = path_id.unwrap_or_default();
+4500                    match self.local_cid_state.get_mut(&path_id) {
+4501                        None => error!(?path_id, "RETIRE_CONNECTION_ID for unknown path"),
+4502                        Some(cid_state) => {
+4503                            let allow_more_cids = cid_state
+4504                                .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?;
+4505
+4506                            // If the path has closed, we do not issue more CIDs for this path
+4507                            // For details see  https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2
+4508                            // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames.
+4509                            let has_path = !self.abandoned_paths.contains(&path_id);
+4510                            let allow_more_cids = allow_more_cids && has_path;
+4511
+4512                            self.endpoint_events
+4513                                .push_back(EndpointEventInner::RetireConnectionId(
+4514                                    now,
+4515                                    path_id,
+4516                                    sequence,
+4517                                    allow_more_cids,
+4518                                ));
+4519                        }
+4520                    }
+4521                }
+4522                Frame::NewConnectionId(frame) => {
+4523                    let path_id = if let Some(path_id) = frame.path_id {
+4524                        if !self.is_multipath_negotiated() {
+4525                            return Err(TransportError::PROTOCOL_VIOLATION(
+4526                                "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated",
+4527                            ));
+4528                        }
+4529                        if path_id > self.local_max_path_id {
+4530                            return Err(TransportError::PROTOCOL_VIOLATION(
+4531                                "PATH_NEW_CONNECTION_ID contains path_id exceeding current max",
+4532                            ));
+4533                        }
+4534                        path_id
+4535                    } else {
+4536                        PathId::ZERO
+4537                    };
+4538
+4539                    if self.abandoned_paths.contains(&path_id) {
+4540                        trace!("ignoring issued CID for abandoned path");
+4541                        continue;
+4542                    }
+4543                    if let Some(ref path_id) = frame.path_id {
+4544                        span.record("path", tracing::field::debug(&path_id));
+4545                    }
+4546                    let rem_cids = self
+4547                        .rem_cids
+4548                        .entry(path_id)
+4549                        .or_insert_with(|| CidQueue::new(frame.id));
+4550                    if rem_cids.active().is_empty() {
+4551                        return Err(TransportError::PROTOCOL_VIOLATION(
+4552                            "NEW_CONNECTION_ID when CIDs aren't in use",
+4553                        ));
+4554                    }
+4555                    if frame.retire_prior_to > frame.sequence {
+4556                        return Err(TransportError::PROTOCOL_VIOLATION(
+4557                            "NEW_CONNECTION_ID retiring unissued CIDs",
+4558                        ));
+4559                    }
+4560
+4561                    use crate::cid_queue::InsertError;
+4562                    match rem_cids.insert(frame) {
+4563                        Ok(None) if self.path(path_id).is_none() => {
+4564                            // if this gives us CIDs to open a new path and a nat traversal attempt
+4565                            // is underway we could try to probe a pending remote
+4566                            self.continue_nat_traversal_round(now);
+4567                        }
+4568                        Ok(None) => {}
+4569                        Ok(Some((retired, reset_token))) => {
+4570                            let pending_retired =
+4571                                &mut self.spaces[SpaceId::Data].pending.retire_cids;
+4572                            /// Ensure `pending_retired` cannot grow without bound. Limit is
+4573                            /// somewhat arbitrary but very permissive.
+4574                            const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10;
+4575                            // We don't bother counting in-flight frames because those are bounded
+4576                            // by congestion control.
+4577                            if (pending_retired.len() as u64)
+4578                                .saturating_add(retired.end.saturating_sub(retired.start))
+4579                                > MAX_PENDING_RETIRED_CIDS
+4580                            {
+4581                                return Err(TransportError::CONNECTION_ID_LIMIT_ERROR(
+4582                                    "queued too many retired CIDs",
+4583                                ));
+4584                            }
+4585                            pending_retired.extend(retired.map(|seq| (path_id, seq)));
+4586                            // TODO(matheus23): Reset token for a remote or a full 4-tuple?
+4587                            self.set_reset_token(path_id, network_path.remote, reset_token);
+4588                        }
+4589                        Err(InsertError::ExceedsLimit) => {
+4590                            return Err(TransportError::CONNECTION_ID_LIMIT_ERROR(""));
+4591                        }
+4592                        Err(InsertError::Retired) => {
+4593                            trace!("discarding already-retired");
+4594                            // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a
+4595                            // range of connection IDs larger than the active connection ID limit
+4596                            // was retired all at once via retire_prior_to.
+4597                            self.spaces[SpaceId::Data]
+4598                                .pending
+4599                                .retire_cids
+4600                                .push((path_id, frame.sequence));
+4601                            continue;
+4602                        }
+4603                    };
+4604
+4605                    if self.side.is_server()
+4606                        && path_id == PathId::ZERO
+4607                        && self
+4608                            .rem_cids
+4609                            .get(&PathId::ZERO)
+4610                            .map(|cids| cids.active_seq() == 0)
+4611                            .unwrap_or_default()
+4612                    {
+4613                        // We're a server still using the initial remote CID for the client, so
+4614                        // let's switch immediately to enable clientside stateless resets.
+4615                        self.update_rem_cid(PathId::ZERO);
+4616                    }
+4617                }
+4618                Frame::NewToken(NewToken { token }) => {
+4619                    let ConnectionSide::Client {
+4620                        token_store,
+4621                        server_name,
+4622                        ..
+4623                    } = &self.side
+4624                    else {
+4625                        return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN"));
+4626                    };
+4627                    if token.is_empty() {
+4628                        return Err(TransportError::FRAME_ENCODING_ERROR("empty token"));
+4629                    }
+4630                    trace!("got new token");
+4631                    token_store.insert(server_name, token);
+4632                }
+4633                Frame::Datagram(datagram) => {
+4634                    if self
+4635                        .datagrams
+4636                        .received(datagram, &self.config.datagram_receive_buffer_size)?
+4637                    {
+4638                        self.events.push_back(Event::DatagramReceived);
+4639                    }
+4640                }
+4641                Frame::AckFrequency(ack_frequency) => {
+4642                    // This frame can only be sent in the Data space
+4643
+4644                    if !self.ack_frequency.ack_frequency_received(&ack_frequency)? {
+4645                        // The AckFrequency frame is stale (we have already received a more
+4646                        // recent one)
+4647                        continue;
+4648                    }
+4649
+4650                    // Update the params for all of our paths
+4651                    for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() {
+4652                        space.pending_acks.set_ack_frequency_params(&ack_frequency);
+4653
+4654                        // Our `max_ack_delay` has been updated, so we may need to adjust
+4655                        // its associated timeout
+4656                        if let Some(timeout) = space
+4657                            .pending_acks
+4658                            .max_ack_delay_timeout(self.ack_frequency.max_ack_delay)
+4659                        {
+4660                            self.timers.set(
+4661                                Timer::PerPath(*path_id, PathTimer::MaxAckDelay),
+4662                                timeout,
+4663                                self.qlog.with_time(now),
+4664                            );
+4665                        }
+4666                    }
+4667                }
+4668                Frame::ImmediateAck => {
+4669                    // This frame can only be sent in the Data space
+4670                    for pns in self.spaces[SpaceId::Data].iter_paths_mut() {
+4671                        pns.pending_acks.set_immediate_ack_required();
+4672                    }
+4673                }
+4674                Frame::HandshakeDone => {
+4675                    if self.side.is_server() {
+4676                        return Err(TransportError::PROTOCOL_VIOLATION(
+4677                            "client sent HANDSHAKE_DONE",
+4678                        ));
+4679                    }
+4680                    if self.spaces[SpaceId::Handshake].crypto.is_some() {
+4681                        self.discard_space(now, SpaceId::Handshake);
+4682                    }
+4683                    self.events.push_back(Event::HandshakeConfirmed);
+4684                    trace!("handshake confirmed");
+4685                }
+4686                Frame::ObservedAddr(observed) => {
+4687                    // check if params allows the peer to send report and this node to receive it
+4688                    trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port);
+4689                    if !self
+4690                        .peer_params
+4691                        .address_discovery_role
+4692                        .should_report(&self.config.address_discovery_role)
+4693                    {
+4694                        return Err(TransportError::PROTOCOL_VIOLATION(
+4695                            "received OBSERVED_ADDRESS frame when not negotiated",
+4696                        ));
+4697                    }
+4698                    // must only be sent in data space
+4699                    if packet.header.space() != SpaceId::Data {
+4700                        return Err(TransportError::PROTOCOL_VIOLATION(
+4701                            "OBSERVED_ADDRESS frame outside data space",
+4702                        ));
+4703                    }
+4704
+4705                    let path = self.path_data_mut(path_id);
+4706                    if network_path == path.network_path {
+4707                        if let Some(updated) = path.update_observed_addr_report(observed) {
+4708                            if path.open {
+4709                                self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+4710                                    id: path_id,
+4711                                    addr: updated,
+4712                                }));
+4713                            }
+4714                            // otherwise the event is reported when the path is deemed open
+4715                        }
+4716                    } else {
+4717                        // include in migration
+4718                        migration_observed_addr = Some(observed)
+4719                    }
+4720                }
+4721                Frame::PathAbandon(frame::PathAbandon {
+4722                    path_id,
+4723                    error_code,
+4724                }) => {
+4725                    span.record("path", tracing::field::debug(&path_id));
+4726                    // TODO(flub): don't really know which error code to use here.
+4727                    let already_abandoned = match self.close_path(now, path_id, error_code.into()) {
+4728                        Ok(()) => {
+4729                            trace!("peer abandoned path");
+4730                            false
+4731                        }
+4732                        Err(ClosePathError::LastOpenPath) => {
+4733                            trace!("peer abandoned last path, closing connection");
+4734                            // TODO(flub): which error code?
+4735                            return Err(TransportError::NO_ERROR("last path abandoned by peer"));
+4736                        }
+4737                        Err(ClosePathError::ClosedPath) => {
+4738                            trace!("peer abandoned already closed path");
+4739                            true
+4740                        }
+4741                    };
+4742                    // If we receive a retransmit of PATH_ABANDON then we may already have
+4743                    // abandoned this path locally.  In that case the DiscardPath timer
+4744                    // may already have fired and we no longer have any state for this path.
+4745                    // Only set this timer if we still have path state.
+4746                    if self.path(path_id).is_some() && !already_abandoned {
+4747                        // TODO(flub): Checking is_some() here followed by a number of calls
+4748                        //    that would panic if it was None is really ugly.  If only we
+4749                        //    could do something like PathData::pto().  One day we'll have
+4750                        //    unified SpaceId and PathId and this will be possible.
+4751                        let delay = self.pto(SpaceId::Data, path_id) * 3;
+4752                        self.timers.set(
+4753                            Timer::PerPath(path_id, PathTimer::DiscardPath),
+4754                            now + delay,
+4755                            self.qlog.with_time(now),
+4756                        );
+4757                    }
+4758                }
+4759                Frame::PathStatusAvailable(info) => {
+4760                    span.record("path", tracing::field::debug(&info.path_id));
+4761                    if self.is_multipath_negotiated() {
+4762                        self.on_path_status(
+4763                            info.path_id,
+4764                            PathStatus::Available,
+4765                            info.status_seq_no,
+4766                        );
+4767                    } else {
+4768                        return Err(TransportError::PROTOCOL_VIOLATION(
+4769                            "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated",
+4770                        ));
+4771                    }
+4772                }
+4773                Frame::PathStatusBackup(info) => {
+4774                    span.record("path", tracing::field::debug(&info.path_id));
+4775                    if self.is_multipath_negotiated() {
+4776                        self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no);
+4777                    } else {
+4778                        return Err(TransportError::PROTOCOL_VIOLATION(
+4779                            "received PATH_STATUS_BACKUP frame when multipath was not negotiated",
+4780                        ));
+4781                    }
+4782                }
+4783                Frame::MaxPathId(frame::MaxPathId(path_id)) => {
+4784                    span.record("path", tracing::field::debug(&path_id));
+4785                    if !self.is_multipath_negotiated() {
+4786                        return Err(TransportError::PROTOCOL_VIOLATION(
+4787                            "received MAX_PATH_ID frame when multipath was not negotiated",
+4788                        ));
+4789                    }
+4790                    // frames that do not increase the path id are ignored
+4791                    if path_id > self.remote_max_path_id {
+4792                        self.remote_max_path_id = path_id;
+4793                        self.issue_first_path_cids(now);
+4794                        while let Some(true) = self.continue_nat_traversal_round(now) {}
+4795                    }
+4796                }
+4797                Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => {
+4798                    // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST
+4799                    // be treated as a connection error of type PROTOCOL_VIOLATION.
+4800                    // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids>
+4801                    if self.is_multipath_negotiated() {
+4802                        if self.local_max_path_id > max_path_id {
+4803                            return Err(TransportError::PROTOCOL_VIOLATION(
+4804                                "PATHS_BLOCKED maximum path identifier was larger than local maximum",
+4805                            ));
+4806                        }
+4807                        debug!("received PATHS_BLOCKED({:?})", max_path_id);
+4808                        // TODO(@divma): ensure max concurrent paths
+4809                    } else {
+4810                        return Err(TransportError::PROTOCOL_VIOLATION(
+4811                            "received PATHS_BLOCKED frame when not multipath was not negotiated",
+4812                        ));
+4813                    }
+4814                }
+4815                Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => {
+4816                    // Nothing to do.  This is recorded in the frame stats, but otherwise we
+4817                    // always issue all CIDs we're allowed to issue, so either this is an
+4818                    // impatient peer or a bug on our side.
+4819
+4820                    // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST
+4821                    // be treated as a connection error of type PROTOCOL_VIOLATION.
+4822                    // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids>
+4823                    if self.is_multipath_negotiated() {
+4824                        if path_id > self.local_max_path_id {
+4825                            return Err(TransportError::PROTOCOL_VIOLATION(
+4826                                "PATH_CIDS_BLOCKED path identifier was larger than local maximum",
+4827                            ));
+4828                        }
+4829                        if next_seq.0
+4830                            > self
+4831                                .local_cid_state
+4832                                .get(&path_id)
+4833                                .map(|cid_state| cid_state.active_seq().1 + 1)
+4834                                .unwrap_or_default()
+4835                        {
+4836                            return Err(TransportError::PROTOCOL_VIOLATION(
+4837                                "PATH_CIDS_BLOCKED next sequence number larger than in local state",
+4838                            ));
+4839                        }
+4840                        debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED");
+4841                    } else {
+4842                        return Err(TransportError::PROTOCOL_VIOLATION(
+4843                            "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated",
+4844                        ));
+4845                    }
+4846                }
+4847                Frame::AddAddress(addr) => {
+4848                    let client_state = match self.iroh_hp.client_side_mut() {
+4849                        Ok(state) => state,
+4850                        Err(err) => {
+4851                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+4852                                "Nat traversal(ADD_ADDRESS): {err}"
+4853                            )));
+4854                        }
+4855                    };
+4856
+4857                    if !client_state.check_remote_address(&addr) {
+4858                        // if the address is not valid we flag it, but update anyway
+4859                        warn!(?addr, "server sent illegal ADD_ADDRESS frame");
+4860                    }
+4861
+4862                    match client_state.add_remote_address(addr) {
+4863                        Ok(maybe_added) => {
+4864                            if let Some(added) = maybe_added {
+4865                                self.events.push_back(Event::NatTraversal(
+4866                                    iroh_hp::Event::AddressAdded(added),
+4867                                ));
+4868                            }
+4869                        }
+4870                        Err(e) => {
+4871                            warn!(%e, "failed to add remote address")
+4872                        }
+4873                    }
+4874                }
+4875                Frame::RemoveAddress(addr) => {
+4876                    let client_state = match self.iroh_hp.client_side_mut() {
+4877                        Ok(state) => state,
+4878                        Err(err) => {
+4879                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+4880                                "Nat traversal(REMOVE_ADDRESS): {err}"
+4881                            )));
+4882                        }
+4883                    };
+4884                    if let Some(removed_addr) = client_state.remove_remote_address(addr) {
+4885                        self.events
+4886                            .push_back(Event::NatTraversal(iroh_hp::Event::AddressRemoved(
+4887                                removed_addr,
+4888                            )));
+4889                    }
+4890                }
+4891                Frame::ReachOut(reach_out) => {
+4892                    let server_state = match self.iroh_hp.server_side_mut() {
+4893                        Ok(state) => state,
+4894                        Err(err) => {
+4895                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+4896                                "Nat traversal(REACH_OUT): {err}"
+4897                            )));
+4898                        }
+4899                    };
+4900
+4901                    if let Err(err) = server_state.handle_reach_out(reach_out) {
+4902                        return Err(TransportError::PROTOCOL_VIOLATION(format!(
+4903                            "Nat traversal(REACH_OUT): {err}"
+4904                        )));
+4905                    }
+4906                }
+4907            }
+4908        }
+4909
+4910        let space = self.spaces[SpaceId::Data].for_path(path_id);
+4911        if space
+4912            .pending_acks
+4913            .packet_received(now, number, ack_eliciting, &space.dedup)
+4914        {
+4915            if self.abandoned_paths.contains(&path_id) {
+4916                // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from
+4917                // abandoned paths.
+4918                space.pending_acks.set_immediate_ack_required();
+4919            } else {
+4920                self.timers.set(
+4921                    Timer::PerPath(path_id, PathTimer::MaxAckDelay),
+4922                    now + self.ack_frequency.max_ack_delay,
+4923                    self.qlog.with_time(now),
+4924                );
+4925            }
+4926        }
+4927
+4928        // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets
+4929        // on stopped streams. Incoming finishes/resets on open streams are not handled here as they
+4930        // are only freed, and hence only issue credit, once the application has been notified
+4931        // during a read on the stream.
+4932        let pending = &mut self.spaces[SpaceId::Data].pending;
+4933        self.streams.queue_max_stream_id(pending);
+4934
+4935        if let Some(reason) = close {
+4936            self.state.move_to_draining(Some(reason.into()));
+4937            self.close = true;
+4938        }
+4939
+4940        if Some(number) == self.spaces[SpaceId::Data].for_path(path_id).rx_packet
+4941            && !is_probing_packet
+4942            && network_path != self.path_data(path_id).network_path
+4943        {
+4944            let ConnectionSide::Server { ref server_config } = self.side else {
+4945                panic!("packets from unknown remote should be dropped by clients");
+4946            };
+4947            debug_assert!(
+4948                server_config.migration,
+4949                "migration-initiating packets should have been dropped immediately"
+4950            );
+4951            self.migrate(path_id, now, network_path, migration_observed_addr);
+4952            // Break linkability, if possible
+4953            self.update_rem_cid(path_id);
+4954            self.spin = false;
+4955        }
+4956
+4957        Ok(())
+4958    }
+4959
+4960    fn migrate(
+4961        &mut self,
+4962        path_id: PathId,
+4963        now: Instant,
+4964        network_path: FourTuple,
+4965        observed_addr: Option<ObservedAddr>,
+4966    ) {
+4967        trace!(%network_path, %path_id, "migration initiated");
+4968        self.path_counter = self.path_counter.wrapping_add(1);
+4969        // TODO(@divma): conditions for path migration in multipath are very specific, check them
+4970        // again to prevent path migrations that should actually create a new path
+4971
+4972        // Reset rtt/congestion state for new path unless it looks like a NAT rebinding.
+4973        // Note that the congestion window will not grow until validation terminates. Helps mitigate
+4974        // amplification attacks performed by spoofing source addresses.
+4975        let prev_pto = self.pto(SpaceId::Data, path_id);
+4976        let known_path = self.paths.get_mut(&path_id).expect("known path");
+4977        let path = &mut known_path.data;
+4978        let mut new_path = if network_path.remote.is_ipv4()
+4979            && network_path.remote.ip() == path.network_path.remote.ip()
+4980        {
+4981            PathData::from_previous(network_path, path, self.path_counter, now)
+4982        } else {
+4983            let peer_max_udp_payload_size =
+4984                u16::try_from(self.peer_params.max_udp_payload_size.into_inner())
+4985                    .unwrap_or(u16::MAX);
+4986            PathData::new(
+4987                network_path,
+4988                self.allow_mtud,
+4989                Some(peer_max_udp_payload_size),
+4990                self.path_counter,
+4991                now,
+4992                &self.config,
+4993            )
+4994        };
+4995        new_path.last_observed_addr_report = path.last_observed_addr_report.clone();
+4996        if let Some(report) = observed_addr {
+4997            if let Some(updated) = new_path.update_observed_addr_report(report) {
+4998                tracing::info!("adding observed addr event from migration");
+4999                self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+5000                    id: path_id,
+5001                    addr: updated,
+5002                }));
+5003            }
+5004        }
+5005        new_path.send_new_challenge = true;
+5006
+5007        let mut prev = mem::replace(path, new_path);
+5008        // Don't clobber the original path if the previous one hasn't been validated yet
+5009        if !prev.is_validating_path() {
+5010            prev.send_new_challenge = true;
+5011            // We haven't updated the remote CID yet, this captures the remote CID we were using on
+5012            // the previous path.
+5013
+5014            known_path.prev = Some((self.rem_cids.get(&path_id).unwrap().active(), prev));
+5015        }
+5016
+5017        // We need to re-assign the correct remote to this path in qlog
+5018        self.qlog.emit_tuple_assigned(path_id, network_path, now);
+5019
+5020        self.timers.set(
+5021            Timer::PerPath(path_id, PathTimer::PathValidation),
+5022            now + 3 * cmp::max(self.pto(SpaceId::Data, path_id), prev_pto),
+5023            self.qlog.with_time(now),
+5024        );
+5025    }
+5026
+5027    /// Handle a change in the local address, i.e. an active migration
+5028    pub fn local_address_changed(&mut self) {
+5029        // TODO(flub): if multipath is enabled this needs to create a new path entirely.
+5030        self.update_rem_cid(PathId::ZERO);
+5031        self.ping();
+5032    }
+5033
+5034    /// Switch to a previously unused remote connection ID, if possible
+5035    fn update_rem_cid(&mut self, path_id: PathId) {
+5036        let Some((reset_token, retired)) =
+5037            self.rem_cids.get_mut(&path_id).and_then(|cids| cids.next())
+5038        else {
+5039            return;
+5040        };
+5041
+5042        // Retire the current remote CID and any CIDs we had to skip.
+5043        self.spaces[SpaceId::Data]
+5044            .pending
+5045            .retire_cids
+5046            .extend(retired.map(|seq| (path_id, seq)));
+5047        let remote = self.path_data(path_id).network_path.remote;
+5048        self.set_reset_token(path_id, remote, reset_token);
+5049    }
+5050
+5051    /// Sends this reset token to the endpoint
+5052    ///
+5053    /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer
+5054    /// sends a reset token it knows to route it to this connection. See RFC 9000 section
+5055    /// 10.3. Stateless Reset.
+5056    ///
+5057    /// Reset tokens are different for each path, the endpoint identifies paths by peer
+5058    /// socket address however, not by path ID.
+5059    fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) {
+5060        self.endpoint_events
+5061            .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token));
+5062
+5063        // During the handshake the server sends a reset token in the transport
+5064        // parameters. When we are the client and we receive the reset token during the
+5065        // handshake we want this to affect our peer transport parameters.
+5066        // TODO(flub): Pretty sure this is pointless, the entire params is overwritten
+5067        //    shortly after this was called.  And then the params don't have this anymore.
+5068        if path_id == PathId::ZERO {
+5069            self.peer_params.stateless_reset_token = Some(reset_token);
+5070        }
+5071    }
+5072
+5073    /// Issue an initial set of connection IDs to the peer upon connection
+5074    fn issue_first_cids(&mut self, now: Instant) {
+5075        if self
+5076            .local_cid_state
+5077            .get(&PathId::ZERO)
+5078            .expect("PathId::ZERO exists when the connection is created")
+5079            .cid_len()
+5080            == 0
+5081        {
+5082            return;
+5083        }
+5084
+5085        // Subtract 1 to account for the CID we supplied while handshaking
+5086        let mut n = self.peer_params.issue_cids_limit() - 1;
+5087        if let ConnectionSide::Server { server_config } = &self.side {
+5088            if server_config.has_preferred_address() {
+5089                // We also sent a CID in the transport parameters
+5090                n -= 1;
+5091            }
+5092        }
+5093        self.endpoint_events
+5094            .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n));
+5095    }
+5096
+5097    /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued
+5098    ///
+5099    /// Later CIDs are issued when CIDs expire or are retired by the peer.
+5100    fn issue_first_path_cids(&mut self, now: Instant) {
+5101        if let Some(max_path_id) = self.max_path_id() {
+5102            let mut path_id = self.max_path_id_with_cids.next();
+5103            while path_id <= max_path_id {
+5104                self.endpoint_events
+5105                    .push_back(EndpointEventInner::NeedIdentifiers(
+5106                        path_id,
+5107                        now,
+5108                        self.peer_params.issue_cids_limit(),
+5109                    ));
+5110                path_id = path_id.next();
+5111            }
+5112            self.max_path_id_with_cids = max_path_id;
+5113        }
+5114    }
+5115
+5116    /// Populates a packet with frames
+5117    ///
+5118    /// This tries to fit as many frames as possible into the packet.
+5119    ///
+5120    /// *path_exclusive_only* means to only build frames which can only be sent on this
+5121    /// *path.  This is used in multipath for backup paths while there is still an active
+5122    /// *path.
+5123    fn populate_packet(
+5124        &mut self,
+5125        now: Instant,
+5126        space_id: SpaceId,
+5127        path_id: PathId,
+5128        path_exclusive_only: bool,
+5129        buf: &mut impl BufMut,
+5130        pn: u64,
+5131        #[allow(unused)] qlog: &mut QlogSentPacket,
+5132    ) -> SentFrames {
+5133        let mut sent = SentFrames::default();
+5134        let is_multipath_negotiated = self.is_multipath_negotiated();
+5135        let space = &mut self.spaces[space_id];
+5136        let path = &mut self.paths.get_mut(&path_id).expect("known path").data;
+5137        let is_0rtt = space_id == SpaceId::Data && space.crypto.is_none();
+5138        space
+5139            .for_path(path_id)
+5140            .pending_acks
+5141            .maybe_ack_non_eliciting();
+5142
+5143        // HANDSHAKE_DONE
+5144        if !is_0rtt && mem::replace(&mut space.pending.handshake_done, false) {
+5145            trace!("HANDSHAKE_DONE");
+5146            buf.write(frame::FrameType::HandshakeDone);
+5147            qlog.frame(&Frame::HandshakeDone);
+5148            sent.retransmits.get_or_create().handshake_done = true;
+5149            // This is just a u8 counter and the frame is typically just sent once
+5150            self.stats.frame_tx.handshake_done =
+5151                self.stats.frame_tx.handshake_done.saturating_add(1);
+5152        }
+5153
+5154        // REACH_OUT
+5155        // TODO(@divma): path explusive considerations
+5156        if let Some((round, addresses)) = space.pending.reach_out.as_mut() {
+5157            while let Some(local_addr) = addresses.pop() {
+5158                let reach_out = frame::ReachOut::new(*round, local_addr);
+5159                if buf.remaining_mut() > reach_out.size() {
+5160                    trace!(%round, ?local_addr, "REACH_OUT");
+5161                    reach_out.encode(buf);
+5162                    let sent_reachouts = sent
+5163                        .retransmits
+5164                        .get_or_create()
+5165                        .reach_out
+5166                        .get_or_insert_with(|| (*round, Default::default()));
+5167                    sent_reachouts.1.push(local_addr);
+5168                    self.stats.frame_tx.reach_out = self.stats.frame_tx.reach_out.saturating_add(1);
+5169                    qlog.frame(&Frame::ReachOut(reach_out));
+5170                } else {
+5171                    addresses.push(local_addr);
+5172                    break;
+5173                }
+5174            }
+5175            if addresses.is_empty() {
+5176                space.pending.reach_out = None;
+5177            }
+5178        }
+5179
+5180        // OBSERVED_ADDR
+5181        if !path_exclusive_only
+5182            && space_id == SpaceId::Data
+5183            && self
+5184                .config
+5185                .address_discovery_role
+5186                .should_report(&self.peer_params.address_discovery_role)
+5187            && (!path.observed_addr_sent || space.pending.observed_addr)
+5188        {
+5189            let frame =
+5190                frame::ObservedAddr::new(path.network_path.remote, self.next_observed_addr_seq_no);
+5191            if buf.remaining_mut() > frame.size() {
+5192                trace!(seq = %frame.seq_no, ip = %frame.ip, port = frame.port, "OBSERVED_ADDRESS");
+5193                frame.encode(buf);
+5194
+5195                self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8);
+5196                path.observed_addr_sent = true;
+5197
+5198                self.stats.frame_tx.observed_addr += 1;
+5199                sent.retransmits.get_or_create().observed_addr = true;
+5200                space.pending.observed_addr = false;
+5201                qlog.frame(&Frame::ObservedAddr(frame));
+5202            }
+5203        }
+5204
+5205        // PING
+5206        if mem::replace(&mut space.for_path(path_id).ping_pending, false) {
+5207            trace!("PING");
+5208            buf.write(frame::FrameType::Ping);
+5209            sent.non_retransmits = true;
+5210            self.stats.frame_tx.ping += 1;
+5211            qlog.frame(&Frame::Ping);
+5212        }
+5213
+5214        // IMMEDIATE_ACK
+5215        if mem::replace(&mut space.for_path(path_id).immediate_ack_pending, false) {
+5216            debug_assert_eq!(
+5217                space_id,
+5218                SpaceId::Data,
+5219                "immediate acks must be sent in the data space"
+5220            );
+5221            trace!("IMMEDIATE_ACK");
+5222            buf.write(frame::FrameType::ImmediateAck);
+5223            sent.non_retransmits = true;
+5224            self.stats.frame_tx.immediate_ack += 1;
+5225            qlog.frame(&Frame::ImmediateAck);
+5226        }
+5227
+5228        // ACK
+5229        // TODO(flub): Should this send acks for this path anyway?
+5230
+5231        if !path_exclusive_only {
+5232            for path_id in space
+5233                .number_spaces
+5234                .iter_mut()
+5235                .filter(|(_, pns)| pns.pending_acks.can_send())
+5236                .map(|(&path_id, _)| path_id)
+5237                .collect::<Vec<_>>()
+5238            {
+5239                Self::populate_acks(
+5240                    now,
+5241                    self.receiving_ecn,
+5242                    &mut sent,
+5243                    path_id,
+5244                    space_id,
+5245                    space,
+5246                    is_multipath_negotiated,
+5247                    buf,
+5248                    &mut self.stats,
+5249                    qlog,
+5250                );
+5251            }
+5252        }
+5253
+5254        // ACK_FREQUENCY
+5255        if !path_exclusive_only && mem::replace(&mut space.pending.ack_frequency, false) {
+5256            let sequence_number = self.ack_frequency.next_sequence_number();
+5257
+5258            // Safe to unwrap because this is always provided when ACK frequency is enabled
+5259            let config = self.config.ack_frequency_config.as_ref().unwrap();
+5260
+5261            // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error
+5262            let max_ack_delay = self.ack_frequency.candidate_max_ack_delay(
+5263                path.rtt.get(),
+5264                config,
+5265                &self.peer_params,
+5266            );
+5267
+5268            trace!(?max_ack_delay, "ACK_FREQUENCY");
+5269
+5270            let frame = frame::AckFrequency {
+5271                sequence: sequence_number,
+5272                ack_eliciting_threshold: config.ack_eliciting_threshold,
+5273                request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX),
+5274                reordering_threshold: config.reordering_threshold,
+5275            };
+5276            frame.encode(buf);
+5277            qlog.frame(&Frame::AckFrequency(frame));
+5278
+5279            sent.retransmits.get_or_create().ack_frequency = true;
+5280
+5281            self.ack_frequency
+5282                .ack_frequency_sent(path_id, pn, max_ack_delay);
+5283            self.stats.frame_tx.ack_frequency += 1;
+5284        }
+5285
+5286        // PATH_CHALLENGE
+5287        if buf.remaining_mut() > frame::PathChallenge::SIZE_BOUND
+5288            && space_id == SpaceId::Data
+5289            && path.send_new_challenge
+5290            && !self.state.is_closed()
+5291        // we don't want to send new challenges if we are already closing
+5292        {
+5293            path.send_new_challenge = false;
+5294
+5295            // Generate a new challenge every time we send a new PATH_CHALLENGE
+5296            let token = self.rng.random();
+5297            let info = paths::SentChallengeInfo {
+5298                sent_instant: now,
+5299                network_path: path.network_path,
+5300            };
+5301            path.challenges_sent.insert(token, info);
+5302            sent.non_retransmits = true;
+5303            sent.requires_padding = true;
+5304            let challenge = frame::PathChallenge(token);
+5305            trace!(frame = %challenge);
+5306            buf.write(challenge);
+5307            qlog.frame(&Frame::PathChallenge(challenge));
+5308            self.stats.frame_tx.path_challenge += 1;
+5309            let pto = self.ack_frequency.max_ack_delay_for_pto() + path.rtt.pto_base();
+5310            self.timers.set(
+5311                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+5312                now + pto,
+5313                self.qlog.with_time(now),
+5314            );
+5315
+5316            if is_multipath_negotiated && !path.validated && path.send_new_challenge {
+5317                // queue informing the path status along with the challenge
+5318                space.pending.path_status.insert(path_id);
+5319            }
+5320
+5321            // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless
+5322            // of whether one has already been sent on this path.
+5323            if space_id == SpaceId::Data
+5324                && self
+5325                    .config
+5326                    .address_discovery_role
+5327                    .should_report(&self.peer_params.address_discovery_role)
+5328            {
+5329                let frame = frame::ObservedAddr::new(
+5330                    path.network_path.remote,
+5331                    self.next_observed_addr_seq_no,
+5332                );
+5333                if buf.remaining_mut() > frame.size() {
+5334                    frame.encode(buf);
+5335                    qlog.frame(&Frame::ObservedAddr(frame));
+5336
+5337                    self.next_observed_addr_seq_no =
+5338                        self.next_observed_addr_seq_no.saturating_add(1u8);
+5339                    path.observed_addr_sent = true;
+5340
+5341                    self.stats.frame_tx.observed_addr += 1;
+5342                    sent.retransmits.get_or_create().observed_addr = true;
+5343                    space.pending.observed_addr = false;
+5344                }
+5345            }
+5346        }
+5347
+5348        // PATH_RESPONSE
+5349        if buf.remaining_mut() > frame::PathResponse::SIZE_BOUND && space_id == SpaceId::Data {
+5350            if let Some(token) = path.path_responses.pop_on_path(path.network_path) {
+5351                sent.non_retransmits = true;
+5352                sent.requires_padding = true;
+5353                let response = frame::PathResponse(token);
+5354                trace!(frame = %response);
+5355                buf.write(response);
+5356                qlog.frame(&Frame::PathResponse(response));
+5357                self.stats.frame_tx.path_response += 1;
+5358
+5359                // NOTE: this is technically not required but might be useful to ride the
+5360                // request/response nature of path challenges to refresh an observation
+5361                // Since PATH_RESPONSE is a probing frame, this is allowed by the spec.
+5362                if space_id == SpaceId::Data
+5363                    && self
+5364                        .config
+5365                        .address_discovery_role
+5366                        .should_report(&self.peer_params.address_discovery_role)
+5367                {
+5368                    let frame = frame::ObservedAddr::new(
+5369                        path.network_path.remote,
+5370                        self.next_observed_addr_seq_no,
+5371                    );
+5372                    if buf.remaining_mut() > frame.size() {
+5373                        frame.encode(buf);
+5374                        qlog.frame(&Frame::ObservedAddr(frame));
+5375
+5376                        self.next_observed_addr_seq_no =
+5377                            self.next_observed_addr_seq_no.saturating_add(1u8);
+5378                        path.observed_addr_sent = true;
+5379
+5380                        self.stats.frame_tx.observed_addr += 1;
+5381                        sent.retransmits.get_or_create().observed_addr = true;
+5382                        space.pending.observed_addr = false;
+5383                    }
+5384                }
+5385            }
+5386        }
+5387
+5388        // CRYPTO
+5389        while !path_exclusive_only && buf.remaining_mut() > frame::Crypto::SIZE_BOUND && !is_0rtt {
+5390            let mut frame = match space.pending.crypto.pop_front() {
+5391                Some(x) => x,
+5392                None => break,
+5393            };
+5394
+5395            // Calculate the maximum amount of crypto data we can store in the buffer.
+5396            // Since the offset is known, we can reserve the exact size required to encode it.
+5397            // For length we reserve 2bytes which allows to encode up to 2^14,
+5398            // which is more than what fits into normally sized QUIC frames.
+5399            let max_crypto_data_size = buf.remaining_mut()
+5400                - 1 // Frame Type
+5401                - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) })
+5402                - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes
+5403
+5404            let len = frame
+5405                .data
+5406                .len()
+5407                .min(2usize.pow(14) - 1)
+5408                .min(max_crypto_data_size);
+5409
+5410            let data = frame.data.split_to(len);
+5411            let truncated = frame::Crypto {
+5412                offset: frame.offset,
+5413                data,
+5414            };
+5415            trace!(
+5416                "CRYPTO: off {} len {}",
+5417                truncated.offset,
+5418                truncated.data.len()
+5419            );
+5420            truncated.encode(buf);
+5421            self.stats.frame_tx.crypto += 1;
+5422
+5423            // The clone is cheap but we still cfg it out if qlog is disabled.
+5424            #[cfg(feature = "qlog")]
+5425            qlog.frame(&Frame::Crypto(truncated.clone()));
+5426            sent.retransmits.get_or_create().crypto.push_back(truncated);
+5427            if !frame.data.is_empty() {
+5428                frame.offset += len as u64;
+5429                space.pending.crypto.push_front(frame);
+5430            }
+5431        }
+5432
+5433        // TODO(flub): maybe this is much higher priority?
+5434        // PATH_ABANDON
+5435        while !path_exclusive_only
+5436            && space_id == SpaceId::Data
+5437            && frame::PathAbandon::SIZE_BOUND <= buf.remaining_mut()
+5438        {
+5439            let Some((path_id, error_code)) = space.pending.path_abandon.pop_first() else {
+5440                break;
+5441            };
+5442            let frame = frame::PathAbandon {
+5443                path_id,
+5444                error_code,
+5445            };
+5446            frame.encode(buf);
+5447            qlog.frame(&Frame::PathAbandon(frame));
+5448            self.stats.frame_tx.path_abandon += 1;
+5449            trace!(%path_id, "PATH_ABANDON");
+5450            sent.retransmits
+5451                .get_or_create()
+5452                .path_abandon
+5453                .entry(path_id)
+5454                .or_insert(error_code);
+5455        }
+5456
+5457        // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP
+5458        while !path_exclusive_only
+5459            && space_id == SpaceId::Data
+5460            && frame::PathStatusAvailable::SIZE_BOUND <= buf.remaining_mut()
+5461        {
+5462            let Some(path_id) = space.pending.path_status.pop_first() else {
+5463                break;
+5464            };
+5465            let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else {
+5466                trace!(%path_id, "discarding queued path status for unknown path");
+5467                continue;
+5468            };
+5469
+5470            let seq = path.status.seq();
+5471            sent.retransmits.get_or_create().path_status.insert(path_id);
+5472            match path.local_status() {
+5473                PathStatus::Available => {
+5474                    let frame = frame::PathStatusAvailable {
+5475                        path_id,
+5476                        status_seq_no: seq,
+5477                    };
+5478                    frame.encode(buf);
+5479                    qlog.frame(&Frame::PathStatusAvailable(frame));
+5480                    self.stats.frame_tx.path_status_available += 1;
+5481                    trace!(%path_id, %seq, "PATH_STATUS_AVAILABLE")
+5482                }
+5483                PathStatus::Backup => {
+5484                    let frame = frame::PathStatusBackup {
+5485                        path_id,
+5486                        status_seq_no: seq,
+5487                    };
+5488                    frame.encode(buf);
+5489                    qlog.frame(&Frame::PathStatusBackup(frame));
+5490                    self.stats.frame_tx.path_status_backup += 1;
+5491                    trace!(%path_id, %seq, "PATH_STATUS_BACKUP")
+5492                }
+5493            }
+5494        }
+5495
+5496        // MAX_PATH_ID
+5497        if space_id == SpaceId::Data
+5498            && space.pending.max_path_id
+5499            && frame::MaxPathId::SIZE_BOUND <= buf.remaining_mut()
+5500        {
+5501            let frame = frame::MaxPathId(self.local_max_path_id);
+5502            frame.encode(buf);
+5503            qlog.frame(&Frame::MaxPathId(frame));
+5504            space.pending.max_path_id = false;
+5505            sent.retransmits.get_or_create().max_path_id = true;
+5506            trace!(val = %self.local_max_path_id, "MAX_PATH_ID");
+5507            self.stats.frame_tx.max_path_id += 1;
+5508        }
+5509
+5510        // PATHS_BLOCKED
+5511        if space_id == SpaceId::Data
+5512            && space.pending.paths_blocked
+5513            && frame::PathsBlocked::SIZE_BOUND <= buf.remaining_mut()
+5514        {
+5515            let frame = frame::PathsBlocked(self.remote_max_path_id);
+5516            frame.encode(buf);
+5517            qlog.frame(&Frame::PathsBlocked(frame));
+5518            space.pending.paths_blocked = false;
+5519            sent.retransmits.get_or_create().paths_blocked = true;
+5520            trace!(max_path_id = %self.remote_max_path_id, "PATHS_BLOCKED");
+5521            self.stats.frame_tx.paths_blocked += 1;
+5522        }
+5523
+5524        // PATH_CIDS_BLOCKED
+5525        while space_id == SpaceId::Data && frame::PathCidsBlocked::SIZE_BOUND <= buf.remaining_mut()
+5526        {
+5527            let Some(path_id) = space.pending.path_cids_blocked.pop() else {
+5528                break;
+5529            };
+5530            let next_seq = match self.rem_cids.get(&path_id) {
+5531                Some(cid_queue) => cid_queue.active_seq() + 1,
+5532                None => 0,
+5533            };
+5534            let frame = frame::PathCidsBlocked {
+5535                path_id,
+5536                next_seq: VarInt(next_seq),
+5537            };
+5538            frame.encode(buf);
+5539            qlog.frame(&Frame::PathCidsBlocked(frame));
+5540            sent.retransmits
+5541                .get_or_create()
+5542                .path_cids_blocked
+5543                .push(path_id);
+5544            trace!(%path_id, next_seq, "PATH_CIDS_BLOCKED");
+5545            self.stats.frame_tx.path_cids_blocked += 1;
+5546        }
+5547
+5548        // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS
+5549        if space_id == SpaceId::Data {
+5550            self.streams.write_control_frames(
+5551                buf,
+5552                &mut space.pending,
+5553                &mut sent.retransmits,
+5554                &mut self.stats.frame_tx,
+5555                qlog,
+5556            );
+5557        }
+5558
+5559        // NEW_CONNECTION_ID
+5560        let cid_len = self
+5561            .local_cid_state
+5562            .values()
+5563            .map(|cid_state| cid_state.cid_len())
+5564            .max()
+5565            .expect("some local CID state must exist");
+5566        let new_cid_size_bound =
+5567            frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len);
+5568        while !path_exclusive_only && buf.remaining_mut() > new_cid_size_bound {
+5569            let issued = match space.pending.new_cids.pop() {
+5570                Some(x) => x,
+5571                None => break,
+5572            };
+5573            let retire_prior_to = self
+5574                .local_cid_state
+5575                .get(&issued.path_id)
+5576                .map(|cid_state| cid_state.retire_prior_to())
+5577                .unwrap_or_else(|| panic!("missing local CID state for path={}", issued.path_id));
+5578
+5579            let cid_path_id = match is_multipath_negotiated {
+5580                true => {
+5581                    trace!(
+5582                        path_id = ?issued.path_id,
+5583                        sequence = issued.sequence,
+5584                        id = %issued.id,
+5585                        "PATH_NEW_CONNECTION_ID",
+5586                    );
+5587                    self.stats.frame_tx.path_new_connection_id += 1;
+5588                    Some(issued.path_id)
+5589                }
+5590                false => {
+5591                    trace!(
+5592                        sequence = issued.sequence,
+5593                        id = %issued.id,
+5594                        "NEW_CONNECTION_ID"
+5595                    );
+5596                    debug_assert_eq!(issued.path_id, PathId::ZERO);
+5597                    self.stats.frame_tx.new_connection_id += 1;
+5598                    None
+5599                }
+5600            };
+5601            let frame = frame::NewConnectionId {
+5602                path_id: cid_path_id,
+5603                sequence: issued.sequence,
+5604                retire_prior_to,
+5605                id: issued.id,
+5606                reset_token: issued.reset_token,
+5607            };
+5608            frame.encode(buf);
+5609            sent.retransmits.get_or_create().new_cids.push(issued);
+5610            qlog.frame(&Frame::NewConnectionId(frame));
+5611        }
+5612
+5613        // RETIRE_CONNECTION_ID
+5614        let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated);
+5615        while !path_exclusive_only && buf.remaining_mut() > retire_cid_bound {
+5616            let (path_id, sequence) = match space.pending.retire_cids.pop() {
+5617                Some((PathId::ZERO, seq)) if !is_multipath_negotiated => {
+5618                    trace!(sequence = seq, "RETIRE_CONNECTION_ID");
+5619                    self.stats.frame_tx.retire_connection_id += 1;
+5620                    (None, seq)
+5621                }
+5622                Some((path_id, seq)) => {
+5623                    trace!(%path_id, sequence = seq, "PATH_RETIRE_CONNECTION_ID");
+5624                    self.stats.frame_tx.path_retire_connection_id += 1;
+5625                    (Some(path_id), seq)
+5626                }
+5627                None => break,
+5628            };
+5629            let frame = frame::RetireConnectionId { path_id, sequence };
+5630            frame.encode(buf);
+5631            qlog.frame(&Frame::RetireConnectionId(frame));
+5632            sent.retransmits
+5633                .get_or_create()
+5634                .retire_cids
+5635                .push((path_id.unwrap_or_default(), sequence));
+5636        }
+5637
+5638        // DATAGRAM
+5639        let mut sent_datagrams = false;
+5640        while !path_exclusive_only
+5641            && buf.remaining_mut() > Datagram::SIZE_BOUND
+5642            && space_id == SpaceId::Data
+5643        {
+5644            let prev_remaining = buf.remaining_mut();
+5645            match self.datagrams.write(buf) {
+5646                true => {
+5647                    sent_datagrams = true;
+5648                    sent.non_retransmits = true;
+5649                    self.stats.frame_tx.datagram += 1;
+5650                    qlog.frame_datagram((prev_remaining - buf.remaining_mut()) as u64);
+5651                }
+5652                false => break,
+5653            }
+5654        }
+5655        if self.datagrams.send_blocked && sent_datagrams {
+5656            self.events.push_back(Event::DatagramsUnblocked);
+5657            self.datagrams.send_blocked = false;
+5658        }
+5659
+5660        let path = &mut self.paths.get_mut(&path_id).expect("known path").data;
+5661
+5662        // NEW_TOKEN
+5663        while let Some(network_path) = space.pending.new_tokens.pop() {
+5664            if path_exclusive_only {
+5665                break;
+5666            }
+5667            debug_assert_eq!(space_id, SpaceId::Data);
+5668            let ConnectionSide::Server { server_config } = &self.side else {
+5669                panic!("NEW_TOKEN frames should not be enqueued by clients");
+5670            };
+5671
+5672            if !network_path.is_probably_same_path(&path.network_path) {
+5673                // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only
+5674                // useful if used from the same IP address.  Thus, we abandon enqueued NEW_TOKEN
+5675                // frames upon an path change. Instead, when the new path becomes validated,
+5676                // NEW_TOKEN frames may be enqueued for the new path instead.
+5677                continue;
+5678            }
+5679
+5680            let token = Token::new(
+5681                TokenPayload::Validation {
+5682                    ip: network_path.remote.ip(),
+5683                    issued: server_config.time_source.now(),
+5684                },
+5685                &mut self.rng,
+5686            );
+5687            let new_token = NewToken {
+5688                token: token.encode(&*server_config.token_key).into(),
+5689            };
+5690
+5691            if buf.remaining_mut() < new_token.size() {
+5692                space.pending.new_tokens.push(network_path);
+5693                break;
+5694            }
+5695
+5696            trace!("NEW_TOKEN");
+5697            new_token.encode(buf);
+5698            qlog.frame(&Frame::NewToken(new_token));
+5699            sent.retransmits
+5700                .get_or_create()
+5701                .new_tokens
+5702                .push(network_path);
+5703            self.stats.frame_tx.new_token += 1;
+5704        }
+5705
+5706        // STREAM
+5707        if !path_exclusive_only && space_id == SpaceId::Data {
+5708            sent.stream_frames =
+5709                self.streams
+5710                    .write_stream_frames(buf, self.config.send_fairness, qlog);
+5711            self.stats.frame_tx.stream += sent.stream_frames.len() as u64;
+5712        }
+5713
+5714        // ADD_ADDRESS
+5715        // TODO(@divma): check if we need to do path exclusive filters
+5716        while space_id == SpaceId::Data && frame::AddAddress::SIZE_BOUND <= buf.remaining_mut() {
+5717            if let Some(added_address) = space.pending.add_address.pop_last() {
+5718                trace!(
+5719                    seq = %added_address.seq_no,
+5720                    ip = ?added_address.ip,
+5721                    port = added_address.port,
+5722                    "ADD_ADDRESS",
+5723                );
+5724                added_address.encode(buf);
+5725                sent.retransmits
+5726                    .get_or_create()
+5727                    .add_address
+5728                    .insert(added_address);
+5729                self.stats.frame_tx.add_address = self.stats.frame_tx.add_address.saturating_add(1);
+5730                qlog.frame(&Frame::AddAddress(added_address));
+5731            } else {
+5732                break;
+5733            }
+5734        }
+5735
+5736        // REMOVE_ADDRESS
+5737        while space_id == SpaceId::Data && frame::RemoveAddress::SIZE_BOUND <= buf.remaining_mut() {
+5738            if let Some(removed_address) = space.pending.remove_address.pop_last() {
+5739                trace!(seq = %removed_address.seq_no, "REMOVE_ADDRESS");
+5740                removed_address.encode(buf);
+5741                sent.retransmits
+5742                    .get_or_create()
+5743                    .remove_address
+5744                    .insert(removed_address);
+5745                self.stats.frame_tx.remove_address =
+5746                    self.stats.frame_tx.remove_address.saturating_add(1);
+5747                qlog.frame(&Frame::RemoveAddress(removed_address));
+5748            } else {
+5749                break;
+5750            }
+5751        }
+5752
+5753        sent
+5754    }
+5755
+5756    /// Write pending ACKs into a buffer
+5757    fn populate_acks(
+5758        now: Instant,
+5759        receiving_ecn: bool,
+5760        sent: &mut SentFrames,
+5761        path_id: PathId,
+5762        space_id: SpaceId,
+5763        space: &mut PacketSpace,
+5764        is_multipath_negotiated: bool,
+5765        buf: &mut impl BufMut,
+5766        stats: &mut ConnectionStats,
+5767        #[allow(unused)] qlog: &mut QlogSentPacket,
+5768    ) {
+5769        // 0-RTT packets must never carry acks (which would have to be of handshake packets)
+5770        debug_assert!(space.crypto.is_some(), "tried to send ACK in 0-RTT");
+5771
+5772        debug_assert!(
+5773            is_multipath_negotiated || path_id == PathId::ZERO,
+5774            "Only PathId::ZERO allowed without multipath (have {path_id:?})"
+5775        );
+5776        if is_multipath_negotiated {
+5777            debug_assert!(
+5778                space_id == SpaceId::Data || path_id == PathId::ZERO,
+5779                "path acks must be sent in 1RTT space (have {space_id:?})"
+5780            );
+5781        }
+5782
+5783        let pns = space.for_path(path_id);
+5784        let ranges = pns.pending_acks.ranges();
+5785        debug_assert!(!ranges.is_empty(), "can not send empty ACK range");
+5786        let ecn = if receiving_ecn {
+5787            Some(&pns.ecn_counters)
+5788        } else {
+5789            None
+5790        };
+5791        if let Some(max) = ranges.max() {
+5792            sent.largest_acked.insert(path_id, max);
+5793        }
+5794
+5795        let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64;
+5796        // TODO: This should come from `TransportConfig` if that gets configurable.
+5797        let ack_delay_exp = TransportParameters::default().ack_delay_exponent;
+5798        let delay = delay_micros >> ack_delay_exp.into_inner();
+5799
+5800        if is_multipath_negotiated && space_id == SpaceId::Data {
+5801            if !ranges.is_empty() {
+5802                trace!("PATH_ACK {path_id:?} {ranges:?}, Delay = {delay_micros}us");
+5803                frame::PathAck::encoder(path_id, delay as _, ranges, ecn).encode(buf);
+5804                qlog.frame_path_ack(path_id, delay as _, ranges, ecn);
+5805                stats.frame_tx.path_acks += 1;
+5806            }
+5807        } else {
+5808            trace!("ACK {ranges:?}, Delay = {delay_micros}us");
+5809            frame::Ack::encoder(delay as _, ranges, ecn).encode(buf);
+5810            stats.frame_tx.acks += 1;
+5811            qlog.frame_ack(delay, ranges, ecn);
+5812        }
+5813    }
+5814
+5815    fn close_common(&mut self) {
+5816        trace!("connection closed");
+5817        self.timers.reset();
+5818    }
+5819
+5820    fn set_close_timer(&mut self, now: Instant) {
+5821        // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO with PTO the max of
+5822        // the PTO for all paths.
+5823        let pto_max = self.pto_max_path(self.highest_space, true);
+5824        self.timers.set(
+5825            Timer::Conn(ConnTimer::Close),
+5826            now + 3 * pto_max,
+5827            self.qlog.with_time(now),
+5828        );
+5829    }
+5830
+5831    /// Handle transport parameters received from the peer
+5832    ///
+5833    /// *rem_cid* and *loc_cid* are the source and destination CIDs respectively of the
+5834    /// *packet into which the transport parameters arrived.
+5835    fn handle_peer_params(
+5836        &mut self,
+5837        params: TransportParameters,
+5838        loc_cid: ConnectionId,
+5839        rem_cid: ConnectionId,
+5840        now: Instant,
+5841    ) -> Result<(), TransportError> {
+5842        if Some(self.orig_rem_cid) != params.initial_src_cid
+5843            || (self.side.is_client()
+5844                && (Some(self.initial_dst_cid) != params.original_dst_cid
+5845                    || self.retry_src_cid != params.retry_src_cid))
+5846        {
+5847            return Err(TransportError::TRANSPORT_PARAMETER_ERROR(
+5848                "CID authentication failure",
+5849            ));
+5850        }
+5851        if params.initial_max_path_id.is_some() && (loc_cid.is_empty() || rem_cid.is_empty()) {
+5852            return Err(TransportError::PROTOCOL_VIOLATION(
+5853                "multipath must not use zero-length CIDs",
+5854            ));
+5855        }
+5856
+5857        self.set_peer_params(params);
+5858        self.qlog.emit_peer_transport_params_received(self, now);
+5859
+5860        Ok(())
+5861    }
+5862
+5863    fn set_peer_params(&mut self, params: TransportParameters) {
+5864        self.streams.set_params(&params);
+5865        self.idle_timeout =
+5866            negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout));
+5867        trace!("negotiated max idle timeout {:?}", self.idle_timeout);
+5868
+5869        if let Some(ref info) = params.preferred_address {
+5870            // During the handshake PathId::ZERO exists.
+5871            self.rem_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId {
+5872                path_id: None,
+5873                sequence: 1,
+5874                id: info.connection_id,
+5875                reset_token: info.stateless_reset_token,
+5876                retire_prior_to: 0,
+5877            })
+5878            .expect(
+5879                "preferred address CID is the first received, and hence is guaranteed to be legal",
+5880            );
+5881            let remote = self.path_data(PathId::ZERO).network_path.remote;
+5882            self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token);
+5883        }
+5884        self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params);
+5885
+5886        let mut multipath_enabled = None;
+5887        if let (Some(local_max_path_id), Some(remote_max_path_id)) = (
+5888            self.config.get_initial_max_path_id(),
+5889            params.initial_max_path_id,
+5890        ) {
+5891            // multipath is enabled, register the local and remote maximums
+5892            self.local_max_path_id = local_max_path_id;
+5893            self.remote_max_path_id = remote_max_path_id;
+5894            let initial_max_path_id = local_max_path_id.min(remote_max_path_id);
+5895            debug!(%initial_max_path_id, "multipath negotiated");
+5896            multipath_enabled = Some(initial_max_path_id);
+5897        }
+5898
+5899        if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) =
+5900            self.config
+5901                .max_remote_nat_traversal_addresses
+5902                .zip(params.max_remote_nat_traversal_addresses)
+5903        {
+5904            if let Some(max_initial_paths) =
+5905                multipath_enabled.map(|path_id| path_id.saturating_add(1u8))
+5906            {
+5907                let max_local_addresses = max_remotely_allowed_remote_addresses.get();
+5908                let max_remote_addresses = max_locally_allowed_remote_addresses.get();
+5909                self.iroh_hp =
+5910                    iroh_hp::State::new(max_remote_addresses, max_local_addresses, self.side());
+5911                debug!(
+5912                    %max_remote_addresses, %max_local_addresses,
+5913                    "iroh hole punching negotiated"
+5914                );
+5915
+5916                match self.side() {
+5917                    Side::Client => {
+5918                        if max_initial_paths.as_u32() < max_remote_addresses as u32 + 1 {
+5919                            // in this case the client might try to open `max_remote_addresses` new
+5920                            // paths, but the current multipath configuration will not allow it
+5921                            warn!(%max_initial_paths, %max_remote_addresses, "local client configuration might cause nat traversal issues")
+5922                        } else if max_local_addresses as u64
+5923                            > params.active_connection_id_limit.into_inner()
+5924                        {
+5925                            // the server allows us to send at most `params.active_connection_id_limit`
+5926                            // but they might need at least `max_local_addresses` to effectively send
+5927                            // `PATH_CHALLENGE` frames to each advertised local address
+5928                            warn!(%max_local_addresses, remote_cid_limit=%params.active_connection_id_limit.into_inner(), "remote server configuration might cause nat traversal issues")
+5929                        }
+5930                    }
+5931                    Side::Server => {
+5932                        if (max_initial_paths.as_u32() as u64) < crate::LOC_CID_COUNT {
+5933                            warn!(%max_initial_paths, local_cid_limit=%crate::LOC_CID_COUNT, "local server configuration might cause nat traversal issues")
+5934                        }
+5935                    }
+5936                }
+5937            } else {
+5938                debug!("iroh nat traversal enabled for both endpoints, but multipath is missing")
+5939            }
+5940        }
+5941
+5942        self.peer_params = params;
+5943        let peer_max_udp_payload_size =
+5944            u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX);
+5945        self.path_data_mut(PathId::ZERO)
+5946            .mtud
+5947            .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size);
+5948    }
+5949
+5950    /// Decrypts a packet, returning the packet number on success
+5951    fn decrypt_packet(
+5952        &mut self,
+5953        now: Instant,
+5954        path_id: PathId,
+5955        packet: &mut Packet,
+5956    ) -> Result<Option<u64>, Option<TransportError>> {
+5957        let result = packet_crypto::decrypt_packet_body(
+5958            packet,
+5959            path_id,
+5960            &self.spaces,
+5961            self.zero_rtt_crypto.as_ref(),
+5962            self.key_phase,
+5963            self.prev_crypto.as_ref(),
+5964            self.next_crypto.as_ref(),
+5965        )?;
+5966
+5967        let result = match result {
+5968            Some(r) => r,
+5969            None => return Ok(None),
+5970        };
+5971
+5972        if result.outgoing_key_update_acked {
+5973            if let Some(prev) = self.prev_crypto.as_mut() {
+5974                prev.end_packet = Some((result.number, now));
+5975                self.set_key_discard_timer(now, packet.header.space());
+5976            }
+5977        }
+5978
+5979        if result.incoming_key_update {
+5980            trace!("key update authenticated");
+5981            self.update_keys(Some((result.number, now)), true);
+5982            self.set_key_discard_timer(now, packet.header.space());
+5983        }
+5984
+5985        Ok(Some(result.number))
+5986    }
+5987
+5988    fn update_keys(&mut self, end_packet: Option<(u64, Instant)>, remote: bool) {
+5989        trace!("executing key update");
+5990        // Generate keys for the key phase after the one we're switching to, store them in
+5991        // `next_crypto`, make the contents of `next_crypto` current, and move the current keys into
+5992        // `prev_crypto`.
+5993        let new = self
+5994            .crypto
+5995            .next_1rtt_keys()
+5996            .expect("only called for `Data` packets");
+5997        self.key_phase_size = new
+5998            .local
+5999            .confidentiality_limit()
+6000            .saturating_sub(KEY_UPDATE_MARGIN);
+6001        let old = mem::replace(
+6002            &mut self.spaces[SpaceId::Data]
+6003                .crypto
+6004                .as_mut()
+6005                .unwrap() // safe because update_keys() can only be triggered by short packets
+6006                .packet,
+6007            mem::replace(self.next_crypto.as_mut().unwrap(), new),
+6008        );
+6009        self.spaces[SpaceId::Data]
+6010            .iter_paths_mut()
+6011            .for_each(|s| s.sent_with_keys = 0);
+6012        self.prev_crypto = Some(PrevCrypto {
+6013            crypto: old,
+6014            end_packet,
+6015            update_unacked: remote,
+6016        });
+6017        self.key_phase = !self.key_phase;
+6018    }
+6019
+6020    fn peer_supports_ack_frequency(&self) -> bool {
+6021        self.peer_params.min_ack_delay.is_some()
+6022    }
+6023
+6024    /// Send an IMMEDIATE_ACK frame to the remote endpoint
+6025    ///
+6026    /// According to the spec, this will result in an error if the remote endpoint does not support
+6027    /// the Acknowledgement Frequency extension
+6028    pub(crate) fn immediate_ack(&mut self, path_id: PathId) {
+6029        debug_assert_eq!(
+6030            self.highest_space,
+6031            SpaceId::Data,
+6032            "immediate ack must be written in the data space"
+6033        );
+6034        self.spaces[self.highest_space]
+6035            .for_path(path_id)
+6036            .immediate_ack_pending = true;
+6037    }
+6038
+6039    /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests
+6040    #[cfg(test)]
+6041    pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> {
+6042        let (path_id, first_decode, remaining) = match &event.0 {
+6043            ConnectionEventInner::Datagram(DatagramConnectionEvent {
+6044                path_id,
+6045                first_decode,
+6046                remaining,
+6047                ..
+6048            }) => (path_id, first_decode, remaining),
+6049            _ => return None,
+6050        };
+6051
+6052        if remaining.is_some() {
+6053            panic!("Packets should never be coalesced in tests");
+6054        }
+6055
+6056        let decrypted_header = packet_crypto::unprotect_header(
+6057            first_decode.clone(),
+6058            &self.spaces,
+6059            self.zero_rtt_crypto.as_ref(),
+6060            self.peer_params.stateless_reset_token,
+6061        )?;
+6062
+6063        let mut packet = decrypted_header.packet?;
+6064        packet_crypto::decrypt_packet_body(
+6065            &mut packet,
+6066            *path_id,
+6067            &self.spaces,
+6068            self.zero_rtt_crypto.as_ref(),
+6069            self.key_phase,
+6070            self.prev_crypto.as_ref(),
+6071            self.next_crypto.as_ref(),
+6072        )
+6073        .ok()?;
+6074
+6075        Some(packet.payload.to_vec())
+6076    }
+6077
+6078    /// The number of bytes of packets containing retransmittable frames that have not been
+6079    /// acknowledged or declared lost.
+6080    #[cfg(test)]
+6081    pub(crate) fn bytes_in_flight(&self) -> u64 {
+6082        // TODO(@divma): consider including for multipath?
+6083        self.path_data(PathId::ZERO).in_flight.bytes
+6084    }
+6085
+6086    /// Number of bytes worth of non-ack-only packets that may be sent
+6087    #[cfg(test)]
+6088    pub(crate) fn congestion_window(&self) -> u64 {
+6089        let path = self.path_data(PathId::ZERO);
+6090        path.congestion
+6091            .window()
+6092            .saturating_sub(path.in_flight.bytes)
+6093    }
+6094
+6095    /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running
+6096    #[cfg(test)]
+6097    pub(crate) fn is_idle(&self) -> bool {
+6098        let current_timers = self.timers.values();
+6099        current_timers
+6100            .into_iter()
+6101            .filter(|(timer, _)| {
+6102                !matches!(
+6103                    timer,
+6104                    Timer::Conn(ConnTimer::KeepAlive)
+6105                        | Timer::PerPath(_, PathTimer::PathKeepAlive)
+6106                        | Timer::Conn(ConnTimer::PushNewCid)
+6107                        | Timer::Conn(ConnTimer::KeyDiscard)
+6108                )
+6109            })
+6110            .min_by_key(|(_, time)| *time)
+6111            .is_none_or(|(timer, _)| timer == Timer::Conn(ConnTimer::Idle))
+6112    }
+6113
+6114    /// Whether explicit congestion notification is in use on outgoing packets.
+6115    #[cfg(test)]
+6116    pub(crate) fn using_ecn(&self) -> bool {
+6117        self.path_data(PathId::ZERO).sending_ecn
+6118    }
+6119
+6120    /// The number of received bytes in the current path
+6121    #[cfg(test)]
+6122    pub(crate) fn total_recvd(&self) -> u64 {
+6123        self.path_data(PathId::ZERO).total_recvd
+6124    }
+6125
+6126    #[cfg(test)]
+6127    pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) {
+6128        self.local_cid_state
+6129            .get(&PathId::ZERO)
+6130            .unwrap()
+6131            .active_seq()
+6132    }
+6133
+6134    #[cfg(test)]
+6135    #[track_caller]
+6136    pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) {
+6137        self.local_cid_state
+6138            .get(&PathId(path_id))
+6139            .unwrap()
+6140            .active_seq()
+6141    }
+6142
+6143    /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame
+6144    /// with updated `retire_prior_to` field set to `v`
+6145    #[cfg(test)]
+6146    pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) {
+6147        let n = self
+6148            .local_cid_state
+6149            .get_mut(&PathId::ZERO)
+6150            .unwrap()
+6151            .assign_retire_seq(v);
+6152        self.endpoint_events
+6153            .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n));
+6154    }
+6155
+6156    /// Check the current active remote CID sequence for `PathId::ZERO`
+6157    #[cfg(test)]
+6158    pub(crate) fn active_rem_cid_seq(&self) -> u64 {
+6159        self.rem_cids.get(&PathId::ZERO).unwrap().active_seq()
+6160    }
+6161
+6162    /// Returns the detected maximum udp payload size for the current path
+6163    #[cfg(test)]
+6164    pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 {
+6165        self.path_data(path_id).current_mtu()
+6166    }
+6167
+6168    /// Triggers path validation on all paths
+6169    #[cfg(test)]
+6170    pub(crate) fn trigger_path_validation(&mut self) {
+6171        for path in self.paths.values_mut() {
+6172            path.data.send_new_challenge = true;
+6173        }
+6174    }
+6175
+6176    /// Whether we have 1-RTT data to send
+6177    ///
+6178    /// This checks for frames that can only be sent in the data space (1-RTT):
+6179    /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated.
+6180    /// - Pending PATH_RESPONSE frames.
+6181    /// - Pending data to send in STREAM frames.
+6182    /// - Pending DATAGRAM frames to send.
+6183    ///
+6184    /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that
+6185    /// may need to be sent.
+6186    fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames {
+6187        let path_exclusive = self.paths.get(&path_id).is_some_and(|path| {
+6188            path.data.send_new_challenge
+6189                || path
+6190                    .prev
+6191                    .as_ref()
+6192                    .is_some_and(|(_, path)| path.send_new_challenge)
+6193                || !path.data.path_responses.is_empty()
+6194        });
+6195        let other = self.streams.can_send_stream_data()
+6196            || self
+6197                .datagrams
+6198                .outgoing
+6199                .front()
+6200                .is_some_and(|x| x.size(true) <= max_size);
+6201        SendableFrames {
+6202            acks: false,
+6203            other,
+6204            close: false,
+6205            path_exclusive,
+6206        }
+6207    }
+6208
+6209    /// Terminate the connection instantly, without sending a close packet
+6210    fn kill(&mut self, reason: ConnectionError) {
+6211        self.close_common();
+6212        self.state.move_to_drained(Some(reason));
+6213        self.endpoint_events.push_back(EndpointEventInner::Drained);
+6214    }
+6215
+6216    /// Storage size required for the largest packet that can be transmitted on all currently
+6217    /// available paths
+6218    ///
+6219    /// Buffers passed to [`Connection::poll_transmit`] should be at least this large.
+6220    ///
+6221    /// When multipath is enabled, this value is the minimum MTU across all available paths.
+6222    pub fn current_mtu(&self) -> u16 {
+6223        self.paths
+6224            .iter()
+6225            .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id))
+6226            .map(|(_path_id, path_state)| path_state.data.current_mtu())
+6227            .min()
+6228            .expect("There is always at least one available path")
+6229    }
+6230
+6231    /// Size of non-frame data for a 1-RTT packet
+6232    ///
+6233    /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are
+6234    /// frames. Changes if the length of the remote connection ID changes, which is expected to be
+6235    /// rare. If `pn` is specified, may additionally change unpredictably due to variations in
+6236    /// latency and packet loss.
+6237    fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize {
+6238        let pn_len = PacketNumber::new(
+6239            pn,
+6240            self.spaces[SpaceId::Data]
+6241                .for_path(path)
+6242                .largest_acked_packet
+6243                .unwrap_or(0),
+6244        )
+6245        .len();
+6246
+6247        // 1 byte for flags
+6248        1 + self
+6249            .rem_cids
+6250            .get(&path)
+6251            .map(|cids| cids.active().len())
+6252            .unwrap_or(20)      // Max CID len in QUIC v1
+6253            + pn_len
+6254            + self.tag_len_1rtt()
+6255    }
+6256
+6257    fn predict_1rtt_overhead_no_pn(&self) -> usize {
+6258        let pn_len = 4;
+6259
+6260        let cid_len = self
+6261            .rem_cids
+6262            .values()
+6263            .map(|cids| cids.active().len())
+6264            .max()
+6265            .unwrap_or(20); // Max CID len in QUIC v1
+6266
+6267        // 1 byte for flags
+6268        1 + cid_len + pn_len + self.tag_len_1rtt()
+6269    }
+6270
+6271    fn tag_len_1rtt(&self) -> usize {
+6272        let key = match self.spaces[SpaceId::Data].crypto.as_ref() {
+6273            Some(crypto) => Some(&*crypto.packet.local),
+6274            None => self.zero_rtt_crypto.as_ref().map(|x| &*x.packet),
+6275        };
+6276        // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of
+6277        // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead,
+6278        // but that would needlessly prevent sending datagrams during 0-RTT.
+6279        key.map_or(16, |x| x.tag_len())
+6280    }
+6281
+6282    /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate
+6283    fn on_path_validated(&mut self, path_id: PathId) {
+6284        self.path_data_mut(path_id).validated = true;
+6285        let ConnectionSide::Server { server_config } = &self.side else {
+6286            return;
+6287        };
+6288        let network_path = self.path_data(path_id).network_path;
+6289        let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens;
+6290        new_tokens.clear();
+6291        for _ in 0..server_config.validation_token.sent {
+6292            new_tokens.push(network_path);
+6293        }
+6294    }
+6295
+6296    /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP
+6297    fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) {
+6298        if let Some(path) = self.paths.get_mut(&path_id) {
+6299            path.data.status.remote_update(status, status_seq_no);
+6300        } else {
+6301            debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id);
+6302        }
+6303        self.events.push_back(
+6304            PathEvent::RemoteStatus {
+6305                id: path_id,
+6306                status,
+6307            }
+6308            .into(),
+6309        );
+6310    }
+6311
+6312    /// Returns the maximum [`PathId`] to be used for sending in this connection.
+6313    ///
+6314    /// This is calculated as minimum between the local and remote's maximums when multipath is
+6315    /// enabled, or `None` when disabled.
+6316    ///
+6317    /// For data that's received, we should use [`Self::local_max_path_id`] instead.
+6318    /// The reasoning is that the remote might already have updated to its own newer
+6319    /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered.
+6320    fn max_path_id(&self) -> Option<PathId> {
+6321        if self.is_multipath_negotiated() {
+6322            Some(self.remote_max_path_id.min(self.local_max_path_id))
+6323        } else {
+6324            None
+6325        }
+6326    }
+6327
+6328    /// Add addresses the local endpoint considers are reachable for nat traversal
+6329    pub fn add_nat_traversal_address(&mut self, address: SocketAddr) -> Result<(), iroh_hp::Error> {
+6330        if let Some(added) = self.iroh_hp.add_local_address(address)? {
+6331            self.spaces[SpaceId::Data].pending.add_address.insert(added);
+6332        };
+6333        Ok(())
+6334    }
+6335
+6336    /// Removes an address the endpoing no longer considers reachable for nat traversal
+6337    ///
+6338    /// Addresses not present in the set will be silently ignored.
+6339    pub fn remove_nat_traversal_address(
+6340        &mut self,
+6341        address: SocketAddr,
+6342    ) -> Result<(), iroh_hp::Error> {
+6343        if let Some(removed) = self.iroh_hp.remove_local_address(address)? {
+6344            self.spaces[SpaceId::Data]
+6345                .pending
+6346                .remove_address
+6347                .insert(removed);
+6348        }
+6349        Ok(())
+6350    }
+6351
+6352    /// Get the current local nat traversal addresses
+6353    pub fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+6354        self.iroh_hp.get_local_nat_traversal_addresses()
+6355    }
+6356
+6357    /// Get the currently advertised nat traversal addresses by the server
+6358    pub fn get_remote_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+6359        Ok(self
+6360            .iroh_hp
+6361            .client_side()?
+6362            .get_remote_nat_traversal_addresses())
+6363    }
+6364
+6365    /// Attempts to open a path for nat traversal.
+6366    ///
+6367    /// `ipv6` indicates if the path should be opened using an IPV6 remote. If the address is
+6368    /// ignored, it will return `None`.
+6369    ///
+6370    /// On success returns the [`PathId`] and remote address of the path, as well as whether the path
+6371    /// existed for the adjusted remote.
+6372    fn open_nat_traversal_path(
+6373        &mut self,
+6374        now: Instant,
+6375        (ip, port): (IpAddr, u16),
+6376        ipv6: bool,
+6377    ) -> Result<Option<(PathId, SocketAddr, bool)>, PathError> {
+6378        // If this endpoint is an IPv6 endpoint we use IPv6 addresses for all remotes.
+6379        let remote = match ip {
+6380            IpAddr::V4(addr) if ipv6 => SocketAddr::new(addr.to_ipv6_mapped().into(), port),
+6381            IpAddr::V4(addr) => SocketAddr::new(addr.into(), port),
+6382            IpAddr::V6(_) if ipv6 => SocketAddr::new(ip, port),
+6383            IpAddr::V6(_) => {
+6384                trace!("not using IPv6 nat candidate for IPv4 socket");
+6385                return Ok(None);
+6386            }
+6387        };
+6388        // TODO(matheus23): Probe the correct 4-tuple, instead of only a remote address?
+6389        // By specifying None, we do two things: 1. open_path_ensure won't generate two
+6390        // paths to the same remote and 2. we let the OS choose which interface to use for
+6391        // sending on that path.
+6392        let network_path = FourTuple {
+6393            remote,
+6394            local_ip: None,
+6395        };
+6396        match self.open_path_ensure(network_path, PathStatus::Backup, now) {
+6397            Ok((path_id, path_was_known)) => {
+6398                if path_was_known {
+6399                    trace!(%path_id, %remote, "nat traversal: path existed for remote");
+6400                }
+6401                Ok(Some((path_id, remote, path_was_known)))
+6402            }
+6403            Err(e) => {
+6404                debug!(%remote, %e, "nat traversal: failed to probe remote");
+6405                Err(e)
+6406            }
+6407        }
+6408    }
+6409
+6410    /// Initiates a new nat traversal round
+6411    ///
+6412    /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT`
+6413    /// frames, and initiating probing of the known remote addresses. When a new round is
+6414    /// initiated, the previous one is cancelled, and paths that have not been opened are closed.
+6415    ///
+6416    /// Returns the server addresses that are now being probed.
+6417    /// If addresses fail due to spurious errors, these might succeed later and not be returned in
+6418    /// this set.
+6419    pub fn initiate_nat_traversal_round(
+6420        &mut self,
+6421        now: Instant,
+6422    ) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+6423        if self.state.is_closed() {
+6424            return Err(iroh_hp::Error::Closed);
+6425        }
+6426
+6427        let client_state = self.iroh_hp.client_side_mut()?;
+6428        let iroh_hp::NatTraversalRound {
+6429            new_round,
+6430            reach_out_at,
+6431            addresses_to_probe,
+6432            prev_round_path_ids,
+6433        } = client_state.initiate_nat_traversal_round()?;
+6434
+6435        self.spaces[SpaceId::Data].pending.reach_out = Some((new_round, reach_out_at));
+6436
+6437        for path_id in prev_round_path_ids {
+6438            // TODO(@divma): this sounds reasonable but we need if this actually works for the
+6439            // purposes of the protocol
+6440            let validated = self
+6441                .path(path_id)
+6442                .map(|path| path.validated)
+6443                .unwrap_or(false);
+6444
+6445            if !validated {
+6446                let _ = self.close_path(
+6447                    now,
+6448                    path_id,
+6449                    TransportErrorCode::APPLICATION_ABANDON_PATH.into(),
+6450                );
+6451            }
+6452        }
+6453
+6454        let mut err = None;
+6455
+6456        let mut path_ids = Vec::with_capacity(addresses_to_probe.len());
+6457        let mut probed_addresses = Vec::with_capacity(addresses_to_probe.len());
+6458        let ipv6 = self
+6459            .paths
+6460            .values()
+6461            .any(|p| p.data.network_path.remote.is_ipv6());
+6462
+6463        for (id, address) in addresses_to_probe {
+6464            match self.open_nat_traversal_path(now, address, ipv6) {
+6465                Ok(None) => {}
+6466                Ok(Some((path_id, remote, path_was_known))) => {
+6467                    if !path_was_known {
+6468                        path_ids.push(path_id);
+6469                        probed_addresses.push(remote);
+6470                    }
+6471                }
+6472                Err(e) => {
+6473                    self.iroh_hp
+6474                        .client_side_mut()
+6475                        .expect("validated")
+6476                        .report_in_continuation(id, e);
+6477                    err.get_or_insert(e);
+6478                }
+6479            }
+6480        }
+6481
+6482        if let Some(err) = err {
+6483            // We failed to probe any addresses, bail out
+6484            if probed_addresses.is_empty() {
+6485                return Err(iroh_hp::Error::Multipath(err));
+6486            }
+6487        }
+6488
+6489        self.iroh_hp
+6490            .client_side_mut()
+6491            .expect("connection side validated")
+6492            .set_round_path_ids(path_ids);
+6493
+6494        Ok(probed_addresses)
+6495    }
+6496
+6497    /// Attempts to continue a nat traversal round by trying to open paths for pending client probes.
+6498    ///
+6499    /// If there was nothing to do, it returns `None`. Otherwise it returns whether the path was
+6500    /// successfully open.
+6501    fn continue_nat_traversal_round(&mut self, now: Instant) -> Option<bool> {
+6502        let client_state = self.iroh_hp.client_side_mut().ok()?;
+6503        let (id, address) = client_state.continue_nat_traversal_round()?;
+6504        let ipv6 = self
+6505            .paths
+6506            .values()
+6507            .any(|p| p.data.network_path.remote.is_ipv6());
+6508        let open_result = self.open_nat_traversal_path(now, address, ipv6);
+6509        let client_state = self.iroh_hp.client_side_mut().expect("validated");
+6510        match open_result {
+6511            Ok(None) => Some(true),
+6512            Ok(Some((path_id, _remote, path_was_known))) => {
+6513                if !path_was_known {
+6514                    client_state.add_round_path_id(path_id);
+6515                }
+6516                Some(true)
+6517            }
+6518            Err(e) => {
+6519                client_state.report_in_continuation(id, e);
+6520                Some(false)
+6521            }
+6522        }
+6523    }
+6524}
+6525
+6526impl fmt::Debug for Connection {
+6527    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+6528        f.debug_struct("Connection")
+6529            .field("handshake_cid", &self.handshake_cid)
+6530            .finish()
+6531    }
+6532}
+6533
+6534#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+6535enum PathBlocked {
+6536    No,
+6537    AntiAmplification,
+6538    Congestion,
+6539    Pacing,
+6540}
+6541
+6542/// Fields of `Connection` specific to it being client-side or server-side
+6543enum ConnectionSide {
+6544    Client {
+6545        /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded
+6546        token: Bytes,
+6547        token_store: Arc<dyn TokenStore>,
+6548        server_name: String,
+6549    },
+6550    Server {
+6551        server_config: Arc<ServerConfig>,
+6552    },
+6553}
+6554
+6555impl ConnectionSide {
+6556    fn remote_may_migrate(&self, state: &State) -> bool {
+6557        match self {
+6558            Self::Server { server_config } => server_config.migration,
+6559            Self::Client { .. } => {
+6560                if let Some(hs) = state.as_handshake() {
+6561                    hs.allow_server_migration
+6562                } else {
+6563                    false
+6564                }
+6565            }
+6566        }
+6567    }
+6568
+6569    fn is_client(&self) -> bool {
+6570        self.side().is_client()
+6571    }
+6572
+6573    fn is_server(&self) -> bool {
+6574        self.side().is_server()
+6575    }
+6576
+6577    fn side(&self) -> Side {
+6578        match *self {
+6579            Self::Client { .. } => Side::Client,
+6580            Self::Server { .. } => Side::Server,
+6581        }
+6582    }
+6583}
+6584
+6585impl From<SideArgs> for ConnectionSide {
+6586    fn from(side: SideArgs) -> Self {
+6587        match side {
+6588            SideArgs::Client {
+6589                token_store,
+6590                server_name,
+6591            } => Self::Client {
+6592                token: token_store.take(&server_name).unwrap_or_default(),
+6593                token_store,
+6594                server_name,
+6595            },
+6596            SideArgs::Server {
+6597                server_config,
+6598                pref_addr_cid: _,
+6599                path_validated: _,
+6600            } => Self::Server { server_config },
+6601        }
+6602    }
+6603}
+6604
+6605/// Parameters to `Connection::new` specific to it being client-side or server-side
+6606pub(crate) enum SideArgs {
+6607    Client {
+6608        token_store: Arc<dyn TokenStore>,
+6609        server_name: String,
+6610    },
+6611    Server {
+6612        server_config: Arc<ServerConfig>,
+6613        pref_addr_cid: Option<ConnectionId>,
+6614        path_validated: bool,
+6615    },
+6616}
+6617
+6618impl SideArgs {
+6619    pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> {
+6620        match *self {
+6621            Self::Client { .. } => None,
+6622            Self::Server { pref_addr_cid, .. } => pref_addr_cid,
+6623        }
+6624    }
+6625
+6626    pub(crate) fn path_validated(&self) -> bool {
+6627        match *self {
+6628            Self::Client { .. } => true,
+6629            Self::Server { path_validated, .. } => path_validated,
+6630        }
+6631    }
+6632
+6633    pub(crate) fn side(&self) -> Side {
+6634        match *self {
+6635            Self::Client { .. } => Side::Client,
+6636            Self::Server { .. } => Side::Server,
+6637        }
+6638    }
+6639}
+6640
+6641/// Reasons why a connection might be lost
+6642#[derive(Debug, Error, Clone, PartialEq, Eq)]
+6643pub enum ConnectionError {
+6644    /// The peer doesn't implement any supported version
+6645    #[error("peer doesn't implement any supported version")]
+6646    VersionMismatch,
+6647    /// The peer violated the QUIC specification as understood by this implementation
+6648    #[error(transparent)]
+6649    TransportError(#[from] TransportError),
+6650    /// The peer's QUIC stack aborted the connection automatically
+6651    #[error("aborted by peer: {0}")]
+6652    ConnectionClosed(frame::ConnectionClose),
+6653    /// The peer closed the connection
+6654    #[error("closed by peer: {0}")]
+6655    ApplicationClosed(frame::ApplicationClose),
+6656    /// The peer is unable to continue processing this connection, usually due to having restarted
+6657    #[error("reset by peer")]
+6658    Reset,
+6659    /// Communication with the peer has lapsed for longer than the negotiated idle timeout
+6660    ///
+6661    /// If neither side is sending keep-alives, a connection will time out after a long enough idle
+6662    /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`]
+6663    /// and [`TransportConfig::keep_alive_interval()`].
+6664    #[error("timed out")]
+6665    TimedOut,
+6666    /// The local application closed the connection
+6667    #[error("closed")]
+6668    LocallyClosed,
+6669    /// The connection could not be created because not enough of the CID space is available
+6670    ///
+6671    /// Try using longer connection IDs.
+6672    #[error("CIDs exhausted")]
+6673    CidsExhausted,
+6674}
+6675
+6676impl From<Close> for ConnectionError {
+6677    fn from(x: Close) -> Self {
+6678        match x {
+6679            Close::Connection(reason) => Self::ConnectionClosed(reason),
+6680            Close::Application(reason) => Self::ApplicationClosed(reason),
+6681        }
+6682    }
+6683}
+6684
+6685// For compatibility with API consumers
+6686impl From<ConnectionError> for io::Error {
+6687    fn from(x: ConnectionError) -> Self {
+6688        use ConnectionError::*;
+6689        let kind = match x {
+6690            TimedOut => io::ErrorKind::TimedOut,
+6691            Reset => io::ErrorKind::ConnectionReset,
+6692            ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted,
+6693            TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => {
+6694                io::ErrorKind::Other
+6695            }
+6696        };
+6697        Self::new(kind, x)
+6698    }
+6699}
+6700
+6701/// Errors that might trigger a path being closed
+6702// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API
+6703#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)]
+6704pub enum PathError {
+6705    /// The extension was not negotiated with the peer
+6706    #[error("multipath extension not negotiated")]
+6707    MultipathNotNegotiated,
+6708    /// Paths can only be opened client-side
+6709    #[error("the server side may not open a path")]
+6710    ServerSideNotAllowed,
+6711    /// Current limits do not allow us to open more paths
+6712    #[error("maximum number of concurrent paths reached")]
+6713    MaxPathIdReached,
+6714    /// No remote CIDs available to open a new path
+6715    #[error("remoted CIDs exhausted")]
+6716    RemoteCidsExhausted,
+6717    /// Path could not be validated and will be abandoned
+6718    #[error("path validation failed")]
+6719    ValidationFailed,
+6720    /// The remote address for the path is not supported by the endpoint
+6721    #[error("invalid remote address")]
+6722    InvalidRemoteAddress(SocketAddr),
+6723}
+6724
+6725/// Errors triggered when abandoning a path
+6726#[derive(Debug, Error, Clone, Eq, PartialEq)]
+6727pub enum ClosePathError {
+6728    /// The path is already closed or was never opened
+6729    #[error("closed path")]
+6730    ClosedPath,
+6731    /// This is the last path, which can not be abandoned
+6732    #[error("last open path")]
+6733    LastOpenPath,
+6734}
+6735
+6736#[derive(Debug, Error, Clone, Copy)]
+6737#[error("Multipath extension not negotiated")]
+6738pub struct MultipathNotNegotiated {
+6739    _private: (),
+6740}
+6741
+6742/// Events of interest to the application
+6743#[derive(Debug)]
+6744pub enum Event {
+6745    /// The connection's handshake data is ready
+6746    HandshakeDataReady,
+6747    /// The connection was successfully established
+6748    Connected,
+6749    /// The TLS handshake was confirmed
+6750    HandshakeConfirmed,
+6751    /// The connection was lost
+6752    ///
+6753    /// Emitted if the peer closes the connection or an error is encountered.
+6754    ConnectionLost {
+6755        /// Reason that the connection was closed
+6756        reason: ConnectionError,
+6757    },
+6758    /// Stream events
+6759    Stream(StreamEvent),
+6760    /// One or more application datagrams have been received
+6761    DatagramReceived,
+6762    /// One or more application datagrams have been sent after blocking
+6763    DatagramsUnblocked,
+6764    /// (Multi)Path events
+6765    Path(PathEvent),
+6766    /// Iroh's nat traversal events
+6767    NatTraversal(iroh_hp::Event),
+6768}
+6769
+6770impl From<PathEvent> for Event {
+6771    fn from(source: PathEvent) -> Self {
+6772        Self::Path(source)
+6773    }
+6774}
+6775
+6776fn get_max_ack_delay(params: &TransportParameters) -> Duration {
+6777    Duration::from_micros(params.max_ack_delay.0 * 1000)
+6778}
+6779
+6780// Prevents overflow and improves behavior in extreme circumstances
+6781const MAX_BACKOFF_EXPONENT: u32 = 16;
+6782
+6783/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag
+6784///
+6785/// This must be at least as large as the header for a well-formed empty packet to be coalesced,
+6786/// plus some space for frames. We only care about handshake headers because short header packets
+6787/// necessarily have smaller headers, and initial packets are only ever the first packet in a
+6788/// datagram (because we coalesce in ascending packet space order and the only reason to split a
+6789/// packet is when packet space changes).
+6790const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32;
+6791
+6792/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header
+6793///
+6794/// Excludes packet-type-specific fields such as packet number or Initial token
+6795// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid +
+6796// scid len + scid + length + pn
+6797const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize =
+6798    1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4;
+6799
+6800/// Perform key updates this many packets before the AEAD confidentiality limit.
+6801///
+6802/// Chosen arbitrarily, intended to be large enough to prevent spurious connection loss.
+6803const KEY_UPDATE_MARGIN: u64 = 10_000;
+6804
+6805#[derive(Default)]
+6806struct SentFrames {
+6807    retransmits: ThinRetransmits,
+6808    /// The packet number of the largest acknowledged packet for each path
+6809    largest_acked: FxHashMap<PathId, u64>,
+6810    stream_frames: StreamMetaVec,
+6811    /// Whether the packet contains non-retransmittable frames (like datagrams)
+6812    non_retransmits: bool,
+6813    /// If the datagram containing these frames should be padded to the min MTU
+6814    requires_padding: bool,
+6815}
+6816
+6817impl SentFrames {
+6818    /// Returns whether the packet contains only ACKs
+6819    fn is_ack_only(&self, streams: &StreamsState) -> bool {
+6820        !self.largest_acked.is_empty()
+6821            && !self.non_retransmits
+6822            && self.stream_frames.is_empty()
+6823            && self.retransmits.is_empty(streams)
+6824    }
+6825}
+6826
+6827/// Compute the negotiated idle timeout based on local and remote max_idle_timeout transport parameters.
+6828///
+6829/// 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.>
+6830///
+6831/// 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.>
+6832///
+6833/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have opted out of idle timeout.
+6834fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> {
+6835    match (x, y) {
+6836        (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None,
+6837        (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)),
+6838        (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)),
+6839        (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)),
+6840    }
+6841}
+6842
+6843#[cfg(test)]
+6844mod tests {
+6845    use super::*;
+6846
+6847    #[test]
+6848    fn negotiate_max_idle_timeout_commutative() {
+6849        let test_params = [
+6850            (None, None, None),
+6851            (None, Some(VarInt(0)), None),
+6852            (None, Some(VarInt(2)), Some(Duration::from_millis(2))),
+6853            (Some(VarInt(0)), Some(VarInt(0)), None),
+6854            (
+6855                Some(VarInt(2)),
+6856                Some(VarInt(0)),
+6857                Some(Duration::from_millis(2)),
+6858            ),
+6859            (
+6860                Some(VarInt(1)),
+6861                Some(VarInt(4)),
+6862                Some(Duration::from_millis(1)),
+6863            ),
+6864        ];
+6865
+6866        for (left, right, result) in test_params {
+6867            assert_eq!(negotiate_max_idle_timeout(left, right), result);
+6868            assert_eq!(negotiate_max_idle_timeout(right, left), result);
+6869        }
+6870    }
+6871}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/mtud.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/mtud.rs.html new file mode 100644 index 000000000..e94f6f998 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/connection/pacing.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/pacing.rs.html new file mode 100644 index 000000000..19fabe57a --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html new file mode 100644 index 000000000..de299604a --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html @@ -0,0 +1,375 @@ +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, Instant, MIN_INITIAL_SIZE, TransportError, TransportErrorCode,
+8    connection::{ConnectionSide, qlog::QlogSentPacket},
+9    frame::{self, Close},
+10    packet::{FIXED_BIT, Header, InitialHeader, LongType, PacketNumber, PartialEncode, SpaceId},
+11};
+12
+13/// QUIC packet builder
+14///
+15/// This allows building QUIC packets: it takes care of writing the header, allows writing
+16/// frames and on [`PacketBuilder::finish`] (or [`PacketBuilder::finish_and_track`]) it
+17/// encrypts the packet so it is ready to be sent on the wire.
+18///
+19/// The builder manages the write buffer into which the packet is written, and directly
+20/// implements [`BufMut`] to write frames into the packet.
+21pub(super) struct PacketBuilder<'a, 'b> {
+22    pub(super) buf: &'a mut TransmitBuf<'b>,
+23    pub(super) space: SpaceId,
+24    path: PathId,
+25    pub(super) partial_encode: PartialEncode,
+26    pub(super) ack_eliciting: bool,
+27    pub(super) exact_number: u64,
+28    pub(super) short_header: bool,
+29    /// Smallest absolute position in the associated buffer that must be occupied by this packet's
+30    /// frames
+31    pub(super) min_size: usize,
+32    pub(super) tag_len: usize,
+33    pub(super) _span: tracing::span::EnteredSpan,
+34}
+35
+36impl<'a, 'b> PacketBuilder<'a, 'b> {
+37    /// Write a new packet header to `buffer` and determine the packet's properties
+38    ///
+39    /// Marks the connection drained and returns `None` if the confidentiality limit would be
+40    /// violated.
+41    pub(super) fn new(
+42        now: Instant,
+43        space_id: SpaceId,
+44        path_id: PathId,
+45        dst_cid: ConnectionId,
+46        buffer: &'a mut TransmitBuf<'b>,
+47        ack_eliciting: bool,
+48        conn: &mut Connection,
+49        #[allow(unused)] qlog: &mut QlogSentPacket,
+50    ) -> Option<Self>
+51    where
+52        'b: 'a,
+53    {
+54        let version = conn.version;
+55        // Initiate key update if we're approaching the confidentiality limit
+56        let sent_with_keys = conn.spaces[space_id].sent_with_keys();
+57        if space_id == SpaceId::Data {
+58            if sent_with_keys >= conn.key_phase_size {
+59                debug!("routine key update due to phase exhaustion");
+60                conn.force_key_update();
+61            }
+62        } else {
+63            let confidentiality_limit = conn.spaces[space_id]
+64                .crypto
+65                .as_ref()
+66                .map_or_else(
+67                    || &conn.zero_rtt_crypto.as_ref().unwrap().packet,
+68                    |keys| &keys.packet.local,
+69                )
+70                .confidentiality_limit();
+71            if sent_with_keys.saturating_add(1) == confidentiality_limit {
+72                // We still have time to attempt a graceful close
+73                conn.close_inner(
+74                    now,
+75                    Close::Connection(frame::ConnectionClose {
+76                        error_code: TransportErrorCode::AEAD_LIMIT_REACHED,
+77                        frame_type: frame::MaybeFrame::None,
+78                        reason: Bytes::from_static(b"confidentiality limit reached"),
+79                    }),
+80                )
+81            } else if sent_with_keys > confidentiality_limit {
+82                // Confidentiality limited violated and there's nothing we can do
+83                conn.kill(
+84                    TransportError::AEAD_LIMIT_REACHED("confidentiality limit reached").into(),
+85                );
+86                return None;
+87            }
+88        }
+89
+90        let space = &mut conn.spaces[space_id];
+91        let exact_number = space.for_path(path_id).get_tx_number(&mut conn.rng);
+92        let span = trace_span!("send", space = ?space_id, pn = exact_number, %path_id).entered();
+93
+94        let number = PacketNumber::new(
+95            exact_number,
+96            space.for_path(path_id).largest_acked_packet.unwrap_or(0),
+97        );
+98        let header = match space_id {
+99            SpaceId::Data if space.crypto.is_some() => Header::Short {
+100                dst_cid,
+101                number,
+102                spin: if conn.spin_enabled {
+103                    conn.spin
+104                } else {
+105                    conn.rng.random()
+106                },
+107                key_phase: conn.key_phase,
+108            },
+109            SpaceId::Data => Header::Long {
+110                ty: LongType::ZeroRtt,
+111                src_cid: conn.handshake_cid,
+112                dst_cid,
+113                number,
+114                version,
+115            },
+116            SpaceId::Handshake => Header::Long {
+117                ty: LongType::Handshake,
+118                src_cid: conn.handshake_cid,
+119                dst_cid,
+120                number,
+121                version,
+122            },
+123            SpaceId::Initial => Header::Initial(InitialHeader {
+124                src_cid: conn.handshake_cid,
+125                dst_cid,
+126                token: match &conn.side {
+127                    ConnectionSide::Client { token, .. } => token.clone(),
+128                    ConnectionSide::Server { .. } => Bytes::new(),
+129                },
+130                number,
+131                version,
+132            }),
+133        };
+134
+135        let partial_encode = header.encode(buffer);
+136        if conn.peer_params.grease_quic_bit && conn.rng.random() {
+137            buffer.as_mut_slice()[partial_encode.start] ^= FIXED_BIT;
+138        }
+139
+140        let (sample_size, tag_len) = if let Some(ref crypto) = space.crypto {
+141            (
+142                crypto.header.local.sample_size(),
+143                crypto.packet.local.tag_len(),
+144            )
+145        } else if space_id == SpaceId::Data {
+146            let zero_rtt = conn.zero_rtt_crypto.as_ref().unwrap();
+147            (zero_rtt.header.sample_size(), zero_rtt.packet.tag_len())
+148        } else {
+149            unreachable!();
+150        };
+151
+152        // Each packet must be large enough for header protection sampling, i.e. the combined
+153        // lengths of the encoded packet number and protected payload must be at least 4 bytes
+154        // longer than the sample required for header protection. Further, each packet should be at
+155        // least tag_len + 6 bytes larger than the destination CID on incoming packets so that the
+156        // peer may send stateless resets that are indistinguishable from regular traffic.
+157
+158        // pn_len + payload_len + tag_len >= sample_size + 4
+159        // payload_len >= sample_size + 4 - pn_len - tag_len
+160        let min_size = Ord::max(
+161            buffer.len() + (sample_size + 4).saturating_sub(number.len() + tag_len),
+162            partial_encode.start + dst_cid.len() + 6,
+163        );
+164        let max_size = buffer.datagram_max_offset() - tag_len;
+165        debug_assert!(max_size >= min_size);
+166
+167        qlog.header(
+168            &header,
+169            Some(exact_number),
+170            space_id,
+171            space_id == SpaceId::Data && conn.spaces[SpaceId::Data].crypto.is_none(),
+172            path_id,
+173        );
+174
+175        Some(Self {
+176            buf: buffer,
+177            space: space_id,
+178            path: path_id,
+179            partial_encode,
+180            exact_number,
+181            short_header: header.is_short(),
+182            min_size,
+183            tag_len,
+184            ack_eliciting,
+185            _span: span,
+186        })
+187    }
+188
+189    /// Append the minimum amount of padding to the packet such that, after encryption, the
+190    /// enclosing datagram will occupy at least `min_size` bytes
+191    pub(super) fn pad_to(&mut self, min_size: u16) {
+192        // The datagram might already have a larger minimum size than the caller is requesting, if
+193        // e.g. we're coalescing packets and have populated more than `min_size` bytes with packets
+194        // already.
+195        self.min_size = Ord::max(
+196            self.min_size,
+197            self.buf.datagram_start_offset() + (min_size as usize) - self.tag_len,
+198        );
+199    }
+200
+201    /// Returns a writable buffer limited to the remaining frame space
+202    ///
+203    /// The [`BufMut::remaining_mut`] call on the returned buffer indicates the amount of
+204    /// space available to write QUIC frames into.
+205    // In rust 1.82 we can use `-> impl BufMut + use<'_, 'a, 'b>`
+206    pub(super) fn frame_space_mut(&mut self) -> bytes::buf::Limit<&mut TransmitBuf<'b>> {
+207        self.buf.limit(self.frame_space_remaining())
+208    }
+209
+210    pub(super) fn finish_and_track(
+211        mut self,
+212        now: Instant,
+213        conn: &mut Connection,
+214        path_id: PathId,
+215        sent: SentFrames,
+216        pad_datagram: PadDatagram,
+217        qlog: QlogSentPacket,
+218    ) {
+219        match pad_datagram {
+220            PadDatagram::No => (),
+221            PadDatagram::ToSize(size) => self.pad_to(size),
+222            PadDatagram::ToSegmentSize => self.pad_to(self.buf.segment_size() as u16),
+223            PadDatagram::ToMinMtu => self.pad_to(MIN_INITIAL_SIZE),
+224        }
+225        let ack_eliciting = self.ack_eliciting;
+226        let exact_number = self.exact_number;
+227        let space_id = self.space;
+228        let (size, padded) = self.finish(conn, now, qlog);
+229
+230        let size = match padded || ack_eliciting {
+231            true => size as u16,
+232            false => 0,
+233        };
+234
+235        let packet = SentPacket {
+236            path_generation: conn.paths.get_mut(&path_id).unwrap().data.generation(),
+237            largest_acked: sent.largest_acked,
+238            time_sent: now,
+239            size,
+240            ack_eliciting,
+241            retransmits: sent.retransmits,
+242            stream_frames: sent.stream_frames,
+243        };
+244
+245        conn.paths.get_mut(&path_id).unwrap().data.sent(
+246            exact_number,
+247            packet,
+248            conn.spaces[space_id].for_path(path_id),
+249        );
+250        conn.path_stats.entry(path_id).or_default().sent_packets += 1;
+251        conn.reset_keep_alive(path_id, now);
+252        if size != 0 {
+253            if ack_eliciting {
+254                conn.spaces[space_id]
+255                    .for_path(path_id)
+256                    .time_of_last_ack_eliciting_packet = Some(now);
+257                if conn.permit_idle_reset {
+258                    conn.reset_idle_timeout(now, space_id, path_id);
+259                }
+260                conn.permit_idle_reset = false;
+261            }
+262            conn.set_loss_detection_timer(now, path_id);
+263            conn.path_data_mut(path_id).pacing.on_transmit(size);
+264        }
+265    }
+266
+267    /// Encrypt packet, returning the length of the packet and whether padding was added
+268    pub(super) fn finish(
+269        self,
+270        conn: &mut Connection,
+271        now: Instant,
+272        #[allow(unused_mut)] mut qlog: QlogSentPacket,
+273    ) -> (usize, bool) {
+274        debug_assert!(
+275            self.buf.len() <= self.buf.datagram_max_offset() - self.tag_len,
+276            "packet exceeds maximum size"
+277        );
+278        let pad = self.buf.len() < self.min_size;
+279        if pad {
+280            let padding = self.min_size - self.buf.len();
+281            trace!("PADDING * {}", padding);
+282            self.buf.put_bytes(0, padding);
+283            qlog.frame_padding(padding);
+284        }
+285
+286        let space = &conn.spaces[self.space];
+287        let (header_crypto, packet_crypto) = if let Some(ref crypto) = space.crypto {
+288            (&*crypto.header.local, &*crypto.packet.local)
+289        } else if self.space == SpaceId::Data {
+290            let zero_rtt = conn.zero_rtt_crypto.as_ref().unwrap();
+291            (&*zero_rtt.header, &*zero_rtt.packet)
+292        } else {
+293            unreachable!("tried to send {:?} packet without keys", self.space);
+294        };
+295
+296        debug_assert_eq!(
+297            packet_crypto.tag_len(),
+298            self.tag_len,
+299            "Mismatching crypto tag len"
+300        );
+301
+302        self.buf.put_bytes(0, packet_crypto.tag_len());
+303        let encode_start = self.partial_encode.start;
+304        let packet_buf = &mut self.buf.as_mut_slice()[encode_start..];
+305        // for packet protection, PathId::ZERO and no path are equivalent.
+306        self.partial_encode.finish(
+307            packet_buf,
+308            header_crypto,
+309            Some((self.exact_number, self.path, packet_crypto)),
+310        );
+311
+312        let packet_len = self.buf.len() - encode_start;
+313        trace!(size = %packet_len, short_header = %self.short_header, "wrote packet");
+314        qlog.finalize(packet_len);
+315        conn.qlog.emit_packet_sent(qlog, now);
+316        (packet_len, pad)
+317    }
+318
+319    /// The number of additional bytes the current packet would take up if it was finished now
+320    ///
+321    /// This will include any padding which is required to make the size large enough to be
+322    /// encrypted correctly.
+323    pub(super) fn predict_packet_end(&self) -> usize {
+324        self.buf.len().max(self.min_size) + self.tag_len - self.buf.len()
+325    }
+326
+327    /// Returns the remaining space in the packet that can be taken up by QUIC frames
+328    ///
+329    /// This leaves space in the datagram for the cryptographic tag that needs to be written
+330    /// when the packet is finished.
+331    pub(super) fn frame_space_remaining(&self) -> usize {
+332        let max_offset = self.buf.datagram_max_offset() - self.tag_len;
+333        max_offset.saturating_sub(self.buf.len())
+334    }
+335}
+336
+337#[derive(Debug, Copy, Clone)]
+338pub(super) enum PadDatagram {
+339    /// Do not pad the datagram
+340    No,
+341    /// To a specific size
+342    ToSize(u16),
+343    /// Pad to the current MTU/segment size
+344    ///
+345    /// For the first datagram in a transmit the MTU is the same as the
+346    /// [`TransmitBuf::segment_size`].
+347    ToSegmentSize,
+348    /// Pad to [`MIN_INITIAL_SIZE`], the minimal QUIC MTU of 1200 bytes
+349    ToMinMtu,
+350}
+351
+352impl std::ops::BitOrAssign for PadDatagram {
+353    fn bitor_assign(&mut self, rhs: Self) {
+354        *self = *self | rhs;
+355    }
+356}
+357
+358impl std::ops::BitOr for PadDatagram {
+359    type Output = Self;
+360
+361    fn bitor(self, rhs: Self) -> Self::Output {
+362        match (self, rhs) {
+363            (Self::No, rhs) => rhs,
+364            (Self::ToSize(size), Self::No) => Self::ToSize(size),
+365            (Self::ToSize(a), Self::ToSize(b)) => Self::ToSize(a.max(b)),
+366            (Self::ToSize(_), Self::ToSegmentSize) => Self::ToSegmentSize,
+367            (Self::ToSize(_), Self::ToMinMtu) => Self::ToMinMtu,
+368            (Self::ToSegmentSize, Self::No) => Self::ToSegmentSize,
+369            (Self::ToSegmentSize, Self::ToSize(_)) => Self::ToSegmentSize,
+370            (Self::ToSegmentSize, Self::ToSegmentSize) => Self::ToSegmentSize,
+371            (Self::ToSegmentSize, Self::ToMinMtu) => Self::ToMinMtu,
+372            (Self::ToMinMtu, _) => Self::ToMinMtu,
+373        }
+374    }
+375}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/packet_crypto.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/packet_crypto.rs.html new file mode 100644 index 000000000..c7ee71bf4 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/packet_crypto.rs.html @@ -0,0 +1,197 @@ +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        if prev.end_packet.is_none() && packet_key_phase == conn_key_phase {
+147            outgoing_key_update_acked = true;
+148        }
+149    }
+150
+151    if crypto_update {
+152        // Validate incoming key update
+153        // If `rx_packet` is `None`, then either the path is entirely new, or we haven't received
+154        // any packets on this path yet. In that case, having the first packet be a crypto update
+155        // is fine.
+156        let invalid_packet_number = rx_packet.is_some_and(|rx_packet| number <= rx_packet);
+157        if invalid_packet_number || prev_crypto.is_some_and(|x| x.update_unacked) {
+158            trace!(?number, ?rx_packet, %path_id, "crypto update failed");
+159            return Err(Some(TransportError::KEY_UPDATE_ERROR("")));
+160        }
+161    }
+162
+163    Ok(Some(DecryptPacketResult {
+164        number,
+165        outgoing_key_update_acked,
+166        incoming_key_update: crypto_update,
+167    }))
+168}
+169
+170pub(super) struct DecryptPacketResult {
+171    /// The packet number
+172    pub(super) number: u64,
+173    /// Whether a locally initiated key update has been acknowledged by the peer
+174    pub(super) outgoing_key_update_acked: bool,
+175    /// Whether the peer has initiated a key update
+176    pub(super) incoming_key_update: bool,
+177}
+178
+179pub(super) struct PrevCrypto {
+180    /// The keys used for the previous key phase, temporarily retained to decrypt packets sent by
+181    /// the peer prior to its own key update.
+182    pub(super) crypto: KeyPair<Box<dyn PacketKey>>,
+183    /// The incoming packet that ends the interval for which these keys are applicable, and the time
+184    /// of its receipt.
+185    ///
+186    /// Incoming packets should be decrypted using these keys iff this is `None` or their packet
+187    /// number is lower. `None` indicates that we have not yet received a packet using newer keys,
+188    /// which implies that the update was locally initiated.
+189    pub(super) end_packet: Option<(u64, Instant)>,
+190    /// Whether the following key phase is from a remotely initiated update that we haven't acked
+191    pub(super) update_unacked: bool,
+192}
+193
+194pub(super) struct ZeroRttCrypto {
+195    pub(super) header: Box<dyn HeaderKey>,
+196    pub(super) packet: Box<dyn PacketKey>,
+197}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/paths.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/paths.rs.html new file mode 100644 index 000000000..cf2b5c827 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/paths.rs.html @@ -0,0 +1,978 @@ +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#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
+26pub struct PathId(pub(crate) u32);
+27
+28impl std::hash::Hash for PathId {
+29    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+30        state.write_u32(self.0);
+31    }
+32}
+33
+34impl identity_hash::IdentityHashable for PathId {}
+35
+36impl Decodable for PathId {
+37    fn decode<B: bytes::Buf>(r: &mut B) -> coding::Result<Self> {
+38        let v = VarInt::decode(r)?;
+39        let v = u32::try_from(v.0).map_err(|_| coding::UnexpectedEnd)?;
+40        Ok(Self(v))
+41    }
+42}
+43
+44impl Encodable for PathId {
+45    fn encode<B: bytes::BufMut>(&self, w: &mut B) {
+46        VarInt(self.0.into()).encode(w)
+47    }
+48}
+49
+50impl PathId {
+51    /// The maximum path ID allowed.
+52    pub const MAX: Self = Self(u32::MAX);
+53
+54    /// The 0 path id.
+55    pub const ZERO: Self = Self(0);
+56
+57    /// The number of bytes this [`PathId`] uses when encoded as a [`VarInt`]
+58    pub(crate) const fn size(&self) -> usize {
+59        VarInt(self.0 as u64).size()
+60    }
+61
+62    /// Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead
+63    /// of overflowing.
+64    pub fn saturating_add(self, rhs: impl Into<Self>) -> Self {
+65        let rhs = rhs.into();
+66        let inner = self.0.saturating_add(rhs.0);
+67        Self(inner)
+68    }
+69
+70    /// Saturating integer subtraction. Computes self - rhs, saturating at the numeric bounds
+71    /// instead of overflowing.
+72    pub fn saturating_sub(self, rhs: impl Into<Self>) -> Self {
+73        let rhs = rhs.into();
+74        let inner = self.0.saturating_sub(rhs.0);
+75        Self(inner)
+76    }
+77
+78    /// Get the next [`PathId`]
+79    pub(crate) fn next(&self) -> Self {
+80        self.saturating_add(Self(1))
+81    }
+82
+83    /// Get the underlying u32
+84    pub(crate) fn as_u32(&self) -> u32 {
+85        self.0
+86    }
+87}
+88
+89impl std::fmt::Display for PathId {
+90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+91        self.0.fmt(f)
+92    }
+93}
+94
+95impl<T: Into<u32>> From<T> for PathId {
+96    fn from(source: T) -> Self {
+97        Self(source.into())
+98    }
+99}
+100
+101/// State needed for a single path ID.
+102///
+103/// A single path ID can migrate according to the rules in RFC9000 §9, either voluntary or
+104/// involuntary. We need to keep the [`PathData`] of the previously used such path available
+105/// in order to defend against migration attacks (see RFC9000 §9.3.1, §9.3.2 and §9.3.3) as
+106/// well as to support path probing (RFC9000 §9.1).
+107#[derive(Debug)]
+108pub(super) struct PathState {
+109    pub(super) data: PathData,
+110    pub(super) prev: Option<(ConnectionId, PathData)>,
+111}
+112
+113impl PathState {
+114    /// Update counters to account for a packet becoming acknowledged, lost, or abandoned
+115    pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) {
+116        // Visit known paths from newest to oldest to find the one `pn` was sent on
+117        for path_data in [&mut self.data]
+118            .into_iter()
+119            .chain(self.prev.as_mut().map(|(_, data)| data))
+120        {
+121            if path_data.remove_in_flight(packet) {
+122                return;
+123            }
+124        }
+125    }
+126}
+127
+128#[derive(Debug)]
+129pub(super) struct SentChallengeInfo {
+130    /// When was the challenge sent on the wire.
+131    pub(super) sent_instant: Instant,
+132    /// The 4-tuple on which this path challenge was sent.
+133    pub(super) network_path: FourTuple,
+134}
+135
+136/// Description of a particular network path
+137#[derive(Debug)]
+138pub(super) struct PathData {
+139    pub(super) network_path: FourTuple,
+140    pub(super) rtt: RttEstimator,
+141    /// Whether we're enabling ECN on outgoing packets
+142    pub(super) sending_ecn: bool,
+143    /// Congestion controller state
+144    pub(super) congestion: Box<dyn congestion::Controller>,
+145    /// Pacing state
+146    pub(super) pacing: Pacer,
+147    /// Actually sent challenges (on the wire).
+148    pub(super) challenges_sent: IntMap<u64, SentChallengeInfo>,
+149    /// Whether to *immediately* trigger another PATH_CHALLENGE.
+150    ///
+151    /// This is picked up by [`super::Connection::space_can_send`].
+152    pub(super) send_new_challenge: bool,
+153    /// Pending responses to PATH_CHALLENGE frames
+154    pub(super) path_responses: PathResponses,
+155    /// Whether we're certain the peer can both send and receive on this address
+156    ///
+157    /// Initially equal to `use_stateless_retry` for servers, and becomes false again on every
+158    /// migration. Always true for clients.
+159    pub(super) validated: bool,
+160    /// Total size of all UDP datagrams sent on this path
+161    pub(super) total_sent: u64,
+162    /// Total size of all UDP datagrams received on this path
+163    pub(super) total_recvd: u64,
+164    /// The state of the MTU discovery process
+165    pub(super) mtud: MtuDiscovery,
+166    /// Packet number of the first packet sent after an RTT sample was collected on this path
+167    ///
+168    /// Used in persistent congestion determination.
+169    pub(super) first_packet_after_rtt_sample: Option<(SpaceId, u64)>,
+170    /// The in-flight packets and bytes
+171    ///
+172    /// Note that this is across all spaces on this path
+173    pub(super) in_flight: InFlight,
+174    /// Whether this path has had it's remote address reported back to the peer. This only happens
+175    /// if both peers agree to so based on their transport parameters.
+176    pub(super) observed_addr_sent: bool,
+177    /// Observed address frame with the largest sequence number received from the peer on this path.
+178    pub(super) last_observed_addr_report: Option<ObservedAddr>,
+179    /// The QUIC-MULTIPATH path status
+180    pub(super) status: PathStatusState,
+181    /// Number of the first packet sent on this path
+182    ///
+183    /// With RFC9000 §9 style migration (i.e. not multipath) the PathId does not change and
+184    /// hence packet numbers continue. This is used to determine whether a packet was sent
+185    /// on such an earlier path. Insufficient to determine if a packet was sent on a later
+186    /// path.
+187    first_packet: Option<u64>,
+188    /// The number of times a PTO has been sent without receiving an ack.
+189    pub(super) pto_count: u32,
+190
+191    //
+192    // Per-path idle & keep alive
+193    //
+194    /// Idle timeout for the path
+195    ///
+196    /// If expired, the path will be abandoned.  This is different from the connection-wide
+197    /// idle timeout which closes the connection if expired.
+198    pub(super) idle_timeout: Option<Duration>,
+199    /// Keep alives to send on this path
+200    ///
+201    /// There is also a connection-level keep alive configured in the
+202    /// [`TransportParameters`].  This triggers activity on any path which can keep the
+203    /// connection alive.
+204    ///
+205    /// [`TransportParameters`]: crate::transport_parameters::TransportParameters
+206    pub(super) keep_alive: Option<Duration>,
+207
+208    /// Whether the path has already been considered opened from an application perspective
+209    ///
+210    /// This means, for paths other than the original [`PathId::ZERO`], a first path challenge has
+211    /// been responded to, regardless of the initial validation status of the path. This state is
+212    /// irreversible, since it's not affected by the path being closed.
+213    pub(super) open: bool,
+214
+215    /// The time at which this path state should've received a PATH_ABANDON already.
+216    ///
+217    /// Receiving data on this path generates a transport error after that point in time.
+218    /// This is checked in [`crate::Connection::on_packet_authenticated`].
+219    ///
+220    /// If set to `None`, then this path isn't abandoned yet and is allowed to receive data.
+221    pub(super) last_allowed_receive: Option<Instant>,
+222
+223    /// Snapshot of the qlog recovery metrics
+224    #[cfg(feature = "qlog")]
+225    recovery_metrics: RecoveryMetrics,
+226
+227    /// Tag uniquely identifying a path in a connection
+228    generation: u64,
+229}
+230
+231impl PathData {
+232    pub(super) fn new(
+233        network_path: FourTuple,
+234        allow_mtud: bool,
+235        peer_max_udp_payload_size: Option<u16>,
+236        generation: u64,
+237        now: Instant,
+238        config: &TransportConfig,
+239    ) -> Self {
+240        let congestion = config
+241            .congestion_controller_factory
+242            .clone()
+243            .build(now, config.get_initial_mtu());
+244        Self {
+245            network_path,
+246            rtt: RttEstimator::new(config.initial_rtt),
+247            sending_ecn: true,
+248            pacing: Pacer::new(
+249                config.initial_rtt,
+250                congestion.initial_window(),
+251                config.get_initial_mtu(),
+252                now,
+253            ),
+254            congestion,
+255            challenges_sent: Default::default(),
+256            send_new_challenge: false,
+257            path_responses: PathResponses::default(),
+258            validated: false,
+259            total_sent: 0,
+260            total_recvd: 0,
+261            mtud: config
+262                .mtu_discovery_config
+263                .as_ref()
+264                .filter(|_| allow_mtud)
+265                .map_or(
+266                    MtuDiscovery::disabled(config.get_initial_mtu(), config.min_mtu),
+267                    |mtud_config| {
+268                        MtuDiscovery::new(
+269                            config.get_initial_mtu(),
+270                            config.min_mtu,
+271                            peer_max_udp_payload_size,
+272                            mtud_config.clone(),
+273                        )
+274                    },
+275                ),
+276            first_packet_after_rtt_sample: None,
+277            in_flight: InFlight::new(),
+278            observed_addr_sent: false,
+279            last_observed_addr_report: None,
+280            status: Default::default(),
+281            first_packet: None,
+282            pto_count: 0,
+283            idle_timeout: config.default_path_max_idle_timeout,
+284            keep_alive: config.default_path_keep_alive_interval,
+285            open: false,
+286            last_allowed_receive: None,
+287            #[cfg(feature = "qlog")]
+288            recovery_metrics: RecoveryMetrics::default(),
+289            generation,
+290        }
+291    }
+292
+293    /// Create a new path from a previous one.
+294    ///
+295    /// This should only be called when migrating paths.
+296    pub(super) fn from_previous(
+297        network_path: FourTuple,
+298        prev: &Self,
+299        generation: u64,
+300        now: Instant,
+301    ) -> Self {
+302        let congestion = prev.congestion.clone_box();
+303        let smoothed_rtt = prev.rtt.get();
+304        Self {
+305            network_path,
+306            rtt: prev.rtt,
+307            pacing: Pacer::new(smoothed_rtt, congestion.window(), prev.current_mtu(), now),
+308            sending_ecn: true,
+309            congestion,
+310            challenges_sent: Default::default(),
+311            send_new_challenge: false,
+312            path_responses: PathResponses::default(),
+313            validated: false,
+314            total_sent: 0,
+315            total_recvd: 0,
+316            mtud: prev.mtud.clone(),
+317            first_packet_after_rtt_sample: prev.first_packet_after_rtt_sample,
+318            in_flight: InFlight::new(),
+319            observed_addr_sent: false,
+320            last_observed_addr_report: None,
+321            status: prev.status.clone(),
+322            first_packet: None,
+323            pto_count: 0,
+324            idle_timeout: prev.idle_timeout,
+325            keep_alive: prev.keep_alive,
+326            open: false,
+327            last_allowed_receive: None,
+328            #[cfg(feature = "qlog")]
+329            recovery_metrics: prev.recovery_metrics.clone(),
+330            generation,
+331        }
+332    }
+333
+334    /// Whether we're in the process of validating this path with PATH_CHALLENGEs
+335    pub(super) fn is_validating_path(&self) -> bool {
+336        !self.challenges_sent.is_empty() || self.send_new_challenge
+337    }
+338
+339    /// Indicates whether we're a server that hasn't validated the peer's address and hasn't
+340    /// received enough data from the peer to permit sending `bytes_to_send` additional bytes
+341    pub(super) fn anti_amplification_blocked(&self, bytes_to_send: u64) -> bool {
+342        !self.validated && self.total_recvd * 3 < self.total_sent + bytes_to_send
+343    }
+344
+345    /// Returns the path's current MTU
+346    pub(super) fn current_mtu(&self) -> u16 {
+347        self.mtud.current_mtu()
+348    }
+349
+350    /// Account for transmission of `packet` with number `pn` in `space`
+351    pub(super) fn sent(&mut self, pn: u64, packet: SentPacket, space: &mut PacketNumberSpace) {
+352        self.in_flight.insert(&packet);
+353        if self.first_packet.is_none() {
+354            self.first_packet = Some(pn);
+355        }
+356        if let Some(forgotten) = space.sent(pn, packet) {
+357            self.remove_in_flight(&forgotten);
+358        }
+359    }
+360
+361    /// Remove `packet` with number `pn` from this path's congestion control counters, or return
+362    /// `false` if `pn` was sent before this path was established.
+363    pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) -> bool {
+364        if packet.path_generation != self.generation {
+365            return false;
+366        }
+367        self.in_flight.remove(packet);
+368        true
+369    }
+370
+371    /// Increment the total size of sent UDP datagrams
+372    pub(super) fn inc_total_sent(&mut self, inc: u64) {
+373        self.total_sent = self.total_sent.saturating_add(inc);
+374        if !self.validated {
+375            trace!(
+376                network_path = %self.network_path,
+377                anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent),
+378                "anti amplification budget decreased"
+379            );
+380        }
+381    }
+382
+383    /// Increment the total size of received UDP datagrams
+384    pub(super) fn inc_total_recvd(&mut self, inc: u64) {
+385        self.total_recvd = self.total_recvd.saturating_add(inc);
+386        if !self.validated {
+387            trace!(
+388                network_path = %self.network_path,
+389                anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent),
+390                "anti amplification budget increased"
+391            );
+392        }
+393    }
+394
+395    /// The earliest time at which a sent challenge is considered lost.
+396    pub(super) fn earliest_expiring_challenge(&self) -> Option<Instant> {
+397        if self.challenges_sent.is_empty() {
+398            return None;
+399        }
+400        let pto = self.rtt.pto_base();
+401        self.challenges_sent
+402            .values()
+403            .map(|info| info.sent_instant)
+404            .min()
+405            .map(|sent_instant| sent_instant + pto)
+406    }
+407
+408    /// Handle receiving a PATH_RESPONSE.
+409    pub(super) fn on_path_response_received(
+410        &mut self,
+411        now: Instant,
+412        token: u64,
+413        network_path: FourTuple,
+414    ) -> OnPathResponseReceived {
+415        match self.challenges_sent.get(&token) {
+416            // Response to an on-path PathChallenge
+417            Some(info)
+418                if info.network_path.is_probably_same_path(&network_path)
+419                    && self.network_path.is_probably_same_path(&network_path) =>
+420            {
+421                self.network_path.update_local_if_same_remote(&network_path);
+422                let sent_instant = info.sent_instant;
+423                if !std::mem::replace(&mut self.validated, true) {
+424                    trace!("new path validated");
+425                }
+426                // Clear any other on-path sent challenge.
+427                self.challenges_sent
+428                    .retain(|_token, info| !info.network_path.is_probably_same_path(&network_path));
+429
+430                self.send_new_challenge = false;
+431
+432                // This RTT can only be used for the initial RTT, not as a normal
+433                // sample: https://www.rfc-editor.org/rfc/rfc9002#section-6.2.2-2.
+434                let rtt = now.saturating_duration_since(sent_instant);
+435                self.rtt.reset_initial_rtt(rtt);
+436
+437                let was_open = std::mem::replace(&mut self.open, true);
+438                OnPathResponseReceived::OnPath { was_open }
+439            }
+440            // Response to an off-path PathChallenge
+441            Some(info) if info.network_path.is_probably_same_path(&network_path) => {
+442                self.challenges_sent
+443                    .retain(|_token, info| !info.network_path.is_probably_same_path(&network_path));
+444                OnPathResponseReceived::OffPath
+445            }
+446            // Response to a PathChallenge we recognize, but from an invalid remote
+447            Some(info) => OnPathResponseReceived::Invalid {
+448                expected: info.network_path,
+449            },
+450            // Response to an unknown PathChallenge
+451            None => OnPathResponseReceived::Unknown,
+452        }
+453    }
+454
+455    #[cfg(feature = "qlog")]
+456    pub(super) fn qlog_recovery_metrics(
+457        &mut self,
+458        path_id: PathId,
+459    ) -> Option<RecoveryMetricsUpdated> {
+460        let controller_metrics = self.congestion.metrics();
+461
+462        let metrics = RecoveryMetrics {
+463            min_rtt: Some(self.rtt.min),
+464            smoothed_rtt: Some(self.rtt.get()),
+465            latest_rtt: Some(self.rtt.latest),
+466            rtt_variance: Some(self.rtt.var),
+467            pto_count: Some(self.pto_count),
+468            bytes_in_flight: Some(self.in_flight.bytes),
+469            packets_in_flight: Some(self.in_flight.ack_eliciting),
+470
+471            congestion_window: Some(controller_metrics.congestion_window),
+472            ssthresh: controller_metrics.ssthresh,
+473            pacing_rate: controller_metrics.pacing_rate,
+474        };
+475
+476        let event = metrics.to_qlog_event(path_id, &self.recovery_metrics);
+477        self.recovery_metrics = metrics;
+478        event
+479    }
+480
+481    /// Return how long we need to wait before sending `bytes_to_send`
+482    ///
+483    /// See [`Pacer::delay`].
+484    pub(super) fn pacing_delay(&mut self, bytes_to_send: u64, now: Instant) -> Option<Instant> {
+485        let smoothed_rtt = self.rtt.get();
+486        self.pacing.delay(
+487            smoothed_rtt,
+488            bytes_to_send,
+489            self.current_mtu(),
+490            self.congestion.window(),
+491            now,
+492        )
+493    }
+494
+495    /// Updates the last observed address report received on this path.
+496    ///
+497    /// If the address was updated, it's returned to be informed to the application.
+498    #[must_use = "updated observed address must be reported to the application"]
+499    pub(super) fn update_observed_addr_report(
+500        &mut self,
+501        observed: ObservedAddr,
+502    ) -> Option<SocketAddr> {
+503        match self.last_observed_addr_report.as_mut() {
+504            Some(prev) => {
+505                if prev.seq_no >= observed.seq_no {
+506                    // frames that do not increase the sequence number on this path are ignored
+507                    None
+508                } else if prev.ip == observed.ip && prev.port == observed.port {
+509                    // keep track of the last seq_no but do not report the address as updated
+510                    prev.seq_no = observed.seq_no;
+511                    None
+512                } else {
+513                    let addr = observed.socket_addr();
+514                    self.last_observed_addr_report = Some(observed);
+515                    Some(addr)
+516                }
+517            }
+518            None => {
+519                let addr = observed.socket_addr();
+520                self.last_observed_addr_report = Some(observed);
+521                Some(addr)
+522            }
+523        }
+524    }
+525
+526    pub(crate) fn remote_status(&self) -> Option<PathStatus> {
+527        self.status.remote_status.map(|(_seq, status)| status)
+528    }
+529
+530    pub(crate) fn local_status(&self) -> PathStatus {
+531        self.status.local_status
+532    }
+533
+534    pub(super) fn generation(&self) -> u64 {
+535        self.generation
+536    }
+537}
+538
+539pub(super) enum OnPathResponseReceived {
+540    /// This response validates the path on its current remote address.
+541    OnPath { was_open: bool },
+542    /// This response is valid, but it's for a remote other than the path's current remote address.
+543    OffPath,
+544    /// The received token is unknown.
+545    Unknown,
+546    /// The response is invalid.
+547    Invalid {
+548        /// The 4-tuple that was expected for this token.
+549        expected: FourTuple,
+550    },
+551}
+552
+553/// Congestion metrics as described in [`recovery_metrics_updated`].
+554///
+555/// [`recovery_metrics_updated`]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-quic-events.html#name-recovery_metrics_updated
+556#[cfg(feature = "qlog")]
+557#[derive(Default, Clone, PartialEq, Debug)]
+558#[non_exhaustive]
+559struct RecoveryMetrics {
+560    pub min_rtt: Option<Duration>,
+561    pub smoothed_rtt: Option<Duration>,
+562    pub latest_rtt: Option<Duration>,
+563    pub rtt_variance: Option<Duration>,
+564    pub pto_count: Option<u32>,
+565    pub bytes_in_flight: Option<u64>,
+566    pub packets_in_flight: Option<u64>,
+567    pub congestion_window: Option<u64>,
+568    pub ssthresh: Option<u64>,
+569    pub pacing_rate: Option<u64>,
+570}
+571
+572#[cfg(feature = "qlog")]
+573impl RecoveryMetrics {
+574    /// Retain only values that have been updated since the last snapshot.
+575    fn retain_updated(&self, previous: &Self) -> Self {
+576        macro_rules! keep_if_changed {
+577            ($name:ident) => {
+578                if previous.$name == self.$name {
+579                    None
+580                } else {
+581                    self.$name
+582                }
+583            };
+584        }
+585
+586        Self {
+587            min_rtt: keep_if_changed!(min_rtt),
+588            smoothed_rtt: keep_if_changed!(smoothed_rtt),
+589            latest_rtt: keep_if_changed!(latest_rtt),
+590            rtt_variance: keep_if_changed!(rtt_variance),
+591            pto_count: keep_if_changed!(pto_count),
+592            bytes_in_flight: keep_if_changed!(bytes_in_flight),
+593            packets_in_flight: keep_if_changed!(packets_in_flight),
+594            congestion_window: keep_if_changed!(congestion_window),
+595            ssthresh: keep_if_changed!(ssthresh),
+596            pacing_rate: keep_if_changed!(pacing_rate),
+597        }
+598    }
+599
+600    /// Emit a `MetricsUpdated` event containing only updated values
+601    fn to_qlog_event(&self, path_id: PathId, previous: &Self) -> Option<RecoveryMetricsUpdated> {
+602        let updated = self.retain_updated(previous);
+603
+604        if updated == Self::default() {
+605            return None;
+606        }
+607
+608        Some(RecoveryMetricsUpdated {
+609            min_rtt: updated.min_rtt.map(|rtt| rtt.as_secs_f32()),
+610            smoothed_rtt: updated.smoothed_rtt.map(|rtt| rtt.as_secs_f32()),
+611            latest_rtt: updated.latest_rtt.map(|rtt| rtt.as_secs_f32()),
+612            rtt_variance: updated.rtt_variance.map(|rtt| rtt.as_secs_f32()),
+613            pto_count: updated
+614                .pto_count
+615                .map(|count| count.try_into().unwrap_or(u16::MAX)),
+616            bytes_in_flight: updated.bytes_in_flight,
+617            packets_in_flight: updated.packets_in_flight,
+618            congestion_window: updated.congestion_window,
+619            ssthresh: updated.ssthresh,
+620            pacing_rate: updated.pacing_rate,
+621            path_id: Some(path_id.as_u32() as u64),
+622        })
+623    }
+624}
+625
+626/// RTT estimation for a particular network path
+627#[derive(Copy, Clone, Debug)]
+628pub struct RttEstimator {
+629    /// The most recent RTT measurement made when receiving an ack for a previously unacked packet
+630    latest: Duration,
+631    /// The smoothed RTT of the connection, computed as described in RFC6298
+632    smoothed: Option<Duration>,
+633    /// The RTT variance, computed as described in RFC6298
+634    var: Duration,
+635    /// The minimum RTT seen in the connection, ignoring ack delay.
+636    min: Duration,
+637}
+638
+639impl RttEstimator {
+640    pub(super) fn new(initial_rtt: Duration) -> Self {
+641        Self {
+642            latest: initial_rtt,
+643            smoothed: None,
+644            var: initial_rtt / 2,
+645            min: initial_rtt,
+646        }
+647    }
+648
+649    /// Resets the estimator using a new initial_rtt value.
+650    ///
+651    /// This only resets the initial_rtt **if** no samples have been recorded yet. If there
+652    /// are any recorded samples the initial estimate can not be adjusted after the fact.
+653    ///
+654    /// This is useful when you receive a PATH_RESPONSE in the first packet received on a
+655    /// new path. In this case you can use the delay of the PATH_CHALLENGE-PATH_RESPONSE as
+656    /// the initial RTT to get a better expected estimation.
+657    ///
+658    /// A PATH_CHALLENGE-PATH_RESPONSE pair later in the connection should not be used
+659    /// explicitly as an estimation since PATH_CHALLENGE is an ACK-eliciting packet itself
+660    /// already.
+661    pub(crate) fn reset_initial_rtt(&mut self, initial_rtt: Duration) {
+662        if self.smoothed.is_none() {
+663            self.latest = initial_rtt;
+664            self.var = initial_rtt / 2;
+665            self.min = initial_rtt;
+666        }
+667    }
+668
+669    /// The current best RTT estimation.
+670    pub fn get(&self) -> Duration {
+671        self.smoothed.unwrap_or(self.latest)
+672    }
+673
+674    /// Conservative estimate of RTT
+675    ///
+676    /// Takes the maximum of smoothed and latest RTT, as recommended
+677    /// in 6.1.2 of the recovery spec (draft 29).
+678    pub fn conservative(&self) -> Duration {
+679        self.get().max(self.latest)
+680    }
+681
+682    /// Minimum RTT registered so far for this estimator.
+683    pub fn min(&self) -> Duration {
+684        self.min
+685    }
+686
+687    /// PTO computed as described in RFC9002#6.2.1.
+688    pub(crate) fn pto_base(&self) -> Duration {
+689        self.get() + cmp::max(4 * self.var, TIMER_GRANULARITY)
+690    }
+691
+692    /// Records an RTT sample.
+693    pub(crate) fn update(&mut self, ack_delay: Duration, rtt: Duration) {
+694        self.latest = rtt;
+695        // https://www.rfc-editor.org/rfc/rfc9002.html#section-5.2-3:
+696        // min_rtt does not adjust for ack_delay to avoid underestimating.
+697        self.min = cmp::min(self.min, self.latest);
+698        // Based on RFC6298.
+699        if let Some(smoothed) = self.smoothed {
+700            let adjusted_rtt = if self.min + ack_delay <= self.latest {
+701                self.latest - ack_delay
+702            } else {
+703                self.latest
+704            };
+705            let var_sample = smoothed.abs_diff(adjusted_rtt);
+706            self.var = (3 * self.var + var_sample) / 4;
+707            self.smoothed = Some((7 * smoothed + adjusted_rtt) / 8);
+708        } else {
+709            self.smoothed = Some(self.latest);
+710            self.var = self.latest / 2;
+711            self.min = self.latest;
+712        }
+713    }
+714}
+715
+716#[derive(Default, Debug)]
+717pub(crate) struct PathResponses {
+718    pending: Vec<PathResponse>,
+719}
+720
+721impl PathResponses {
+722    pub(crate) fn push(&mut self, packet: u64, token: u64, network_path: FourTuple) {
+723        /// Arbitrary permissive limit to prevent abuse
+724        const MAX_PATH_RESPONSES: usize = 16;
+725        let response = PathResponse {
+726            packet,
+727            token,
+728            network_path,
+729        };
+730        let existing = self
+731            .pending
+732            .iter_mut()
+733            .find(|x| x.network_path.remote == network_path.remote);
+734        if let Some(existing) = existing {
+735            // Update a queued response
+736            if existing.packet <= packet {
+737                *existing = response;
+738            }
+739            return;
+740        }
+741        if self.pending.len() < MAX_PATH_RESPONSES {
+742            self.pending.push(response);
+743        } else {
+744            // We don't expect to ever hit this with well-behaved peers, so we don't bother dropping
+745            // older challenges.
+746            trace!("ignoring excessive PATH_CHALLENGE");
+747        }
+748    }
+749
+750    pub(crate) fn pop_off_path(&mut self, network_path: FourTuple) -> Option<(u64, FourTuple)> {
+751        let response = *self.pending.last()?;
+752        // We use an exact comparison here, because once we've received for the first time,
+753        // we really should either already have a local_ip, or we will never get one
+754        // (because our OS doesn't support it).
+755        if response.network_path == network_path {
+756            // We don't bother searching further because we expect that the on-path response will
+757            // get drained in the immediate future by a call to `pop_on_path`
+758            return None;
+759        }
+760        self.pending.pop();
+761        Some((response.token, response.network_path))
+762    }
+763
+764    pub(crate) fn pop_on_path(&mut self, network_path: FourTuple) -> Option<u64> {
+765        let response = *self.pending.last()?;
+766        // Using an exact comparison. See explanation in `pop_off_path`.
+767        if response.network_path != network_path {
+768            // We don't bother searching further because we expect that the off-path response will
+769            // get drained in the immediate future by a call to `pop_off_path`
+770            return None;
+771        }
+772        self.pending.pop();
+773        Some(response.token)
+774    }
+775
+776    pub(crate) fn is_empty(&self) -> bool {
+777        self.pending.is_empty()
+778    }
+779}
+780
+781#[derive(Copy, Clone, Debug)]
+782struct PathResponse {
+783    /// The packet number the corresponding PATH_CHALLENGE was received in
+784    packet: u64,
+785    /// The token of the PATH_CHALLENGE
+786    token: u64,
+787    /// The path the corresponding PATH_CHALLENGE was received from
+788    network_path: FourTuple,
+789}
+790
+791/// Summary statistics of packets that have been sent on a particular path, but which have not yet
+792/// been acked or deemed lost
+793#[derive(Debug)]
+794pub(super) struct InFlight {
+795    /// Sum of the sizes of all sent packets considered "in flight" by congestion control
+796    ///
+797    /// The size does not include IP or UDP overhead. Packets only containing ACK frames do not
+798    /// count towards this to ensure congestion control does not impede congestion feedback.
+799    pub(super) bytes: u64,
+800    /// Number of packets in flight containing frames other than ACK and PADDING
+801    ///
+802    /// This can be 0 even when bytes is not 0 because PADDING frames cause a packet to be
+803    /// considered "in flight" by congestion control. However, if this is nonzero, bytes will always
+804    /// also be nonzero.
+805    pub(super) ack_eliciting: u64,
+806}
+807
+808impl InFlight {
+809    fn new() -> Self {
+810        Self {
+811            bytes: 0,
+812            ack_eliciting: 0,
+813        }
+814    }
+815
+816    fn insert(&mut self, packet: &SentPacket) {
+817        self.bytes += u64::from(packet.size);
+818        self.ack_eliciting += u64::from(packet.ack_eliciting);
+819    }
+820
+821    /// Update counters to account for a packet becoming acknowledged, lost, or abandoned
+822    fn remove(&mut self, packet: &SentPacket) {
+823        self.bytes -= u64::from(packet.size);
+824        self.ack_eliciting -= u64::from(packet.ack_eliciting);
+825    }
+826}
+827
+828/// State for QUIC-MULTIPATH PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP frames
+829#[derive(Debug, Clone, Default)]
+830pub(super) struct PathStatusState {
+831    /// The local status
+832    local_status: PathStatus,
+833    /// Local sequence number, for both PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP
+834    ///
+835    /// This is the number of the *next* path status frame to be sent.
+836    local_seq: VarInt,
+837    /// The status set by the remote
+838    remote_status: Option<(VarInt, PathStatus)>,
+839}
+840
+841impl PathStatusState {
+842    /// To be called on received PATH_STATUS_AVAILABLE/PATH_STATUS_BACKUP frames
+843    pub(super) fn remote_update(&mut self, status: PathStatus, seq: VarInt) {
+844        if self.remote_status.is_some_and(|(curr, _)| curr >= seq) {
+845            return trace!(%seq, "ignoring path status update");
+846        }
+847
+848        let prev = self.remote_status.replace((seq, status)).map(|(_, s)| s);
+849        if prev != Some(status) {
+850            debug!(?status, ?seq, "remote changed path status");
+851        }
+852    }
+853
+854    /// Updates the local status
+855    ///
+856    /// If the local status changed, the previous value is returned
+857    pub(super) fn local_update(&mut self, status: PathStatus) -> Option<PathStatus> {
+858        if self.local_status == status {
+859            return None;
+860        }
+861
+862        self.local_seq = self.local_seq.saturating_add(1u8);
+863        Some(std::mem::replace(&mut self.local_status, status))
+864    }
+865
+866    pub(crate) fn seq(&self) -> VarInt {
+867        self.local_seq
+868    }
+869}
+870
+871/// The QUIC-MULTIPATH path status
+872///
+873/// See section "3.3 Path Status Management":
+874/// <https://quicwg.org/multipath/draft-ietf-quic-multipath.html#name-path-status-management>
+875#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
+876pub enum PathStatus {
+877    /// Paths marked with as available will be used when scheduling packets
+878    ///
+879    /// If multiple paths are available, packets will be scheduled on whichever has
+880    /// capacity.
+881    #[default]
+882    Available,
+883    /// Paths marked as backup will only be used if there are no available paths
+884    ///
+885    /// If the max_idle_timeout is specified the path will be kept alive so that it does not
+886    /// expire.
+887    Backup,
+888}
+889
+890/// Application events about paths
+891#[derive(Debug, Clone, PartialEq, Eq)]
+892pub enum PathEvent {
+893    /// A new path has been opened
+894    Opened {
+895        /// Which path is now open
+896        id: PathId,
+897    },
+898    /// A path has been closed
+899    Closed {
+900        /// Which path has been closed
+901        id: PathId,
+902        /// Error code supplied by the peer
+903        /// See <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-error-codes>
+904        /// for a list of known errors.
+905        error_code: VarInt,
+906    },
+907    /// All remaining state for a path has been removed
+908    ///
+909    /// The [`PathEvent::Closed`] would have been emitted for this path earlier.
+910    Abandoned {
+911        /// Which path had its state dropped
+912        id: PathId,
+913        /// The final path stats, they are no longer available via [`Connection::stats`]
+914        ///
+915        /// [`Connection::stats`]: super::Connection::stats
+916        path_stats: PathStats,
+917    },
+918    /// Path was closed locally
+919    LocallyClosed {
+920        /// Path for which the error occurred
+921        id: PathId,
+922        /// The error that occurred
+923        error: PathError,
+924    },
+925    /// The remote changed the status of the path
+926    ///
+927    /// The local status is not changed because of this event. It is up to the application
+928    /// to update the local status, which is used for packet scheduling, when the remote
+929    /// changes the status.
+930    RemoteStatus {
+931        /// Path which has changed status
+932        id: PathId,
+933        /// The new status set by the remote
+934        status: PathStatus,
+935    },
+936    /// Received an observation of our external address from the peer.
+937    ObservedAddr {
+938        /// Path over which the observed address was reported, [`PathId::ZERO`] when multipath is
+939        /// not negotiated
+940        id: PathId,
+941        /// The address observed by the remote over this path
+942        addr: SocketAddr,
+943    },
+944}
+945
+946/// Error from setting path status
+947#[derive(Debug, Error, Clone, PartialEq, Eq)]
+948pub enum SetPathStatusError {
+949    /// Error indicating that a path has not been opened or has already been abandoned
+950    #[error("closed path")]
+951    ClosedPath,
+952    /// Error indicating that this operation requires multipath to be negotiated whereas it hasn't been
+953    #[error("multipath not negotiated")]
+954    MultipathNotNegotiated,
+955}
+956
+957/// Error indicating that a path has not been opened or has already been abandoned
+958#[derive(Debug, Default, Error, Clone, PartialEq, Eq)]
+959#[error("closed path")]
+960pub struct ClosedPath {
+961    pub(super) _private: (),
+962}
+963
+964#[cfg(test)]
+965mod tests {
+966    use super::*;
+967
+968    #[test]
+969    fn test_path_id_saturating_add() {
+970        // add within range behaves normally
+971        let large: PathId = u16::MAX.into();
+972        let next = u32::from(u16::MAX) + 1;
+973        assert_eq!(large.saturating_add(1u8), PathId::from(next));
+974
+975        // outside range saturates
+976        assert_eq!(PathId::MAX.saturating_add(1u8), PathId::MAX)
+977    }
+978}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/qlog.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/qlog.rs.html new file mode 100644 index 000000000..fb087bc33 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/qlog.rs.html @@ -0,0 +1,1075 @@ +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::{EcnCounts, StreamMeta},
+43    packet::{Header, SpaceId},
+44    range_set::ArrayRangeSet,
+45    transport_parameters::TransportParameters,
+46};
+47#[cfg(feature = "qlog")]
+48use crate::{
+49    QlogConfig, Side, TransportErrorCode,
+50    connection::timer::{ConnTimer, PathTimer},
+51    frame::Close,
+52};
+53
+54/// Shareable handle to a single qlog output stream
+55#[cfg(feature = "qlog")]
+56#[derive(Clone)]
+57pub(crate) struct QlogStream(Arc<Mutex<QlogStreamer>>);
+58
+59#[cfg(feature = "qlog")]
+60impl QlogStream {
+61    pub(crate) fn new(
+62        config: QlogConfig,
+63        initial_dst_cid: ConnectionId,
+64        side: Side,
+65        now: Instant,
+66    ) -> Result<Self, qlog::Error> {
+67        let vantage_point = VantagePoint {
+68            name: None,
+69            ty: match side {
+70                Side::Client => qlog::VantagePointType::Client,
+71                Side::Server => qlog::VantagePointType::Server,
+72            },
+73            flow: None,
+74        };
+75
+76        let common_fields = CommonFields {
+77            group_id: Some(initial_dst_cid.to_string()),
+78            ..Default::default()
+79        };
+80
+81        let trace = qlog::TraceSeq::new(
+82            config.title.clone(),
+83            config.description.clone(),
+84            Some(common_fields),
+85            Some(vantage_point),
+86            vec![],
+87        );
+88
+89        let start_time = config.start_time.unwrap_or(now);
+90
+91        let mut streamer = QlogStreamer::new(
+92            config.title,
+93            config.description,
+94            start_time,
+95            trace,
+96            qlog::events::EventImportance::Extra,
+97            config.writer,
+98        );
+99
+100        streamer.start_log()?;
+101        Ok(Self(Arc::new(Mutex::new(streamer))))
+102    }
+103
+104    fn emit_event(&self, event: EventData, now: Instant) {
+105        self.emit_event_with_tuple_id(event, now, None);
+106    }
+107
+108    fn emit_event_with_tuple_id(
+109        &self,
+110        event: EventData,
+111        now: Instant,
+112        network_path: Option<String>,
+113    ) {
+114        // Time will be overwritten by `add_event_with_instant`
+115        let mut event = Event::with_time(0.0, event);
+116        event.tuple = network_path;
+117        let mut qlog_streamer = self.0.lock().unwrap();
+118        if let Err(e) = qlog_streamer.add_event_with_instant(event, now) {
+119            warn!("could not emit qlog event: {e}");
+120        }
+121    }
+122}
+123
+124/// A [`QlogStream`] that may be either dynamically disabled or compiled out entirely
+125#[derive(Clone, Default)]
+126pub(crate) struct QlogSink {
+127    #[cfg(feature = "qlog")]
+128    stream: Option<QlogStream>,
+129}
+130
+131impl QlogSink {
+132    #[cfg(feature = "qlog")]
+133    pub(crate) fn new(
+134        config: QlogConfig,
+135        initial_dst_cid: ConnectionId,
+136        side: Side,
+137        now: Instant,
+138    ) -> Self {
+139        let stream = QlogStream::new(config, initial_dst_cid, side, now)
+140            .inspect_err(|err| warn!("failed to initialize qlog streamer: {err}"))
+141            .ok();
+142        Self { stream }
+143    }
+144
+145    pub(crate) fn emit_connection_started(
+146        &self,
+147        now: Instant,
+148        loc_cid: ConnectionId,
+149        rem_cid: ConnectionId,
+150        remote: SocketAddr,
+151        local_ip: Option<IpAddr>,
+152        transport_params: &TransportParameters,
+153    ) {
+154        #[cfg(feature = "qlog")]
+155        {
+156            let Some(stream) = self.stream.as_ref() else {
+157                return;
+158            };
+159            stream.emit_event(
+160                EventData::ConnectionStarted(ConnectionStarted {
+161                    local: tuple_endpoint_info(local_ip, None, Some(loc_cid)),
+162                    remote: tuple_endpoint_info(
+163                        Some(remote.ip()),
+164                        Some(remote.port()),
+165                        Some(rem_cid),
+166                    ),
+167                }),
+168                now,
+169            );
+170
+171            let params = transport_params.to_qlog(TransportInitiator::Local);
+172            let event = EventData::ParametersSet(params);
+173            stream.emit_event(event, now);
+174        }
+175    }
+176
+177    pub(super) fn emit_recovery_metrics(&self, path_id: PathId, path: &mut PathData, now: Instant) {
+178        #[cfg(feature = "qlog")]
+179        {
+180            let Some(stream) = self.stream.as_ref() else {
+181                return;
+182            };
+183
+184            let Some(metrics) = path.qlog_recovery_metrics(path_id) else {
+185                return;
+186            };
+187
+188            stream.emit_event(EventData::MetricsUpdated(metrics), now);
+189        }
+190    }
+191
+192    pub(super) fn emit_packet_lost(
+193        &self,
+194        pn: u64,
+195        info: &SentPacket,
+196        loss_delay: Duration,
+197        space: SpaceId,
+198        now: Instant,
+199    ) {
+200        #[cfg(feature = "qlog")]
+201        {
+202            let Some(stream) = self.stream.as_ref() else {
+203                return;
+204            };
+205
+206            let event = PacketLost {
+207                header: Some(PacketHeader {
+208                    packet_number: Some(pn),
+209                    packet_type: packet_type(space, false),
+210                    length: Some(info.size),
+211                    ..Default::default()
+212                }),
+213                frames: None,
+214                trigger: Some(
+215                    match info.time_sent.saturating_duration_since(now) >= loss_delay {
+216                        true => PacketLostTrigger::TimeThreshold,
+217                        false => PacketLostTrigger::ReorderingThreshold,
+218                    },
+219                ),
+220                is_mtu_probe_packet: None,
+221            };
+222
+223            stream.emit_event(EventData::PacketLost(event), now);
+224        }
+225    }
+226
+227    pub(super) fn emit_peer_transport_params_restored(&self, conn: &Connection, now: Instant) {
+228        #[cfg(feature = "qlog")]
+229        {
+230            let Some(stream) = self.stream.as_ref() else {
+231                return;
+232            };
+233            let params = conn.peer_params.to_qlog_restored();
+234            let event = EventData::ParametersRestored(params);
+235            stream.emit_event(event, now);
+236        }
+237    }
+238
+239    pub(super) fn emit_peer_transport_params_received(&self, conn: &Connection, now: Instant) {
+240        #[cfg(feature = "qlog")]
+241        {
+242            let Some(stream) = self.stream.as_ref() else {
+243                return;
+244            };
+245            let params = conn.peer_params.to_qlog(TransportInitiator::Remote);
+246            let event = EventData::ParametersSet(params);
+247            stream.emit_event(event, now);
+248        }
+249    }
+250
+251    pub(super) fn emit_tuple_assigned(&self, path_id: PathId, tuple: FourTuple, now: Instant) {
+252        #[cfg(feature = "qlog")]
+253        {
+254            let Some(stream) = self.stream.as_ref() else {
+255                return;
+256            };
+257            let tuple_id = fmt_tuple_id(path_id.as_u32() as u64);
+258            let event = TupleAssigned {
+259                tuple_id,
+260                tuple_local: tuple
+261                    .local_ip
+262                    .map(|local_ip| tuple_endpoint_info(Some(local_ip), None, None)),
+263                tuple_remote: Some(tuple_endpoint_info(
+264                    Some(tuple.remote.ip()),
+265                    Some(tuple.remote.port()),
+266                    None,
+267                )),
+268            };
+269
+270            stream.emit_event(EventData::TupleAssigned(event), now);
+271        }
+272    }
+273
+274    pub(super) fn emit_packet_sent(&self, packet: QlogSentPacket, now: Instant) {
+275        #[cfg(feature = "qlog")]
+276        {
+277            let Some(stream) = self.stream.as_ref() else {
+278                return;
+279            };
+280            let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id);
+281            stream.emit_event_with_tuple_id(EventData::PacketSent(packet.inner), now, tuple_id);
+282        }
+283    }
+284
+285    pub(super) fn emit_packet_received(&self, packet: QlogRecvPacket, now: Instant) {
+286        #[cfg(feature = "qlog")]
+287        {
+288            let Some(stream) = self.stream.as_ref() else {
+289                return;
+290            };
+291            let mut packet = packet;
+292            packet.emit_padding();
+293            let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id);
+294            let event = packet.inner;
+295            stream.emit_event_with_tuple_id(EventData::PacketReceived(event), now, tuple_id);
+296        }
+297    }
+298
+299    /// Emits a timer event.
+300    ///
+301    /// This function is not public: Instead, create a [`QlogSinkWithTime`] via [`Self::with_time`] and use
+302    /// its `emit_timer_` methods.
+303    #[cfg(feature = "qlog")]
+304    fn emit_timer(&self, timer: Timer, op: TimerOp, now: Instant) {
+305        let Some(stream) = self.stream.as_ref() else {
+306            return;
+307        };
+308
+309        let timer_type: Option<TimerType> = match timer {
+310            Timer::Conn(conn_timer) => match conn_timer {
+311                ConnTimer::Idle => Some(QlogTimerType::IdleTimeout.into()),
+312                ConnTimer::Close => Some(TimerType::custom("close")),
+313                ConnTimer::KeyDiscard => Some(TimerType::custom("key_discard")),
+314                ConnTimer::KeepAlive => Some(TimerType::custom("keep_alive")),
+315                ConnTimer::PushNewCid => Some(TimerType::custom("push_new_cid")),
+316            },
+317            Timer::PerPath(_, path_timer) => match path_timer {
+318                PathTimer::LossDetection => Some(QlogTimerType::LossTimeout.into()),
+319                PathTimer::PathIdle => Some(TimerType::custom("path_idle")),
+320                PathTimer::PathValidation => Some(QlogTimerType::PathValidation.into()),
+321                PathTimer::PathChallengeLost => Some(TimerType::custom("path_challenge_lost")),
+322                PathTimer::PathOpen => Some(TimerType::custom("path_open")),
+323                PathTimer::PathKeepAlive => Some(TimerType::custom("path_keep_alive")),
+324                PathTimer::Pacing => Some(TimerType::custom("pacing")),
+325                PathTimer::MaxAckDelay => Some(QlogTimerType::Ack.into()),
+326                PathTimer::DiscardPath => Some(TimerType::custom("discard_path")),
+327            },
+328        };
+329
+330        let Some(timer_type) = timer_type else {
+331            return;
+332        };
+333
+334        let delta = match op {
+335            TimerOp::Set(instant) => instant
+336                .checked_duration_since(now)
+337                .map(|dur| dur.as_secs_f32() * 1000.),
+338            _ => None,
+339        };
+340        let path_id = match timer {
+341            Timer::Conn(_) => None,
+342            Timer::PerPath(path_id, _) => Some(path_id.as_u32() as u64),
+343        };
+344
+345        let event_type = match op {
+346            TimerOp::Set(_) => TimerEventType::Set,
+347            TimerOp::Expire => TimerEventType::Expired,
+348            TimerOp::Cancelled => TimerEventType::Cancelled,
+349        };
+350
+351        let event = TimerUpdated {
+352            path_id,
+353            timer_type: Some(timer_type),
+354            timer_id: None,
+355            packet_number_space: None,
+356            event_type,
+357            delta,
+358        };
+359        stream.emit_event(EventData::TimerUpdated(event), now);
+360    }
+361
+362    /// Returns a [`QlogSinkWithTime`] that passes along a `now` timestamp.
+363    ///
+364    /// This may be used if you want to pass a [`QlogSink`] downwards together with the current
+365    /// `now` timestamp, to not have to pass the latter separately as an additional argument just
+366    /// for qlog support.
+367    pub(super) fn with_time(&self, now: Instant) -> QlogSinkWithTime<'_> {
+368        #[cfg(feature = "qlog")]
+369        let s = QlogSinkWithTime { sink: self, now };
+370        #[cfg(not(feature = "qlog"))]
+371        let s = QlogSinkWithTime {
+372            _phantom: PhantomData,
+373        };
+374        s
+375    }
+376}
+377
+378/// A [`QlogSink`] with a `now` timestamp.
+379#[derive(Clone)]
+380pub(super) struct QlogSinkWithTime<'a> {
+381    #[cfg(feature = "qlog")]
+382    sink: &'a QlogSink,
+383    #[cfg(feature = "qlog")]
+384    now: Instant,
+385    #[cfg(not(feature = "qlog"))]
+386    _phantom: PhantomData<&'a ()>,
+387}
+388
+389impl<'a> QlogSinkWithTime<'a> {
+390    pub(super) fn emit_timer_stop(&self, timer: Timer) {
+391        #[cfg(feature = "qlog")]
+392        self.sink.emit_timer(timer, TimerOp::Cancelled, self.now)
+393    }
+394
+395    pub(super) fn emit_timer_set(&self, timer: Timer, expire_at: Instant) {
+396        #[cfg(feature = "qlog")]
+397        self.sink
+398            .emit_timer(timer, TimerOp::Set(expire_at), self.now)
+399    }
+400
+401    pub(super) fn emit_timer_expire(&self, timer: Timer) {
+402        #[cfg(feature = "qlog")]
+403        self.sink.emit_timer(timer, TimerOp::Expire, self.now)
+404    }
+405}
+406
+407#[cfg(feature = "qlog")]
+408enum TimerOp {
+409    Set(Instant),
+410    Expire,
+411    Cancelled,
+412}
+413
+414/// Info about a sent packet. Zero-sized struct if `qlog` feature is not enabled.
+415#[derive(Default)]
+416pub(crate) struct QlogSentPacket {
+417    #[cfg(feature = "qlog")]
+418    inner: PacketSent,
+419}
+420
+421impl QlogSentPacket {
+422    /// Sets data from the packet header.
+423    pub(crate) fn header(
+424        &mut self,
+425        header: &Header,
+426        pn: Option<u64>,
+427        space: SpaceId,
+428        is_0rtt: bool,
+429        path_id: PathId,
+430    ) {
+431        #[cfg(feature = "qlog")]
+432        {
+433            self.inner.header.scid = header.src_cid().map(stringify_cid);
+434            self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
+435            self.inner.header.packet_number = pn;
+436            self.inner.header.packet_type = packet_type(space, is_0rtt);
+437            self.inner.header.path_id = Some(path_id.as_u32() as u64);
+438        }
+439    }
+440
+441    /// Adds a frame by pushing a [`Frame`].
+442    pub(crate) fn frame(&mut self, frame: &Frame) {
+443        #[cfg(feature = "qlog")]
+444        self.frame_raw(frame.to_qlog())
+445    }
+446
+447    /// Adds a PADDING frame.
+448    ///
+449    /// This is a no-op if the `qlog` feature is not enabled.
+450    pub(crate) fn frame_padding(&mut self, count: usize) {
+451        #[cfg(feature = "qlog")]
+452        self.frame_raw(QuicFrame::Padding {
+453            raw: Some(RawInfo {
+454                length: Some(count as u64),
+455                payload_length: Some(count as u64),
+456                data: None,
+457            }),
+458        });
+459    }
+460
+461    /// Adds an ACK frame.
+462    ///
+463    /// This is a no-op if the `qlog` feature is not enabled.
+464    pub(crate) fn frame_ack(
+465        &mut self,
+466        delay: u64,
+467        ranges: &ArrayRangeSet,
+468        ecn: Option<&EcnCounts>,
+469    ) {
+470        #[cfg(feature = "qlog")]
+471        self.frame_raw(QuicFrame::Ack {
+472            ack_delay: Some(delay as f32),
+473            acked_ranges: Some(AckedRanges::Double(
+474                ranges
+475                    .iter()
+476                    .map(|range| (range.start, range.end))
+477                    .collect(),
+478            )),
+479            ect1: ecn.map(|e| e.ect1),
+480            ect0: ecn.map(|e| e.ect0),
+481            ce: ecn.map(|e| e.ce),
+482            raw: None,
+483        });
+484    }
+485
+486    /// Adds a PATH_ACK frame.
+487    ///
+488    /// This is a no-op if the `qlog` feature is not enabled.
+489    pub(crate) fn frame_path_ack(
+490        &mut self,
+491        path_id: PathId,
+492        delay: u64,
+493        ranges: &ArrayRangeSet,
+494        ecn: Option<&EcnCounts>,
+495    ) {
+496        #[cfg(feature = "qlog")]
+497        self.frame_raw(QuicFrame::PathAck {
+498            path_id: path_id.as_u32() as u64,
+499            ack_delay: Some(delay as f32),
+500            acked_ranges: Some(AckedRanges::Double(
+501                ranges
+502                    .iter()
+503                    .map(|range| (range.start, range.end))
+504                    .collect(),
+505            )),
+506            ect1: ecn.map(|e| e.ect1),
+507            ect0: ecn.map(|e| e.ect0),
+508            ce: ecn.map(|e| e.ce),
+509            raw: None,
+510        });
+511    }
+512
+513    /// Adds a DATAGRAM frame.
+514    ///
+515    /// This is a no-op if the `qlog` feature is not enabled.
+516    pub(crate) fn frame_datagram(&mut self, len: u64) {
+517        #[cfg(feature = "qlog")]
+518        self.frame_raw(QuicFrame::Datagram {
+519            raw: Some(RawInfo {
+520                length: Some(len),
+521                ..Default::default()
+522            }),
+523        });
+524    }
+525
+526    /// Adds a STREAM frame.
+527    ///
+528    /// This is a no-op if the `qlog` feature is not enabled.
+529    pub(crate) fn frame_stream(&mut self, meta: &StreamMeta) {
+530        #[cfg(feature = "qlog")]
+531        self.frame_raw(QuicFrame::Stream {
+532            stream_id: meta.id.into(),
+533            offset: Some(meta.offsets.start),
+534            fin: Some(meta.fin),
+535            raw: Some(RawInfo {
+536                length: Some(meta.offsets.end - meta.offsets.start),
+537                ..Default::default()
+538            }),
+539        });
+540    }
+541
+542    /// Adds a frame by pushing a [`QuicFrame`].
+543    ///
+544    /// This function is only available if the `qlog` feature is enabled, because constructing a [`QuicFrame`] may involve
+545    /// calculations which shouldn't be performed if the `qlog` feature is disabled.
+546    #[cfg(feature = "qlog")]
+547    fn frame_raw(&mut self, frame: QuicFrame) {
+548        self.inner.frames.get_or_insert_default().push(frame);
+549    }
+550
+551    /// Finalizes the packet by setting the final packet length (after encryption).
+552    pub(super) fn finalize(&mut self, len: usize) {
+553        #[cfg(feature = "qlog")]
+554        {
+555            self.inner.header.length = Some(len as u16);
+556        }
+557    }
+558}
+559
+560/// Info about a received packet. Zero-sized struct if `qlog` feature is not enabled.
+561pub(crate) struct QlogRecvPacket {
+562    #[cfg(feature = "qlog")]
+563    inner: PacketReceived,
+564    #[cfg(feature = "qlog")]
+565    padding: usize,
+566}
+567
+568impl QlogRecvPacket {
+569    /// Creates a new [`QlogRecvPacket`]. Noop if `qlog` feature is not enabled.
+570    ///
+571    /// `len` is the packet's full length (before decryption).
+572    pub(crate) fn new(len: usize) -> Self {
+573        #[cfg(not(feature = "qlog"))]
+574        let this = Self {};
+575
+576        #[cfg(feature = "qlog")]
+577        let this = {
+578            let mut this = Self {
+579                inner: Default::default(),
+580                padding: 0,
+581            };
+582            this.inner.header.length = Some(len as u16);
+583            this
+584        };
+585
+586        this
+587    }
+588
+589    /// Adds info from the packet header.
+590    pub(crate) fn header(&mut self, header: &Header, pn: Option<u64>, path_id: PathId) {
+591        #[cfg(feature = "qlog")]
+592        {
+593            let is_0rtt = !header.is_1rtt();
+594            self.inner.header.scid = header.src_cid().map(stringify_cid);
+595            self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
+596            self.inner.header.packet_number = pn;
+597            self.inner.header.packet_type = packet_type(header.space(), is_0rtt);
+598            self.inner.header.path_id = Some(path_id.as_u32() as u64);
+599        }
+600    }
+601
+602    /// Adds a frame.
+603    pub(crate) fn frame(&mut self, frame: &Frame) {
+604        #[cfg(feature = "qlog")]
+605        {
+606            if matches!(frame, crate::Frame::Padding) {
+607                self.padding += 1;
+608            } else {
+609                self.emit_padding();
+610                self.inner
+611                    .frames
+612                    .get_or_insert_default()
+613                    .push(frame.to_qlog())
+614            }
+615        }
+616    }
+617
+618    #[cfg(feature = "qlog")]
+619    fn emit_padding(&mut self) {
+620        if self.padding > 0 {
+621            self.inner
+622                .frames
+623                .get_or_insert_default()
+624                .push(QuicFrame::Padding {
+625                    raw: Some(RawInfo {
+626                        length: Some(self.padding as u64),
+627                        payload_length: Some(self.padding as u64),
+628                        data: None,
+629                    }),
+630                });
+631            self.padding = 0;
+632        }
+633    }
+634}
+635
+636#[cfg(feature = "qlog")]
+637impl Frame {
+638    /// Converts a [`crate::Frame`] into a [`QuicFrame`].
+639    pub(crate) fn to_qlog(&self) -> QuicFrame {
+640        match self {
+641            Self::Padding => QuicFrame::Padding {
+642                raw: Some(RawInfo {
+643                    length: None,
+644                    payload_length: Some(1),
+645                    data: None,
+646                }),
+647            },
+648            Self::Ping => QuicFrame::Ping { raw: None },
+649            Self::Ack(f) => QuicFrame::Ack {
+650                ack_delay: Some(f.delay as f32),
+651                acked_ranges: Some(AckedRanges::Double(
+652                    f.iter()
+653                        .map(|range| (*range.start(), *range.end()))
+654                        .collect(),
+655                )),
+656                ect1: f.ecn.as_ref().map(|e| e.ect1),
+657                ect0: f.ecn.as_ref().map(|e| e.ect0),
+658                ce: f.ecn.as_ref().map(|e| e.ce),
+659                raw: None,
+660            },
+661            Self::ResetStream(f) => QuicFrame::ResetStream {
+662                stream_id: f.id.into(),
+663                error_code: Some(f.error_code.into_inner()),
+664                final_size: f.final_offset.into(),
+665                error: ApplicationError::Unknown,
+666                raw: None,
+667            },
+668            Self::StopSending(f) => QuicFrame::StopSending {
+669                stream_id: f.id.into(),
+670                error_code: Some(f.error_code.into_inner()),
+671                error: ApplicationError::Unknown,
+672                raw: None,
+673            },
+674            Self::Crypto(f) => QuicFrame::Crypto {
+675                offset: f.offset,
+676                raw: Some(RawInfo {
+677                    length: Some(f.data.len() as u64),
+678                    ..Default::default()
+679                }),
+680            },
+681            Self::NewToken(f) => QuicFrame::NewToken {
+682                token: qlog::Token {
+683                    ty: Some(TokenType::Retry),
+684                    raw: Some(RawInfo {
+685                        data: HexSlice::maybe_string(Some(&f.token)),
+686                        length: Some(f.token.len() as u64),
+687                        payload_length: None,
+688                    }),
+689                    details: None,
+690                },
+691                raw: None,
+692            },
+693            Self::Stream(s) => QuicFrame::Stream {
+694                stream_id: s.id.into(),
+695                offset: Some(s.offset),
+696                fin: Some(s.fin),
+697                raw: Some(RawInfo {
+698                    length: Some(s.data.len() as u64),
+699                    ..Default::default()
+700                }),
+701            },
+702            Self::MaxData(v) => QuicFrame::MaxData {
+703                maximum: (*v).into(),
+704                raw: None,
+705            },
+706            Self::MaxStreamData { id, offset } => QuicFrame::MaxStreamData {
+707                stream_id: (*id).into(),
+708                maximum: *offset,
+709                raw: None,
+710            },
+711            Self::MaxStreams { dir, count } => QuicFrame::MaxStreams {
+712                maximum: *count,
+713                stream_type: (*dir).into(),
+714                raw: None,
+715            },
+716            Self::DataBlocked { offset } => QuicFrame::DataBlocked {
+717                limit: *offset,
+718                raw: None,
+719            },
+720            Self::StreamDataBlocked { id, offset } => QuicFrame::StreamDataBlocked {
+721                stream_id: (*id).into(),
+722                limit: *offset,
+723                raw: None,
+724            },
+725            Self::StreamsBlocked { dir, limit } => QuicFrame::StreamsBlocked {
+726                stream_type: (*dir).into(),
+727                limit: *limit,
+728                raw: None,
+729            },
+730            Self::NewConnectionId(f) => QuicFrame::NewConnectionId {
+731                sequence_number: f.sequence,
+732                retire_prior_to: f.retire_prior_to,
+733                connection_id_length: Some(f.id.len() as u8),
+734                connection_id: f.id.to_string(),
+735                stateless_reset_token: Some(f.reset_token.to_string()),
+736                raw: None,
+737            },
+738            Self::RetireConnectionId(f) => QuicFrame::RetireConnectionId {
+739                sequence_number: f.sequence,
+740                raw: None,
+741            },
+742            Self::PathChallenge(token) => QuicFrame::PathChallenge {
+743                data: Some(token.to_string()),
+744                raw: None,
+745            },
+746            Self::PathResponse(token) => QuicFrame::PathResponse {
+747                data: Some(token.to_string()),
+748                raw: None,
+749            },
+750            Self::Close(close) => match close {
+751                Close::Connection(f) => {
+752                    let (error, error_code) = transport_error(f.error_code);
+753                    let error = error.map(|transport_error| {
+754                        ConnectionClosedFrameError::TransportError(transport_error)
+755                    });
+756                    QuicFrame::ConnectionClose {
+757                        error_space: Some(ErrorSpace::TransportError),
+758                        error,
+759                        error_code,
+760                        reason: String::from_utf8(f.reason.to_vec()).ok(),
+761                        reason_bytes: None,
+762                        trigger_frame_type: None,
+763                    }
+764                }
+765                Close::Application(f) => QuicFrame::ConnectionClose {
+766                    error_space: Some(ErrorSpace::ApplicationError),
+767                    error: None,
+768                    error_code: Some(f.error_code.into_inner()),
+769                    reason: String::from_utf8(f.reason.to_vec()).ok(),
+770                    reason_bytes: None,
+771                    trigger_frame_type: None,
+772                },
+773            },
+774            Self::Datagram(d) => QuicFrame::Datagram {
+775                raw: Some(RawInfo {
+776                    length: Some(d.data.len() as u64),
+777                    ..Default::default()
+778                }),
+779            },
+780            Self::HandshakeDone => QuicFrame::HandshakeDone { raw: None },
+781            Self::PathAck(ack) => QuicFrame::PathAck {
+782                path_id: ack.path_id.as_u32().into(),
+783                ack_delay: Some(ack.delay as f32),
+784                ect1: ack.ecn.as_ref().map(|e| e.ect1),
+785                ect0: ack.ecn.as_ref().map(|e| e.ect0),
+786                ce: ack.ecn.as_ref().map(|e| e.ce),
+787                raw: None,
+788                acked_ranges: Some(AckedRanges::Double(
+789                    ack.into_iter()
+790                        .map(|range| (*range.start(), *range.end()))
+791                        .collect(),
+792                )),
+793            },
+794            Self::PathAbandon(frame) => QuicFrame::PathAbandon {
+795                path_id: frame.path_id.as_u32().into(),
+796                error_code: frame.error_code.into(),
+797                raw: None,
+798            },
+799            Self::PathStatusAvailable(frame) => QuicFrame::PathStatusAvailable {
+800                path_id: frame.path_id.as_u32().into(),
+801                path_status_sequence_number: frame.status_seq_no.into(),
+802                raw: None,
+803            },
+804            Self::PathStatusBackup(frame) => QuicFrame::PathStatusBackup {
+805                path_id: frame.path_id.as_u32().into(),
+806                path_status_sequence_number: frame.status_seq_no.into(),
+807                raw: None,
+808            },
+809            Self::PathsBlocked(frame) => QuicFrame::PathsBlocked {
+810                maximum_path_id: frame.0.as_u32().into(),
+811                raw: None,
+812            },
+813            Self::PathCidsBlocked(frame) => QuicFrame::PathCidsBlocked {
+814                path_id: frame.path_id.as_u32().into(),
+815                next_sequence_number: frame.next_seq.into(),
+816                raw: None,
+817            },
+818            Self::MaxPathId(id) => QuicFrame::MaxPathId {
+819                maximum_path_id: id.0.as_u32().into(),
+820                raw: None,
+821            },
+822            Self::AckFrequency(f) => QuicFrame::AckFrequency {
+823                sequence_number: f.sequence.into_inner(),
+824                ack_eliciting_threshold: f.ack_eliciting_threshold.into_inner(),
+825                requested_max_ack_delay: f.request_max_ack_delay.into_inner(),
+826                reordering_threshold: f.reordering_threshold.into_inner(),
+827                raw: None,
+828            },
+829            Self::ImmediateAck => QuicFrame::ImmediateAck { raw: None },
+830            Self::ObservedAddr(f) => QuicFrame::ObservedAddress {
+831                sequence_number: f.seq_no.into_inner(),
+832                ip_v4: match f.ip {
+833                    IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+834                    IpAddr::V6(ipv6_addr) => None,
+835                },
+836                ip_v6: match f.ip {
+837                    IpAddr::V4(ipv4_addr) => None,
+838                    IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+839                },
+840                port: f.port,
+841                raw: None,
+842            },
+843            Self::AddAddress(f) => QuicFrame::AddAddress {
+844                sequence_number: f.seq_no.into_inner(),
+845                ip_v4: match f.ip {
+846                    IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+847                    IpAddr::V6(ipv6_addr) => None,
+848                },
+849                ip_v6: match f.ip {
+850                    IpAddr::V4(ipv4_addr) => None,
+851                    IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+852                },
+853                port: f.port,
+854            },
+855            Self::ReachOut(f) => QuicFrame::ReachOut {
+856                round: f.round.into_inner(),
+857                ip_v4: match f.ip {
+858                    IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+859                    IpAddr::V6(ipv6_addr) => None,
+860                },
+861                ip_v6: match f.ip {
+862                    IpAddr::V4(ipv4_addr) => None,
+863                    IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+864                },
+865                port: f.port,
+866            },
+867            Self::RemoveAddress(f) => QuicFrame::RemoveAddress {
+868                sequence_number: f.seq_no.into_inner(),
+869            },
+870        }
+871    }
+872}
+873
+874#[cfg(feature = "qlog")]
+875impl From<crate::Dir> for StreamType {
+876    fn from(value: crate::Dir) -> Self {
+877        match value {
+878            crate::Dir::Bi => Self::Bidirectional,
+879            crate::Dir::Uni => Self::Unidirectional,
+880        }
+881    }
+882}
+883
+884#[cfg(feature = "qlog")]
+885fn packet_type(space: SpaceId, is_0rtt: bool) -> PacketType {
+886    match space {
+887        SpaceId::Initial => PacketType::Initial,
+888        SpaceId::Handshake => PacketType::Handshake,
+889        SpaceId::Data if is_0rtt => PacketType::ZeroRtt,
+890        SpaceId::Data => PacketType::OneRtt,
+891    }
+892}
+893
+894#[cfg(feature = "qlog")]
+895fn stringify_cid(cid: ConnectionId) -> String {
+896    format!("{cid}")
+897}
+898
+899#[cfg(feature = "qlog")]
+900fn tuple_endpoint_info(
+901    ip: Option<IpAddr>,
+902    port: Option<u16>,
+903    cid: Option<ConnectionId>,
+904) -> TupleEndpointInfo {
+905    let (ip_v4, port_v4, ip_v6, port_v6) = match ip {
+906        Some(addr) => match addr {
+907            IpAddr::V4(ipv4_addr) => (Some(ipv4_addr.to_string()), port, None, None),
+908            IpAddr::V6(ipv6_addr) => (None, None, Some(ipv6_addr.to_string()), port),
+909        },
+910        None => (None, None, None, None),
+911    };
+912    TupleEndpointInfo {
+913        ip_v4,
+914        port_v4,
+915        ip_v6,
+916        port_v6,
+917        connection_ids: cid.map(|cid| vec![cid.to_string()]),
+918    }
+919}
+920
+921#[cfg(feature = "qlog")]
+922fn transport_error(code: TransportErrorCode) -> (Option<quic::TransportError>, Option<u64>) {
+923    let transport_error = match code {
+924        TransportErrorCode::NO_ERROR => Some(quic::TransportError::NoError),
+925        TransportErrorCode::INTERNAL_ERROR => Some(quic::TransportError::InternalError),
+926        TransportErrorCode::CONNECTION_REFUSED => Some(quic::TransportError::ConnectionRefused),
+927        TransportErrorCode::FLOW_CONTROL_ERROR => Some(quic::TransportError::FlowControlError),
+928        TransportErrorCode::STREAM_LIMIT_ERROR => Some(quic::TransportError::StreamLimitError),
+929        TransportErrorCode::STREAM_STATE_ERROR => Some(quic::TransportError::StreamStateError),
+930        TransportErrorCode::FINAL_SIZE_ERROR => Some(quic::TransportError::FinalSizeError),
+931        TransportErrorCode::FRAME_ENCODING_ERROR => Some(quic::TransportError::FrameEncodingError),
+932        TransportErrorCode::TRANSPORT_PARAMETER_ERROR => {
+933            Some(quic::TransportError::TransportParameterError)
+934        }
+935        TransportErrorCode::CONNECTION_ID_LIMIT_ERROR => {
+936            Some(quic::TransportError::ConnectionIdLimitError)
+937        }
+938        TransportErrorCode::PROTOCOL_VIOLATION => Some(quic::TransportError::ProtocolViolation),
+939        TransportErrorCode::INVALID_TOKEN => Some(quic::TransportError::InvalidToken),
+940        TransportErrorCode::APPLICATION_ERROR => Some(quic::TransportError::ApplicationError),
+941        TransportErrorCode::CRYPTO_BUFFER_EXCEEDED => {
+942            Some(quic::TransportError::CryptoBufferExceeded)
+943        }
+944        TransportErrorCode::KEY_UPDATE_ERROR => Some(quic::TransportError::KeyUpdateError),
+945        TransportErrorCode::AEAD_LIMIT_REACHED => Some(quic::TransportError::AeadLimitReached),
+946        TransportErrorCode::NO_VIABLE_PATH => Some(quic::TransportError::NoViablePath),
+947        // multipath
+948        TransportErrorCode::APPLICATION_ABANDON_PATH => {
+949            Some(quic::TransportError::ApplicationAbandonPath)
+950        }
+951        TransportErrorCode::PATH_RESOURCE_LIMIT_REACHED => {
+952            Some(quic::TransportError::PathResourceLimitReached)
+953        }
+954        TransportErrorCode::PATH_UNSTABLE_OR_POOR => Some(quic::TransportError::PathUnstableOrPoor),
+955        TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH => {
+956            Some(quic::TransportError::NoCidsAvailableForPath)
+957        }
+958        _ => None,
+959    };
+960    let code = match transport_error {
+961        None => Some(code.into()),
+962        Some(_) => None,
+963    };
+964    (transport_error, code)
+965}
+966
+967#[cfg(feature = "qlog")]
+968fn fmt_tuple_id(path_id: u64) -> String {
+969    format!("p{path_id}")
+970}
+971
+972#[cfg(feature = "qlog")]
+973impl TransportParameters {
+974    fn to_qlog(self, initiator: TransportInitiator) -> ParametersSet {
+975        ParametersSet {
+976            initiator: Some(initiator),
+977            resumption_allowed: None,
+978            early_data_enabled: None,
+979            tls_cipher: None,
+980            original_destination_connection_id: self
+981                .original_dst_cid
+982                .as_ref()
+983                .map(ToString::to_string),
+984            initial_source_connection_id: self.initial_src_cid.as_ref().map(ToString::to_string),
+985            retry_source_connection_id: self.retry_src_cid.as_ref().map(ToString::to_string),
+986            stateless_reset_token: self.stateless_reset_token.as_ref().map(ToString::to_string),
+987            disable_active_migration: Some(self.disable_active_migration),
+988            max_idle_timeout: Some(self.max_idle_timeout.into()),
+989            max_udp_payload_size: Some(self.max_udp_payload_size.into()),
+990            ack_delay_exponent: Some(self.ack_delay_exponent.into()),
+991            max_ack_delay: Some(self.max_ack_delay.into()),
+992            active_connection_id_limit: Some(self.active_connection_id_limit.into()),
+993            initial_max_data: Some(self.initial_max_data.into()),
+994            initial_max_stream_data_bidi_local: Some(
+995                self.initial_max_stream_data_bidi_local.into(),
+996            ),
+997            initial_max_stream_data_bidi_remote: Some(
+998                self.initial_max_stream_data_bidi_remote.into(),
+999            ),
+1000            initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
+1001            initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
+1002            initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
+1003            preferred_address: self.preferred_address.as_ref().map(Into::into),
+1004            min_ack_delay: self.min_ack_delay.map(Into::into),
+1005            address_discovery: self.address_discovery_role.to_qlog(),
+1006            initial_max_path_id: self.initial_max_path_id.map(|p| p.as_u32() as u64),
+1007            max_remote_nat_traversal_addresses: self
+1008                .max_remote_nat_traversal_addresses
+1009                .map(|v| u64::from(v.get())),
+1010            max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
+1011            grease_quic_bit: Some(self.grease_quic_bit),
+1012            unknown_parameters: Default::default(),
+1013        }
+1014    }
+1015
+1016    fn to_qlog_restored(self) -> ParametersRestored {
+1017        ParametersRestored {
+1018            disable_active_migration: Some(self.disable_active_migration),
+1019            max_idle_timeout: Some(self.max_idle_timeout.into()),
+1020            max_udp_payload_size: Some(self.max_udp_payload_size.into()),
+1021            active_connection_id_limit: Some(self.active_connection_id_limit.into()),
+1022            initial_max_data: Some(self.initial_max_data.into()),
+1023            initial_max_stream_data_bidi_local: Some(
+1024                self.initial_max_stream_data_bidi_local.into(),
+1025            ),
+1026            initial_max_stream_data_bidi_remote: Some(
+1027                self.initial_max_stream_data_bidi_remote.into(),
+1028            ),
+1029            initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
+1030            initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
+1031            initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
+1032            max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
+1033            grease_quic_bit: Some(self.grease_quic_bit),
+1034        }
+1035    }
+1036}
+1037
+1038#[cfg(feature = "qlog")]
+1039impl From<&crate::transport_parameters::PreferredAddress> for PreferredAddress {
+1040    fn from(value: &crate::transport_parameters::PreferredAddress) -> Self {
+1041        let port_v4 = value.address_v4.map(|addr| addr.port()).unwrap_or_default();
+1042        let port_v6 = value.address_v6.map(|addr| addr.port()).unwrap_or_default();
+1043        let ip_v4 = value
+1044            .address_v4
+1045            .map(|addr| addr.ip().to_string())
+1046            .unwrap_or_default();
+1047        let ip_v6 = value
+1048            .address_v6
+1049            .map(|addr| addr.ip().to_string())
+1050            .unwrap_or_default();
+1051        let connection_id = value.connection_id.to_string();
+1052        let stateless_reset_token = value.stateless_reset_token.to_string();
+1053
+1054        Self {
+1055            ip_v4,
+1056            ip_v6,
+1057            port_v4,
+1058            port_v6,
+1059            connection_id,
+1060            stateless_reset_token,
+1061        }
+1062    }
+1063}
+1064
+1065#[cfg(feature = "qlog")]
+1066impl crate::address_discovery::Role {
+1067    fn to_qlog(self) -> Option<AddressDiscoveryRole> {
+1068        match self {
+1069            Self::SendOnly => Some(AddressDiscoveryRole::SendOnly),
+1070            Self::ReceiveOnly => Some(AddressDiscoveryRole::ReceiveOnly),
+1071            Self::Both => Some(AddressDiscoveryRole::Both),
+1072            Self::Disabled => None,
+1073        }
+1074    }
+1075}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/send_buffer.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/send_buffer.rs.html new file mode 100644 index 000000000..3261a3202 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/send_buffer.rs.html @@ -0,0 +1,752 @@ +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, 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(&mut self, data: Bytes) {
+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);
+78        } else {
+79            // copy
+80            if self.last_segment.len() + data.len() > MAX_COMBINE && !self.last_segment.is_empty() {
+81                self.segments.push_back(self.last_segment.split().freeze());
+82            }
+83            self.last_segment.extend_from_slice(&data);
+84        }
+85    }
+86
+87    /// Discard data from the front of the buffer
+88    ///
+89    /// Calling this with n > len() is allowed and will simply clear the buffer.
+90    fn pop_front(&mut self, n: usize) {
+91        let mut n = n.min(self.len);
+92        self.len -= n;
+93        self.offset += n as u64;
+94        while n > 0 {
+95            // segments is empty, which leaves only last_segment
+96            let Some(front) = self.segments.front_mut() else {
+97                break;
+98            };
+99            if front.len() <= n {
+100                // Remove the whole front segment
+101                n -= front.len();
+102                self.segments.pop_front();
+103            } else {
+104                // Advance within the front segment
+105                front.advance(n);
+106                n = 0;
+107            }
+108        }
+109        // the rest has to be in the last segment
+110        self.last_segment.advance(n);
+111        // shrink segments if we have a lot of unused capacity
+112        if self.segments.len() * 4 < self.segments.capacity() {
+113            self.segments.shrink_to_fit();
+114        }
+115    }
+116
+117    /// Iterator over all segments in order
+118    ///
+119    /// Concatenates `segments` and `last_segment` so they can be handled uniformly
+120    fn segments_iter(&self) -> impl Iterator<Item = &[u8]> {
+121        self.segments
+122            .iter()
+123            .map(|x| x.as_ref())
+124            .chain(std::iter::once(self.last_segment.as_ref()))
+125    }
+126
+127    /// Returns data which is associated with a range
+128    ///
+129    /// Requesting a range outside of the buffered data will panic.
+130    #[cfg(any(test, feature = "bench"))]
+131    fn get(&self, offsets: Range<u64>) -> &[u8] {
+132        assert!(
+133            offsets.start >= self.range().start && offsets.end <= self.range().end,
+134            "Requested range is outside of buffered data"
+135        );
+136        // translate to segment-relative offsets and usize
+137        let offsets = Range {
+138            start: (offsets.start - self.offset) as usize,
+139            end: (offsets.end - self.offset) as usize,
+140        };
+141        let mut segment_offset = 0;
+142        for segment in self.segments_iter() {
+143            if offsets.start >= segment_offset && offsets.start < segment_offset + segment.len() {
+144                let start = offsets.start - segment_offset;
+145                let end = offsets.end - segment_offset;
+146
+147                return &segment[start..end.min(segment.len())];
+148            }
+149            segment_offset += segment.len();
+150        }
+151
+152        unreachable!("impossible if segments and range are consistent");
+153    }
+154
+155    fn get_into(&self, offsets: Range<u64>, buf: &mut impl BufMut) {
+156        assert!(
+157            offsets.start >= self.range().start && offsets.end <= self.range().end,
+158            "Requested range is outside of buffered data"
+159        );
+160        // translate to segment-relative offsets and usize
+161        let offsets = Range {
+162            start: (offsets.start - self.offset) as usize,
+163            end: (offsets.end - self.offset) as usize,
+164        };
+165        let mut segment_offset = 0;
+166        for segment in self.segments_iter() {
+167            // intersect segment range with requested range
+168            let start = segment_offset.max(offsets.start);
+169            let end = (segment_offset + segment.len()).min(offsets.end);
+170            if start < end {
+171                // slice range intersects with requested range
+172                buf.put_slice(&segment[start - segment_offset..end - segment_offset]);
+173            }
+174            segment_offset += segment.len();
+175            if segment_offset >= offsets.end {
+176                // we are beyond the requested range
+177                break;
+178            }
+179        }
+180    }
+181
+182    #[cfg(test)]
+183    fn to_vec(&self) -> Vec<u8> {
+184        let mut result = Vec::with_capacity(self.len);
+185        for segment in self.segments_iter() {
+186            result.extend_from_slice(segment);
+187        }
+188        result
+189    }
+190}
+191
+192impl SendBuffer {
+193    /// Construct an empty buffer at the initial offset
+194    pub(super) fn new() -> Self {
+195        Self::default()
+196    }
+197
+198    /// Append application data to the end of the stream
+199    pub(super) fn write(&mut self, data: Bytes) {
+200        self.data.append(data);
+201    }
+202
+203    /// Discard a range of acknowledged stream data
+204    pub(super) fn ack(&mut self, mut range: Range<u64>) {
+205        // Clamp the range to data which is still tracked
+206        let base_offset = self.fully_acked_offset();
+207        range.start = base_offset.max(range.start);
+208        range.end = base_offset.max(range.end);
+209
+210        self.acks.insert(range);
+211
+212        while self.acks.min() == Some(self.fully_acked_offset()) {
+213            let prefix = self.acks.pop_min().unwrap();
+214            let to_advance = (prefix.end - prefix.start) as usize;
+215            self.data.pop_front(to_advance);
+216        }
+217
+218        // Remove retransmit ranges which have been acknowledged
+219        //
+220        // We have to do this since we have just dropped the data, and asking
+221        // for non-present data would be an error.
+222        self.retransmits.remove(0..self.fully_acked_offset());
+223    }
+224
+225    /// Compute the next range to transmit on this stream and update state to account for that
+226    /// transmission.
+227    ///
+228    /// `max_len` here includes the space which is available to transmit the
+229    /// offset and length of the data to send. The caller has to guarantee that
+230    /// there is at least enough space available to write maximum-sized metadata
+231    /// (8 byte offset + 8 byte length).
+232    ///
+233    /// The method returns a tuple:
+234    /// - The first return value indicates the range of data to send
+235    /// - The second return value indicates whether the length needs to be encoded
+236    ///   in the STREAM frames metadata (`true`), or whether it can be omitted
+237    ///   since the selected range will fill the whole packet.
+238    pub(super) fn poll_transmit(&mut self, mut max_len: usize) -> (Range<u64>, bool) {
+239        debug_assert!(max_len >= 8 + 8);
+240        let mut encode_length = false;
+241
+242        if let Some(range) = self.retransmits.pop_min() {
+243            // Retransmit sent data
+244
+245            // When the offset is known, we know how many bytes are required to encode it.
+246            // Offset 0 requires no space
+247            if range.start != 0 {
+248                max_len -= VarInt::size(unsafe { VarInt::from_u64_unchecked(range.start) });
+249            }
+250            if range.end - range.start < max_len as u64 {
+251                encode_length = true;
+252                max_len -= 8;
+253            }
+254
+255            let end = range.end.min((max_len as u64).saturating_add(range.start));
+256            if end != range.end {
+257                self.retransmits.insert(end..range.end);
+258            }
+259            return (range.start..end, encode_length);
+260        }
+261
+262        // Transmit new data
+263
+264        // When the offset is known, we know how many bytes are required to encode it.
+265        // Offset 0 requires no space
+266        if self.unsent != 0 {
+267            max_len -= VarInt::size(unsafe { VarInt::from_u64_unchecked(self.unsent) });
+268        }
+269        if self.offset() - self.unsent < max_len as u64 {
+270            encode_length = true;
+271            max_len -= 8;
+272        }
+273
+274        let end = self
+275            .offset()
+276            .min((max_len as u64).saturating_add(self.unsent));
+277        let result = self.unsent..end;
+278        self.unsent = end;
+279        (result, encode_length)
+280    }
+281
+282    /// Returns data which is associated with a range
+283    ///
+284    /// This function can return a subset of the range, if the data is stored
+285    /// in noncontiguous fashion in the send buffer. In this case callers
+286    /// should call the function again with an incremented start offset to
+287    /// retrieve more data.
+288    #[cfg(any(test, feature = "bench"))]
+289    pub(super) fn get(&self, offsets: Range<u64>) -> &[u8] {
+290        self.data.get(offsets)
+291    }
+292
+293    pub(super) fn get_into(&self, offsets: Range<u64>, buf: &mut impl BufMut) {
+294        self.data.get_into(offsets, buf)
+295    }
+296
+297    /// Queue a range of sent but unacknowledged data to be retransmitted
+298    pub(super) fn retransmit(&mut self, mut range: Range<u64>) {
+299        debug_assert!(range.end <= self.unsent, "unsent data can't be lost");
+300        // don't allow retransmitting data that has already been fully acknowledged,
+301        // since we don't have it anymore.
+302        //
+303        // Note that we do allow retransmitting data that has been acknowledged
+304        // for simplicity. Not doing so would require clipping the range against
+305        // all acknowledged ranges.
+306        range.start = range.start.max(self.fully_acked_offset());
+307        self.retransmits.insert(range);
+308    }
+309
+310    pub(super) fn retransmit_all_for_0rtt(&mut self) {
+311        // check that we still got all data - we didn't get any acks.
+312        debug_assert_eq!(self.fully_acked_offset(), 0);
+313        self.unsent = 0;
+314    }
+315
+316    /// Offset up to which all data has been acknowledged
+317    fn fully_acked_offset(&self) -> u64 {
+318        self.data.range().start
+319    }
+320
+321    /// First stream offset unwritten by the application, i.e. the offset that the next write will
+322    /// begin at
+323    pub(super) fn offset(&self) -> u64 {
+324        self.data.range().end
+325    }
+326
+327    /// Whether all sent data has been acknowledged
+328    pub(super) fn is_fully_acked(&self) -> bool {
+329        self.data.len() == 0
+330    }
+331
+332    /// Whether there's data to send
+333    ///
+334    /// There may be sent unacknowledged data even when this is false.
+335    pub(super) fn has_unsent_data(&self) -> bool {
+336        self.unsent != self.offset() || !self.retransmits.is_empty()
+337    }
+338
+339    /// Compute the amount of data that hasn't been acknowledged
+340    pub(super) fn unacked(&self) -> u64 {
+341        self.data.len() as u64 - self.acks.iter().map(|x| x.end - x.start).sum::<u64>()
+342    }
+343}
+344
+345#[cfg(test)]
+346mod tests {
+347    use super::*;
+348
+349    #[test]
+350    fn fragment_with_length() {
+351        let mut buf = SendBuffer::new();
+352        const MSG: &[u8] = b"Hello, world!";
+353        buf.write(MSG.into());
+354        // 0 byte offset => 19 bytes left => 13 byte data isn't enough
+355        // with 8 bytes reserved for length 11 payload bytes will fit
+356        assert_eq!(buf.poll_transmit(19), (0..11, true));
+357        assert_eq!(
+358            buf.poll_transmit(MSG.len() + 16 - 11),
+359            (11..MSG.len() as u64, true)
+360        );
+361        assert_eq!(
+362            buf.poll_transmit(58),
+363            (MSG.len() as u64..MSG.len() as u64, true)
+364        );
+365    }
+366
+367    #[test]
+368    fn fragment_without_length() {
+369        let mut buf = SendBuffer::new();
+370        const MSG: &[u8] = b"Hello, world with some extra data!";
+371        buf.write(MSG.into());
+372        // 0 byte offset => 19 bytes left => can be filled by 34 bytes payload
+373        assert_eq!(buf.poll_transmit(19), (0..19, false));
+374        assert_eq!(
+375            buf.poll_transmit(MSG.len() - 19 + 1),
+376            (19..MSG.len() as u64, false)
+377        );
+378        assert_eq!(
+379            buf.poll_transmit(58),
+380            (MSG.len() as u64..MSG.len() as u64, true)
+381        );
+382    }
+383
+384    #[test]
+385    fn reserves_encoded_offset() {
+386        let mut buf = SendBuffer::new();
+387
+388        // Pretend we have more than 1 GB of data in the buffer
+389        let chunk: Bytes = Bytes::from_static(&[0; 1024 * 1024]);
+390        for _ in 0..1025 {
+391            buf.write(chunk.clone());
+392        }
+393
+394        const SIZE1: u64 = 64;
+395        const SIZE2: u64 = 16 * 1024;
+396        const SIZE3: u64 = 1024 * 1024 * 1024;
+397
+398        // Offset 0 requires no space
+399        assert_eq!(buf.poll_transmit(16), (0..16, false));
+400        buf.retransmit(0..16);
+401        assert_eq!(buf.poll_transmit(16), (0..16, false));
+402        let mut transmitted = 16u64;
+403
+404        // Offset 16 requires 1 byte
+405        assert_eq!(
+406            buf.poll_transmit((SIZE1 - transmitted + 1) as usize),
+407            (transmitted..SIZE1, false)
+408        );
+409        buf.retransmit(transmitted..SIZE1);
+410        assert_eq!(
+411            buf.poll_transmit((SIZE1 - transmitted + 1) as usize),
+412            (transmitted..SIZE1, false)
+413        );
+414        transmitted = SIZE1;
+415
+416        // Offset 64 requires 2 bytes
+417        assert_eq!(
+418            buf.poll_transmit((SIZE2 - transmitted + 2) as usize),
+419            (transmitted..SIZE2, false)
+420        );
+421        buf.retransmit(transmitted..SIZE2);
+422        assert_eq!(
+423            buf.poll_transmit((SIZE2 - transmitted + 2) as usize),
+424            (transmitted..SIZE2, false)
+425        );
+426        transmitted = SIZE2;
+427
+428        // Offset 16384 requires requires 4 bytes
+429        assert_eq!(
+430            buf.poll_transmit((SIZE3 - transmitted + 4) as usize),
+431            (transmitted..SIZE3, false)
+432        );
+433        buf.retransmit(transmitted..SIZE3);
+434        assert_eq!(
+435            buf.poll_transmit((SIZE3 - transmitted + 4) as usize),
+436            (transmitted..SIZE3, false)
+437        );
+438        transmitted = SIZE3;
+439
+440        // Offset 1GB requires 8 bytes
+441        assert_eq!(
+442            buf.poll_transmit(chunk.len() + 8),
+443            (transmitted..transmitted + chunk.len() as u64, false)
+444        );
+445        buf.retransmit(transmitted..transmitted + chunk.len() as u64);
+446        assert_eq!(
+447            buf.poll_transmit(chunk.len() + 8),
+448            (transmitted..transmitted + chunk.len() as u64, false)
+449        );
+450    }
+451
+452    /// tests that large segments are copied as-is in the SendBuffer
+453    #[test]
+454    fn multiple_large_segments() {
+455        // this must be bigger than MAX_COMBINE so we don't get writes coalesced.
+456        const N: usize = 2000;
+457        const K: u64 = N as u64;
+458        fn dup(data: &[u8]) -> Bytes {
+459            let mut buf = BytesMut::with_capacity(data.len() * N);
+460            for c in data {
+461                for _ in 0..N {
+462                    buf.put_u8(*c);
+463                }
+464            }
+465            buf.freeze()
+466        }
+467
+468        fn same(a: &[u8], b: &[u8]) -> bool {
+469            // surprisingly, eq also checks the fat pointer metadata aka length
+470            std::ptr::eq(a.as_ptr(), b.as_ptr())
+471        }
+472
+473        let mut buf = SendBuffer::new();
+474        let msg: Bytes = dup(b"Hello, world!");
+475        let msg_len: u64 = msg.len() as u64;
+476
+477        let seg1: Bytes = dup(b"He");
+478        buf.write(seg1.clone());
+479        let seg2: Bytes = dup(b"llo,");
+480        buf.write(seg2.clone());
+481        let seg3: Bytes = dup(b" w");
+482        buf.write(seg3.clone());
+483        let seg4: Bytes = dup(b"o");
+484        buf.write(seg4.clone());
+485        let seg5: Bytes = dup(b"rld!");
+486        buf.write(seg5.clone());
+487        assert_eq!(aggregate_unacked(&buf), msg);
+488        // Check that the segments were stored as-is
+489        assert!(same(buf.get(0..5 * K), &seg1));
+490        assert!(same(buf.get(2 * K..8 * K), &seg2));
+491        assert!(same(buf.get(6 * K..8 * K), &seg3));
+492        assert!(same(buf.get(8 * 2000..msg_len), &seg4));
+493        assert!(same(buf.get(9 * 2000..msg_len), &seg5));
+494        // Now drain the segments
+495        buf.ack(0..K);
+496        assert_eq!(aggregate_unacked(&buf), &msg[N..]);
+497        buf.ack(0..3 * K);
+498        assert_eq!(aggregate_unacked(&buf), &msg[3 * N..]);
+499        buf.ack(3 * K..5 * K);
+500        assert_eq!(aggregate_unacked(&buf), &msg[5 * N..]);
+501        // ack with gap, doesn't free anything
+502        buf.ack(7 * K..9 * K);
+503        assert_eq!(aggregate_unacked(&buf), &msg[5 * N..]);
+504        // fill the gap, free up to 9 K
+505        buf.ack(4 * K..7 * K);
+506        assert_eq!(aggregate_unacked(&buf), &msg[9 * N..]);
+507        // ack all
+508        buf.ack(0..msg_len);
+509        assert_eq!(aggregate_unacked(&buf), &[] as &[u8]);
+510    }
+511
+512    #[test]
+513    fn retransmit() {
+514        let mut buf = SendBuffer::new();
+515        const MSG: &[u8] = b"Hello, world with extra data!";
+516        buf.write(MSG.into());
+517        // Transmit two frames
+518        assert_eq!(buf.poll_transmit(16), (0..16, false));
+519        assert_eq!(buf.poll_transmit(16), (16..23, true));
+520        // Lose the first, but not the second
+521        buf.retransmit(0..16);
+522        // Ensure we only retransmit the lost frame, then continue sending fresh data
+523        assert_eq!(buf.poll_transmit(16), (0..16, false));
+524        assert_eq!(buf.poll_transmit(16), (23..MSG.len() as u64, true));
+525        // Lose the second frame
+526        buf.retransmit(16..23);
+527        assert_eq!(buf.poll_transmit(16), (16..23, true));
+528    }
+529
+530    #[test]
+531    fn ack() {
+532        let mut buf = SendBuffer::new();
+533        const MSG: &[u8] = b"Hello, world!";
+534        buf.write(MSG.into());
+535        assert_eq!(buf.poll_transmit(16), (0..8, true));
+536        buf.ack(0..8);
+537        assert_eq!(aggregate_unacked(&buf), &MSG[8..]);
+538    }
+539
+540    #[test]
+541    fn reordered_ack() {
+542        let mut buf = SendBuffer::new();
+543        const MSG: &[u8] = b"Hello, world with extra data!";
+544        buf.write(MSG.into());
+545        assert_eq!(buf.poll_transmit(16), (0..16, false));
+546        assert_eq!(buf.poll_transmit(16), (16..23, true));
+547        buf.ack(16..23);
+548        assert_eq!(aggregate_unacked(&buf), MSG);
+549        buf.ack(0..16);
+550        assert_eq!(aggregate_unacked(&buf), &MSG[23..]);
+551        assert!(buf.acks.is_empty());
+552    }
+553
+554    fn aggregate_unacked(buf: &SendBuffer) -> Vec<u8> {
+555        buf.data.to_vec()
+556    }
+557
+558    #[test]
+559    #[should_panic(expected = "Requested range is outside of buffered data")]
+560    fn send_buffer_get_out_of_range() {
+561        let data = SendBufferData::default();
+562        data.get(0..1);
+563    }
+564
+565    #[test]
+566    #[should_panic(expected = "Requested range is outside of buffered data")]
+567    fn send_buffer_get_into_out_of_range() {
+568        let data = SendBufferData::default();
+569        let mut buf = Vec::new();
+570        data.get_into(0..1, &mut buf);
+571    }
+572}
+573
+574#[cfg(all(test, not(target_family = "wasm")))]
+575mod proptests {
+576    use super::*;
+577
+578    use proptest::prelude::*;
+579    use test_strategy::{Arbitrary, proptest};
+580    use crate::tests::subscribe;
+581    use tracing::trace;
+582
+583    #[derive(Debug, Clone, Arbitrary)]
+584    enum Op {
+585        // write the given bytes
+586        Write(#[strategy(proptest::collection::vec(any::<u8>(), 0..1024))] Vec<u8>),
+587        // ack a random range
+588        Ack(Range<u64>),
+589        // retransmit a random range
+590        Retransmit(Range<u64>),
+591        // poll_transmit with the given max len
+592        PollTransmit(#[strategy(16usize..1024)] usize),
+593    }
+594
+595    /// Map a range into a target range
+596    ///
+597    /// If the target range is empty, it will be returned as is.
+598    /// For a non-empty target range, 0 in the input range will be mapped to
+599    /// the start of the target range, and the input range will wrap around
+600    /// the target range as needed.
+601    fn map_range(input: Range<u64>, target: Range<u64>) -> Range<u64> {
+602        if target.is_empty() {
+603            return target;
+604        }
+605        let size = target.end - target.start;
+606        let a = target.start + (input.start % size);
+607        let b = target.start + (input.end % size);
+608        a.min(b)..a.max(b)
+609    }
+610
+611    #[proptest]
+612    fn send_buffer_matches_reference(
+613        #[strategy(proptest::collection::vec(any::<Op>(), 1..100))] ops: Vec<Op>,
+614    ) {
+615        let _guard = subscribe();
+616        let mut sb = SendBuffer::new();
+617        // all data written to the send buffer
+618        let mut buf = Vec::new();
+619        // max offset that has been returned by poll_transmit
+620        let mut max_send_offset = 0u64;
+621        // max offset up to which data has been fully acked
+622        let mut max_full_send_offset = 0u64;
+623        trace!("");
+624        for op in ops {
+625            match op {
+626                Op::Write(data) => {
+627                    trace!("Op::Write({})", data.len());
+628                    buf.extend_from_slice(&data);
+629                    sb.write(Bytes::from(data));
+630                }
+631                Op::Ack(range) => {
+632                    // we can only get acks for data that has been sent
+633                    let range = map_range(range, 0..max_send_offset);
+634                    // update fully acked range
+635                    if range.contains(&max_full_send_offset) {
+636                        max_full_send_offset = range.end;
+637                    }
+638                    trace!("Op::Ack({:?})", range);
+639                    sb.ack(range);
+640                }
+641                Op::Retransmit(range) => {
+642                    // we can only get retransmits for data that has been sent
+643                    let range = map_range(range, 0..max_send_offset);
+644                    trace!("Op::Retransmit({:?})", range);
+645                    sb.retransmit(range);
+646                }
+647                Op::PollTransmit(max_len) => {
+648                    trace!("Op::PollTransmit({})", max_len);
+649                    let (range, _partial) = sb.poll_transmit(max_len);
+650                    max_send_offset = max_send_offset.max(range.end);
+651                    assert!(
+652                        range.start >= max_full_send_offset,
+653                        "poll_transmit returned already fully acked data: range={:?}, max_full_send_offset={}",
+654                        range,
+655                        max_full_send_offset
+656                    );
+657
+658                    let mut t1 = Vec::new();
+659                    sb.get_into(range.clone(), &mut t1);
+660
+661                    let mut t2 = Vec::new();
+662                    t2.extend_from_slice(&buf[range.start as usize..range.end as usize]);
+663
+664                    assert_eq!(t1, t2, "Data mismatch for range {:?}", range);
+665                }
+666            }
+667        }
+668        // Drain all remaining data
+669        trace!("Op::Retransmit({:?})", 0..max_send_offset);
+670        sb.retransmit(0..max_send_offset);
+671        loop {
+672            trace!("Op::PollTransmit({})", 1024);
+673            let (range, _partial) = sb.poll_transmit(1024);
+674            if range.is_empty() {
+675                break;
+676            }
+677            trace!("Op::Ack({:?})", range);
+678            sb.ack(range);
+679        }
+680        assert!(
+681            sb.is_fully_acked(),
+682            "SendBuffer not fully acked at end of ops"
+683        );
+684    }
+685}
+686
+687#[cfg(feature = "bench")]
+688pub mod send_buffer_benches {
+689    //! Bench fns for SendBuffer
+690    //!
+691    //! These are defined here and re-exported via `bench_exports` in lib.rs,
+692    //! so we can access the private `SendBuffer` struct.
+693    use bytes::Bytes;
+694    use criterion::Criterion;
+695
+696    use super::SendBuffer;
+697
+698    /// Pathological case: many segments, get from end
+699    pub fn get_into_many_segments(criterion: &mut Criterion) {
+700        let mut group = criterion.benchmark_group("get_into_many_segments");
+701        let mut buf = SendBuffer::new();
+702
+703        const SEGMENTS: u64 = 10000;
+704        const SEGMENT_SIZE: u64 = 10;
+705        const PACKET_SIZE: u64 = 1200;
+706        const BYTES: u64 = SEGMENTS * SEGMENT_SIZE;
+707
+708        // 10000 segments of 10 bytes each = 100KB total (same data size)
+709        for i in 0..SEGMENTS {
+710            buf.write(Bytes::from(vec![i as u8; SEGMENT_SIZE as usize]));
+711        }
+712
+713        let mut tgt = Vec::with_capacity(PACKET_SIZE as usize);
+714        group.bench_function("get_into", |b| {
+715            b.iter(|| {
+716                // Get from end (very slow - scans through all 1000 segments)
+717                tgt.clear();
+718                buf.get_into(BYTES - PACKET_SIZE..BYTES, std::hint::black_box(&mut tgt));
+719            });
+720        });
+721    }
+722
+723    /// Get segments in the old way, using a loop of get calls
+724    pub fn get_loop_many_segments(criterion: &mut Criterion) {
+725        let mut group = criterion.benchmark_group("get_loop_many_segments");
+726        let mut buf = SendBuffer::new();
+727
+728        const SEGMENTS: u64 = 10000;
+729        const SEGMENT_SIZE: u64 = 10;
+730        const PACKET_SIZE: u64 = 1200;
+731        const BYTES: u64 = SEGMENTS * SEGMENT_SIZE;
+732
+733        // 10000 segments of 10 bytes each = 100KB total (same data size)
+734        for i in 0..SEGMENTS {
+735            buf.write(Bytes::from(vec![i as u8; SEGMENT_SIZE as usize]));
+736        }
+737
+738        let mut tgt = Vec::with_capacity(PACKET_SIZE as usize);
+739        group.bench_function("get_loop", |b| {
+740            b.iter(|| {
+741                // Get from end (very slow - scans through all 1000 segments)
+742                tgt.clear();
+743                let mut range = BYTES - PACKET_SIZE..BYTES;
+744                while range.start < range.end {
+745                    let slice = std::hint::black_box(buf.get(range.clone()));
+746                    range.start += slice.len() as u64;
+747                    tgt.extend_from_slice(slice);
+748                }
+749            });
+750        });
+751    }
+752}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/spaces.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/spaces.rs.html new file mode 100644 index 000000000..f8b6bf212 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/spaces.rs.html @@ -0,0 +1,1334 @@ +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            if filter.skip_pn(pn, rng) {
+335                pn = self.next_packet_number;
+336                self.next_packet_number += 1;
+337                self.sent_with_keys += 1;
+338            }
+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            if pn == filter.next_skipped_packet_number {
+347                return pn + 1;
+348            }
+349        }
+350        pn
+351    }
+352
+353    /// Checks whether a skipped packet number was ACKed.
+354    pub(super) fn check_ack(
+355        &self,
+356        range: std::ops::RangeInclusive<u64>,
+357    ) -> Result<(), TransportError> {
+358        if let Some(ref filter) = self.pn_filter {
+359            if filter
+360                .prev_skipped_packet_number
+361                .is_some_and(|pn| range.contains(&pn))
+362            {
+363                return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked"));
+364            }
+365        }
+366        Ok(())
+367    }
+368
+369    /// Verifies sanity of an ECN block and returns whether congestion was encountered.
+370    pub(super) fn detect_ecn(
+371        &mut self,
+372        newly_acked: u64,
+373        ecn: frame::EcnCounts,
+374    ) -> Result<bool, &'static str> {
+375        let ect0_increase = ecn
+376            .ect0
+377            .checked_sub(self.ecn_feedback.ect0)
+378            .ok_or("peer ECT(0) count regression")?;
+379        let ect1_increase = ecn
+380            .ect1
+381            .checked_sub(self.ecn_feedback.ect1)
+382            .ok_or("peer ECT(1) count regression")?;
+383        let ce_increase = ecn
+384            .ce
+385            .checked_sub(self.ecn_feedback.ce)
+386            .ok_or("peer CE count regression")?;
+387        let total_increase = ect0_increase + ect1_increase + ce_increase;
+388        if total_increase < newly_acked {
+389            return Err("ECN bleaching");
+390        }
+391        if (ect0_increase + ce_increase) < newly_acked || ect1_increase != 0 {
+392            return Err("ECN corruption");
+393        }
+394        // If total_increase > newly_acked (which happens when ACKs are lost), this is required by
+395        // the draft so that long-term drift does not occur. If =, then the only question is whether
+396        // to count CE packets as CE or ECT0. Recording them as CE is more consistent and keeps the
+397        // congestion check obvious.
+398        self.ecn_feedback = ecn;
+399        Ok(ce_increase != 0)
+400    }
+401
+402    /// Stop tracking sent packet `number`, and return what we knew about it
+403    pub(super) fn take(&mut self, number: u64) -> Option<SentPacket> {
+404        let packet = self.sent_packets.remove(number)?;
+405        if !packet.ack_eliciting && number > self.largest_ack_eliciting_sent {
+406            self.unacked_non_ack_eliciting_tail =
+407                self.unacked_non_ack_eliciting_tail.checked_sub(1).unwrap();
+408        }
+409        Some(packet)
+410    }
+411
+412    /// May return a packet that should be forgotten
+413    pub(super) fn sent(&mut self, number: u64, packet: SentPacket) -> Option<SentPacket> {
+414        // Retain state for at most this many non-ACK-eliciting packets sent after the most recently
+415        // sent ACK-eliciting packet. We're never guaranteed to receive an ACK for those, and we
+416        // can't judge them as lost without an ACK, so to limit memory in applications which receive
+417        // packets but don't send ACK-eliciting data for long periods use we must eventually start
+418        // forgetting about them, although it might also be reasonable to just kill the connection
+419        // due to weird peer behavior.
+420        const MAX_UNACKED_NON_ACK_ELICTING_TAIL: u64 = 1_000;
+421
+422        let mut forgotten = None;
+423        if packet.ack_eliciting {
+424            self.unacked_non_ack_eliciting_tail = 0;
+425            self.largest_ack_eliciting_sent = number;
+426        } else if self.unacked_non_ack_eliciting_tail > MAX_UNACKED_NON_ACK_ELICTING_TAIL {
+427            let oldest_after_ack_eliciting = self
+428                .sent_packets
+429                .keys_range((
+430                    Bound::Excluded(self.largest_ack_eliciting_sent),
+431                    Bound::Unbounded,
+432                ))
+433                .next()
+434                .unwrap();
+435            // Per https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types,
+436            // non-ACK-eliciting packets must only contain PADDING, ACK, and CONNECTION_CLOSE
+437            // frames, which require no special handling on ACK or loss beyond removal from
+438            // in-flight counters if padded.
+439            let packet = self
+440                .sent_packets
+441                .remove(oldest_after_ack_eliciting)
+442                .unwrap();
+443            debug_assert!(!packet.ack_eliciting);
+444            forgotten = Some(packet);
+445        } else {
+446            self.unacked_non_ack_eliciting_tail += 1;
+447        }
+448
+449        self.sent_packets.insert(number, packet);
+450        forgotten
+451    }
+452
+453    /// Whether any congestion-controlled packets in this space are not yet acknowledged or lost
+454    pub(super) fn has_in_flight(&self) -> bool {
+455        // The number of non-congestion-controlled (i.e. size == 0) packets in flight at a time
+456        // should be small, since otherwise congestion control wouldn't be effective. Therefore,
+457        // this shouldn't need to visit many packets before finishing one way or another.
+458        self.sent_packets.values().any(|x| x.size != 0)
+459    }
+460}
+461
+462/// Represents one or more packets subject to retransmission
+463#[derive(Debug, Clone)]
+464pub(super) struct SentPacket {
+465    /// [`PathData::generation`](super::PathData::generation) of the path on which this packet was sent
+466    pub(super) path_generation: u64,
+467    /// The time the packet was sent.
+468    pub(super) time_sent: Instant,
+469    /// The number of bytes sent in the packet, not including UDP or IP overhead, but including QUIC
+470    /// framing overhead. Zero if this packet is not counted towards congestion control, i.e. not an
+471    /// "in flight" packet.
+472    pub(super) size: u16,
+473    /// Whether an acknowledgement is expected directly in response to this packet.
+474    pub(super) ack_eliciting: bool,
+475    /// The largest packet number acknowledged by this packet
+476    pub(super) largest_acked: FxHashMap<PathId, u64>,
+477    /// Data which needs to be retransmitted in case the packet is lost.
+478    /// The data is boxed to minimize `SentPacket` size for the typical case of
+479    /// packets only containing ACKs and STREAM frames.
+480    pub(super) retransmits: ThinRetransmits,
+481    /// Metadata for stream frames in a packet
+482    ///
+483    /// The actual application data is stored with the stream state.
+484    pub(super) stream_frames: frame::StreamMetaVec,
+485}
+486
+487/// Represents one or more packets that are deemed lost.
+488#[derive(Debug)]
+489pub(super) struct LostPacket {
+490    /// The time the packet was sent.
+491    pub(super) time_sent: Instant,
+492}
+493
+494/// Retransmittable data queue
+495#[allow(unreachable_pub)] // fuzzing only
+496#[derive(Debug, Default, Clone)]
+497pub struct Retransmits {
+498    pub(super) max_data: bool,
+499    pub(super) max_stream_id: [bool; 2],
+500    pub(super) reset_stream: Vec<(StreamId, VarInt)>,
+501    pub(super) stop_sending: Vec<frame::StopSending>,
+502    pub(super) max_stream_data: FxHashSet<StreamId>,
+503    pub(super) crypto: VecDeque<frame::Crypto>,
+504    pub(super) new_cids: Vec<IssuedCid>,
+505    pub(super) retire_cids: Vec<(PathId, u64)>,
+506    pub(super) ack_frequency: bool,
+507    pub(super) handshake_done: bool,
+508    pub(super) observed_addr: bool,
+509    /// Whether we should inform the peer we will allow higher [`PathId`]s.
+510    pub(super) max_path_id: bool,
+511    /// Whether we should inform the peer that their max [`PathId`] is blocking our attempt to open
+512    /// new paths.
+513    // TODO(@divma): we need logic to prevent sending this more than once after being ack-d once
+514    pub(super) paths_blocked: bool,
+515    /// For each enqueued NEW_TOKEN frame, a copy of the path's remote address
+516    ///
+517    /// There are 2 reasons this is unusual:
+518    ///
+519    /// - If the path changes, NEW_TOKEN frames bound for the old path are not retransmitted on the
+520    ///   new path. That is why this field stores the remote address: so that ones for old paths
+521    ///   can be filtered out.
+522    /// - If a token is lost, a new randomly generated token is re-transmitted, rather than the
+523    ///   original. This is so that if both transmissions are received, the client won't risk
+524    ///   sending the same token twice. That is why this field does _not_ store any actual token.
+525    ///
+526    /// It is true that a QUIC endpoint will only want to effectively have NEW_TOKEN frames
+527    /// enqueued for its current path at a given point in time. Based on that, we could conceivably
+528    /// change this from a vector to an `Option<(FourTuple, usize)>` or just a `usize` or
+529    /// something. However, due to the architecture of Quinn, it is considerably simpler to not do
+530    /// that; consider what such a change would mean for implementing `BitOrAssign` on Self.
+531    pub(super) new_tokens: Vec<FourTuple>,
+532    /// Paths which need to be abandoned
+533    pub(super) path_abandon: BTreeMap<PathId, TransportErrorCode>,
+534    /// If a [`frame::PathStatusAvailable`] and [`frame::PathStatusBackup`] need to be sent for a path
+535    pub(super) path_status: BTreeSet<PathId>,
+536    /// If a PATH_CIDS_BLOCKED frame needs to be sent for a path
+537    pub(super) path_cids_blocked: Vec<PathId>,
+538
+539    // Nat traversal data
+540    /// Addresses to report in `ADD_ADDRESS` frames
+541    pub(super) add_address: BTreeSet<AddAddress>,
+542    /// Address IDs to remove in `REMOVE_ADDRESS` frames
+543    pub(super) remove_address: BTreeSet<RemoveAddress>,
+544    /// Round and local addresses to advertise in `REACH_OUT` frames
+545    pub(super) reach_out: Option<(VarInt, Vec<(IpAddr, u16)>)>,
+546}
+547
+548impl Retransmits {
+549    pub(super) fn is_empty(&self, streams: &StreamsState) -> bool {
+550        !self.max_data
+551            && !self.max_stream_id.into_iter().any(|x| x)
+552            && self.reset_stream.is_empty()
+553            && self.stop_sending.is_empty()
+554            && self
+555                .max_stream_data
+556                .iter()
+557                .all(|&id| !streams.can_send_flow_control(id))
+558            && self.crypto.is_empty()
+559            && self.new_cids.is_empty()
+560            && self.retire_cids.is_empty()
+561            && !self.ack_frequency
+562            && !self.handshake_done
+563            && !self.observed_addr
+564            && self.new_tokens.is_empty()
+565            && self.path_abandon.is_empty()
+566            && self.path_status.is_empty()
+567            && !self.max_path_id
+568            && !self.paths_blocked
+569            && self.add_address.is_empty()
+570            && self.remove_address.is_empty()
+571            && self.reach_out.is_none()
+572    }
+573}
+574
+575impl ::std::ops::BitOrAssign for Retransmits {
+576    fn bitor_assign(&mut self, mut rhs: Self) {
+577        // We reduce in-stream head-of-line blocking by queueing retransmits before other data for
+578        // STREAM and CRYPTO frames.
+579        self.max_data |= rhs.max_data;
+580        for dir in Dir::iter() {
+581            self.max_stream_id[dir as usize] |= rhs.max_stream_id[dir as usize];
+582        }
+583        self.reset_stream.extend_from_slice(&rhs.reset_stream);
+584        self.stop_sending.extend_from_slice(&rhs.stop_sending);
+585        self.max_stream_data.extend(&rhs.max_stream_data);
+586        for crypto in rhs.crypto.into_iter().rev() {
+587            self.crypto.push_front(crypto);
+588        }
+589        self.new_cids.extend(&rhs.new_cids);
+590        self.retire_cids.extend(rhs.retire_cids);
+591        self.ack_frequency |= rhs.ack_frequency;
+592        self.handshake_done |= rhs.handshake_done;
+593        self.observed_addr |= rhs.observed_addr;
+594        self.new_tokens.extend_from_slice(&rhs.new_tokens);
+595        self.path_abandon.append(&mut rhs.path_abandon);
+596        self.max_path_id |= rhs.max_path_id;
+597        self.paths_blocked |= rhs.paths_blocked;
+598        self.add_address.extend(rhs.add_address.iter().copied());
+599        self.remove_address
+600            .extend(rhs.remove_address.iter().copied());
+601        // if there are two rounds, prefer the most recent reach out set
+602        let lhs_round = self.reach_out.as_ref().map(|(round, _)| *round);
+603        let rhs_round = rhs.reach_out.as_ref().map(|(round, _)| *round);
+604        match (lhs_round, rhs_round) {
+605            (None, Some(_)) => self.reach_out = rhs.reach_out.clone(),
+606            (Some(lhs_round), Some(rhs_round)) if rhs_round > lhs_round => {
+607                self.reach_out = rhs.reach_out.clone()
+608            }
+609            _ => {}
+610        }
+611    }
+612}
+613
+614impl ::std::ops::BitOrAssign<ThinRetransmits> for Retransmits {
+615    fn bitor_assign(&mut self, rhs: ThinRetransmits) {
+616        if let Some(retransmits) = rhs.retransmits {
+617            self.bitor_assign(*retransmits)
+618        }
+619    }
+620}
+621
+622impl ::std::iter::FromIterator<Self> for Retransmits {
+623    fn from_iter<T>(iter: T) -> Self
+624    where
+625        T: IntoIterator<Item = Self>,
+626    {
+627        let mut result = Self::default();
+628        for packet in iter {
+629            result |= packet;
+630        }
+631        result
+632    }
+633}
+634
+635/// A variant of `Retransmits` which only allocates storage when required
+636#[derive(Debug, Default, Clone)]
+637pub(super) struct ThinRetransmits {
+638    retransmits: Option<Box<Retransmits>>,
+639}
+640
+641impl ThinRetransmits {
+642    /// Returns `true` if no retransmits are necessary
+643    pub(super) fn is_empty(&self, streams: &StreamsState) -> bool {
+644        match &self.retransmits {
+645            Some(retransmits) => retransmits.is_empty(streams),
+646            None => true,
+647        }
+648    }
+649
+650    /// Returns a reference to the retransmits stored in this box
+651    pub(super) fn get(&self) -> Option<&Retransmits> {
+652        self.retransmits.as_deref()
+653    }
+654
+655    /// Returns a mutable reference to the retransmits stored in this box
+656    pub(super) fn get_mut(&mut self) -> Option<&mut Retransmits> {
+657        self.retransmits.as_deref_mut()
+658    }
+659
+660    /// Returns a mutable reference to the stored retransmits
+661    ///
+662    /// This function will allocate a backing storage if required.
+663    pub(super) fn get_or_create(&mut self) -> &mut Retransmits {
+664        if self.retransmits.is_none() {
+665            self.retransmits = Some(Box::default());
+666        }
+667        self.retransmits.as_deref_mut().unwrap()
+668    }
+669}
+670
+671/// RFC4303-style sliding window packet number deduplicator.
+672///
+673/// A contiguous bitfield, where each bit corresponds to a packet number and the rightmost bit is
+674/// always set. A set bit represents a packet that has been successfully authenticated. Bits left of
+675/// the window are assumed to be set.
+676///
+677/// ```text
+678/// ...xxxxxxxxx 1 0
+679///     ^        ^ ^
+680/// window highest next
+681/// ```
+682#[derive(Debug, Default)]
+683pub(super) struct Dedup {
+684    window: Window,
+685    /// Lowest packet number higher than all yet authenticated.
+686    next: u64,
+687}
+688
+689/// Inner bitfield type.
+690///
+691/// Because QUIC never reuses packet numbers, this only needs to be large enough to deal with
+692/// packets that are reordered but still delivered in a timely manner.
+693type Window = u128;
+694
+695/// Number of packets tracked by `Dedup`.
+696const WINDOW_SIZE: u64 = 1 + mem::size_of::<Window>() as u64 * 8;
+697
+698impl Dedup {
+699    /// Construct an empty window positioned at the start.
+700    #[cfg(test)]
+701    pub(super) fn new() -> Self {
+702        Self { window: 0, next: 0 }
+703    }
+704
+705    /// Highest packet number authenticated.
+706    fn highest(&self) -> u64 {
+707        self.next - 1
+708    }
+709
+710    /// Record a newly authenticated packet number.
+711    ///
+712    /// Returns whether the packet might be a duplicate.
+713    pub(super) fn insert(&mut self, packet: u64) -> bool {
+714        if let Some(diff) = packet.checked_sub(self.next) {
+715            // Right of window
+716            self.window = ((self.window << 1) | 1)
+717                .checked_shl(cmp::min(diff, u64::from(u32::MAX)) as u32)
+718                .unwrap_or(0);
+719            self.next = packet + 1;
+720            false
+721        } else if self.highest() - packet < WINDOW_SIZE {
+722            // Within window
+723            if let Some(bit) = (self.highest() - packet).checked_sub(1) {
+724                // < highest
+725                let mask = 1 << bit;
+726                let duplicate = self.window & mask != 0;
+727                self.window |= mask;
+728                duplicate
+729            } else {
+730                // == highest
+731                true
+732            }
+733        } else {
+734            // Left of window
+735            true
+736        }
+737    }
+738
+739    /// Returns the packet number of the smallest packet missing between the provided interval
+740    ///
+741    /// If there are no missing packets, returns `None`
+742    fn smallest_missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> Option<u64> {
+743        debug_assert!(lower_bound <= upper_bound);
+744        debug_assert!(upper_bound <= self.highest());
+745        const BITFIELD_SIZE: u64 = (mem::size_of::<Window>() * 8) as u64;
+746
+747        // Since we already know the packets at the boundaries have been received, we only need to
+748        // check those in between them (this removes the necessity of extra logic to deal with the
+749        // highest packet, which is stored outside the bitfield)
+750        let lower_bound = lower_bound + 1;
+751        let upper_bound = upper_bound.saturating_sub(1);
+752
+753        // Note: the offsets are counted from the right
+754        // The highest packet is not included in the bitfield, so we subtract 1 to account for that
+755        let start_offset = (self.highest() - upper_bound).max(1) - 1;
+756        if start_offset >= BITFIELD_SIZE {
+757            // The start offset is outside of the window. All packets outside of the window are
+758            // considered to be received.
+759            return None;
+760        }
+761
+762        let end_offset_exclusive = self.highest().saturating_sub(lower_bound);
+763
+764        // The range is clamped at the edge of the window, because any earlier packets are
+765        // considered to be received
+766        let range_len = end_offset_exclusive
+767            .saturating_sub(start_offset)
+768            .min(BITFIELD_SIZE);
+769        if range_len == 0 {
+770            return None;
+771        }
+772
+773        // Ensure the shift is within bounds (we already know start_offset < BITFIELD_SIZE,
+774        // because of the early return)
+775        let mask = if range_len == BITFIELD_SIZE {
+776            u128::MAX
+777        } else {
+778            ((1u128 << range_len) - 1) << start_offset
+779        };
+780        let gaps = !self.window & mask;
+781
+782        let smallest_missing_offset = 128 - gaps.leading_zeros() as u64;
+783        let smallest_missing_packet = self.highest() - smallest_missing_offset;
+784
+785        if smallest_missing_packet <= upper_bound {
+786            Some(smallest_missing_packet)
+787        } else {
+788            None
+789        }
+790    }
+791
+792    /// Returns true if there are any missing packets between the provided interval
+793    ///
+794    /// The provided packet numbers must have been received before calling this function
+795    fn missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> bool {
+796        self.smallest_missing_in_interval(lower_bound, upper_bound)
+797            .is_some()
+798    }
+799}
+800
+801/// Indicates which data is available for sending
+802#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+803pub(super) struct SendableFrames {
+804    /// Whether there ACK frames to send, these are not ack-eliciting
+805    pub(super) acks: bool,
+806    /// Whether there are any other frames to send, these are ack-eliciting
+807    pub(super) other: bool,
+808    /// Whether there is a CONNECTION_CLOSE to send, this is not ack-eliciting
+809    pub(super) close: bool,
+810    /// Whether there are frames to send, which can only be sent on the path queried
+811    ///
+812    /// These are ack-eliciting, and a subset of [`SendableFrames::other`].  This is useful
+813    /// for QUIC-MULTIPATH, which may desire not to send any frames on a backup path, which
+814    /// can also be sent on an active path.
+815    pub(super) path_exclusive: bool,
+816}
+817
+818impl SendableFrames {
+819    /// Returns that no data is available for sending
+820    pub(super) fn empty() -> Self {
+821        Self {
+822            acks: false,
+823            other: false,
+824            close: false,
+825            path_exclusive: false,
+826        }
+827    }
+828
+829    /// Whether no data is sendable
+830    pub(super) fn is_empty(&self) -> bool {
+831        !self.acks && !self.other && !self.close && !self.path_exclusive
+832    }
+833}
+834
+835impl ::std::ops::BitOrAssign for SendableFrames {
+836    fn bitor_assign(&mut self, rhs: Self) {
+837        self.acks |= rhs.acks;
+838        self.other |= rhs.other;
+839        self.close |= rhs.close;
+840        self.path_exclusive |= rhs.path_exclusive;
+841    }
+842}
+843
+844#[derive(Debug)]
+845pub(super) struct PendingAcks {
+846    /// Whether we should send an ACK immediately, even if that means sending an ACK-only packet
+847    ///
+848    /// When `immediate_ack_required` is false, the normal behavior is to send ACK frames only when
+849    /// there is other data to send, or when the `MaxAckDelay` timer expires.
+850    immediate_ack_required: bool,
+851    /// The number of ack-eliciting packets received since the last ACK frame was sent
+852    ///
+853    /// Once the count _exceeds_ `ack_eliciting_threshold`, an immediate ACK is required
+854    ack_eliciting_since_last_ack_sent: u64,
+855    non_ack_eliciting_since_last_ack_sent: u64,
+856    ack_eliciting_threshold: u64,
+857    /// The reordering threshold, controlling how we respond to out-of-order ack-eliciting packets
+858    ///
+859    /// Different values enable different behavior:
+860    ///
+861    /// * `0`: no special action is taken
+862    /// * `1`: an ACK is immediately sent if it is out-of-order according to RFC 9000
+863    /// * `>1`: an ACK is immediately sent if it is out-of-order according to the ACK frequency draft
+864    reordering_threshold: u64,
+865    /// The earliest ack-eliciting packet since the last ACK was sent, used to calculate the moment
+866    /// upon which `max_ack_delay` elapses
+867    earliest_ack_eliciting_since_last_ack_sent: Option<Instant>,
+868    /// Packet number ranges for which to still send acknowledgements.
+869    ///
+870    /// These are packet number ranges of ack-eliciting packets the peer has sent and which
+871    /// need to be acknowledged.  Packet numbers are only removed from here once the peer has
+872    /// acknowledged the ACKs for them.
+873    ranges: ArrayRangeSet,
+874    /// The largest packet number received and the time it was received
+875    ///
+876    /// Used to calculate ACK delay in [`PendingAcks::ack_delay`].
+877    largest_packet: Option<(u64, Instant)>,
+878    /// The ack-eliciting packet we have received with the largest packet number
+879    largest_ack_eliciting_packet: Option<u64>,
+880    /// The largest acknowledged packet number sent in an ACK frame
+881    largest_acked: Option<u64>,
+882}
+883
+884impl PendingAcks {
+885    fn new() -> Self {
+886        Self {
+887            immediate_ack_required: false,
+888            ack_eliciting_since_last_ack_sent: 0,
+889            non_ack_eliciting_since_last_ack_sent: 0,
+890            ack_eliciting_threshold: 1,
+891            reordering_threshold: 1,
+892            earliest_ack_eliciting_since_last_ack_sent: None,
+893            ranges: Default::default(),
+894            largest_packet: Default::default(),
+895            largest_ack_eliciting_packet: Default::default(),
+896            largest_acked: Default::default(),
+897        }
+898    }
+899
+900    pub(super) fn set_ack_frequency_params(&mut self, frame: &frame::AckFrequency) {
+901        self.ack_eliciting_threshold = frame.ack_eliciting_threshold.into_inner();
+902        self.reordering_threshold = frame.reordering_threshold.into_inner();
+903    }
+904
+905    pub(super) fn set_immediate_ack_required(&mut self) {
+906        self.immediate_ack_required = true;
+907    }
+908
+909    pub(super) fn on_max_ack_delay_timeout(&mut self) {
+910        self.immediate_ack_required = self.ack_eliciting_since_last_ack_sent > 0;
+911    }
+912
+913    pub(super) fn max_ack_delay_timeout(&self, max_ack_delay: Duration) -> Option<Instant> {
+914        self.earliest_ack_eliciting_since_last_ack_sent
+915            .map(|earliest_unacked| earliest_unacked + max_ack_delay)
+916    }
+917
+918    /// Whether any ACK frames SHOULD be sent
+919    ///
+920    /// This is used in the top-level [`Connection::space_can_send`], so determines if a
+921    /// packet will be built. It is often possible to construct new ACK ranges to send
+922    /// before this returns `true`. This results in more ACK frames being sent, and
+923    /// processing those at the receiver costs CPU for very little improvements.
+924    ///
+925    /// [`Connection::space_can_send`]: super::Connection::space_can_send
+926    pub(super) fn can_send(&self) -> bool {
+927        self.immediate_ack_required && !self.ranges.is_empty()
+928    }
+929
+930    /// Returns the delay since the packet with the largest packet number was received
+931    pub(super) fn ack_delay(&self, now: Instant) -> Duration {
+932        self.largest_packet
+933            .map_or(Duration::default(), |(_, received)| now - received)
+934    }
+935
+936    /// Handle receipt of a new packet
+937    ///
+938    /// Returns true if the max ack delay timer should be armed
+939    pub(super) fn packet_received(
+940        &mut self,
+941        now: Instant,
+942        packet_number: u64,
+943        ack_eliciting: bool,
+944        dedup: &Dedup,
+945    ) -> bool {
+946        if !ack_eliciting {
+947            self.non_ack_eliciting_since_last_ack_sent += 1;
+948            return false;
+949        }
+950
+951        let prev_largest_ack_eliciting = self.largest_ack_eliciting_packet.unwrap_or(0);
+952
+953        // Track largest ack-eliciting packet
+954        self.largest_ack_eliciting_packet = self
+955            .largest_ack_eliciting_packet
+956            .map(|pn| pn.max(packet_number))
+957            .or(Some(packet_number));
+958
+959        // Handle ack_eliciting_threshold
+960        self.ack_eliciting_since_last_ack_sent += 1;
+961        self.immediate_ack_required |=
+962            self.ack_eliciting_since_last_ack_sent > self.ack_eliciting_threshold;
+963
+964        // Handle out-of-order packets
+965        self.immediate_ack_required |=
+966            self.is_out_of_order(packet_number, prev_largest_ack_eliciting, dedup);
+967
+968        // Arm max_ack_delay timer if necessary
+969        if self.earliest_ack_eliciting_since_last_ack_sent.is_none() && !self.can_send() {
+970            self.earliest_ack_eliciting_since_last_ack_sent = Some(now);
+971            return true;
+972        }
+973
+974        false
+975    }
+976
+977    fn is_out_of_order(
+978        &self,
+979        packet_number: u64,
+980        prev_largest_ack_eliciting: u64,
+981        dedup: &Dedup,
+982    ) -> bool {
+983        match self.reordering_threshold {
+984            0 => false,
+985            1 => {
+986                // From https://www.rfc-editor.org/rfc/rfc9000#section-13.2.1-7
+987                packet_number < prev_largest_ack_eliciting
+988                    || dedup.missing_in_interval(prev_largest_ack_eliciting, packet_number)
+989            }
+990            _ => {
+991                // From acknowledgement frequency draft, section 6.1: send an ACK immediately if
+992                // doing so would cause the sender to detect a new packet loss
+993                let Some((largest_acked, largest_unacked)) =
+994                    self.largest_acked.zip(self.largest_ack_eliciting_packet)
+995                else {
+996                    return false;
+997                };
+998                if self.reordering_threshold > largest_acked {
+999                    return false;
+1000                }
+1001                // The largest packet number that could be declared lost without a new ACK being
+1002                // sent
+1003                let largest_reported = largest_acked - self.reordering_threshold + 1;
+1004                let Some(smallest_missing_unreported) =
+1005                    dedup.smallest_missing_in_interval(largest_reported, largest_unacked)
+1006                else {
+1007                    return false;
+1008                };
+1009                largest_unacked - smallest_missing_unreported >= self.reordering_threshold
+1010            }
+1011        }
+1012    }
+1013
+1014    /// Should be called whenever ACKs have been sent
+1015    ///
+1016    /// This will suppress sending further ACKs until additional ACK eliciting frames arrive
+1017    pub(super) fn acks_sent(&mut self) {
+1018        // It is possible (though unlikely) that the ACKs we just sent do not cover all the
+1019        // ACK-eliciting packets we have received (e.g. if there is not enough room in the packet to
+1020        // fit all the ranges). To keep things simple, however, we assume they do. If there are
+1021        // indeed some ACKs that weren't covered, the packets might be ACKed later anyway, because
+1022        // they are still contained in `self.ranges`. If we somehow fail to send the ACKs at a later
+1023        // moment, the peer will assume the packets got lost and will retransmit their frames in a
+1024        // new packet, which is suboptimal, because we already received them. Our assumption here is
+1025        // that simplicity results in code that is more performant, even in the presence of
+1026        // occasional redundant retransmits.
+1027        self.immediate_ack_required = false;
+1028        self.ack_eliciting_since_last_ack_sent = 0;
+1029        self.non_ack_eliciting_since_last_ack_sent = 0;
+1030        self.earliest_ack_eliciting_since_last_ack_sent = None;
+1031        self.largest_acked = self.largest_ack_eliciting_packet;
+1032    }
+1033
+1034    /// Insert one packet that needs to be acknowledged
+1035    pub(super) fn insert_one(&mut self, packet: u64, now: Instant) {
+1036        self.ranges.insert_one(packet);
+1037
+1038        if self.largest_packet.is_none_or(|(pn, _)| packet > pn) {
+1039            self.largest_packet = Some((packet, now));
+1040        }
+1041
+1042        if self.ranges.len() > MAX_ACK_BLOCKS {
+1043            self.ranges.pop_min();
+1044        }
+1045    }
+1046
+1047    /// Remove ACKs of packets numbered at or below `max` from the set of pending ACKs
+1048    pub(super) fn subtract_below(&mut self, max: u64) {
+1049        self.ranges.remove(0..(max + 1));
+1050    }
+1051
+1052    /// Returns the set of currently pending ACK ranges
+1053    pub(super) fn ranges(&self) -> &ArrayRangeSet {
+1054        &self.ranges
+1055    }
+1056
+1057    /// Queue an ACK if a significant number of non-ACK-eliciting packets have not yet been
+1058    /// acknowledged
+1059    ///
+1060    /// Should be called immediately before a non-probing packet is composed, when we've already
+1061    /// committed to sending a packet regardless.
+1062    pub(super) fn maybe_ack_non_eliciting(&mut self) {
+1063        // If we're going to send a packet anyway, and we've received a significant number of
+1064        // non-ACK-eliciting packets, then include an ACK to help the peer perform timely loss
+1065        // detection even if they're not sending any ACK-eliciting packets themselves. Exact
+1066        // threshold chosen somewhat arbitrarily.
+1067        const LAZY_ACK_THRESHOLD: u64 = 10;
+1068        if self.non_ack_eliciting_since_last_ack_sent > LAZY_ACK_THRESHOLD {
+1069            self.immediate_ack_required = true;
+1070        }
+1071    }
+1072}
+1073
+1074/// Helper for mitigating [optimistic ACK attacks]
+1075///
+1076/// A malicious peer could prompt the local application to begin a large data transfer, and then
+1077/// send ACKs without first waiting for data to be received. This could defeat congestion control,
+1078/// allowing the connection to consume disproportionate resources. We therefore occasionally skip
+1079/// packet numbers, and classify any ACK referencing a skipped packet number as a transport error.
+1080///
+1081/// Skipped packet numbers occur only in the application data space (where costly transfers might
+1082/// take place) and are distributed exponentially to reflect the reduced likelihood and impact of
+1083/// bad behavior from a peer that has been well-behaved for an extended period.
+1084///
+1085/// ACKs for packet numbers that have not yet been allocated are also a transport error, but an
+1086/// attacker with knowledge of the congestion control algorithm in use could time falsified ACKs to
+1087/// arrive after the packets they reference are sent.
+1088///
+1089/// [optimistic ACK attacks]: https://www.rfc-editor.org/rfc/rfc9000.html#name-optimistic-ack-attack
+1090pub(super) struct PacketNumberFilter {
+1091    /// Next outgoing packet number to skip
+1092    next_skipped_packet_number: u64,
+1093    /// Most recently skipped packet number
+1094    prev_skipped_packet_number: Option<u64>,
+1095    /// Next packet number to skip is randomly selected from 2^n..2^n+1
+1096    exponent: u32,
+1097}
+1098
+1099impl PacketNumberFilter {
+1100    pub(super) fn new(rng: &mut (impl Rng + ?Sized)) -> Self {
+1101        // First skipped PN is in 0..64
+1102        let exponent = 6;
+1103        Self {
+1104            next_skipped_packet_number: rng.random_range(0..2u64.saturating_pow(exponent)),
+1105            prev_skipped_packet_number: None,
+1106            exponent,
+1107        }
+1108    }
+1109
+1110    #[cfg(test)]
+1111    pub(super) fn disabled() -> Self {
+1112        Self {
+1113            next_skipped_packet_number: u64::MAX,
+1114            prev_skipped_packet_number: None,
+1115            exponent: u32::MAX,
+1116        }
+1117    }
+1118
+1119    /// Whether to use the provided packet number (false) or to skip it (true)
+1120    pub(super) fn skip_pn(&mut self, n: u64, rng: &mut (impl Rng + ?Sized)) -> bool {
+1121        if n != self.next_skipped_packet_number {
+1122            return false;
+1123        }
+1124
+1125        trace!("skipping pn {n}");
+1126        // Skip this packet number, and choose the next one to skip
+1127        self.prev_skipped_packet_number = Some(self.next_skipped_packet_number);
+1128        let next_exponent = self.exponent.saturating_add(1);
+1129        self.next_skipped_packet_number = rng
+1130            .random_range(2u64.saturating_pow(self.exponent)..2u64.saturating_pow(next_exponent));
+1131        self.exponent = next_exponent;
+1132        true
+1133    }
+1134}
+1135
+1136/// Ensures we can always fit all our ACKs in a single minimum-MTU packet with room to spare
+1137const MAX_ACK_BLOCKS: usize = 64;
+1138
+1139#[cfg(test)]
+1140mod test {
+1141    use super::*;
+1142
+1143    #[test]
+1144    fn sanity() {
+1145        let mut dedup = Dedup::new();
+1146        assert!(!dedup.insert(0));
+1147        assert_eq!(dedup.next, 1);
+1148        assert_eq!(dedup.window, 0b1);
+1149        assert!(dedup.insert(0));
+1150        assert_eq!(dedup.next, 1);
+1151        assert_eq!(dedup.window, 0b1);
+1152        assert!(!dedup.insert(1));
+1153        assert_eq!(dedup.next, 2);
+1154        assert_eq!(dedup.window, 0b11);
+1155        assert!(!dedup.insert(2));
+1156        assert_eq!(dedup.next, 3);
+1157        assert_eq!(dedup.window, 0b111);
+1158        assert!(!dedup.insert(4));
+1159        assert_eq!(dedup.next, 5);
+1160        assert_eq!(dedup.window, 0b11110);
+1161        assert!(!dedup.insert(7));
+1162        assert_eq!(dedup.next, 8);
+1163        assert_eq!(dedup.window, 0b1111_0100);
+1164        assert!(dedup.insert(4));
+1165        assert!(!dedup.insert(3));
+1166        assert_eq!(dedup.next, 8);
+1167        assert_eq!(dedup.window, 0b1111_1100);
+1168        assert!(!dedup.insert(6));
+1169        assert_eq!(dedup.next, 8);
+1170        assert_eq!(dedup.window, 0b1111_1101);
+1171        assert!(!dedup.insert(5));
+1172        assert_eq!(dedup.next, 8);
+1173        assert_eq!(dedup.window, 0b1111_1111);
+1174    }
+1175
+1176    #[test]
+1177    fn happypath() {
+1178        let mut dedup = Dedup::new();
+1179        for i in 0..(2 * WINDOW_SIZE) {
+1180            assert!(!dedup.insert(i));
+1181            for j in 0..=i {
+1182                assert!(dedup.insert(j));
+1183            }
+1184        }
+1185    }
+1186
+1187    #[test]
+1188    fn jump() {
+1189        let mut dedup = Dedup::new();
+1190        dedup.insert(2 * WINDOW_SIZE);
+1191        assert!(dedup.insert(WINDOW_SIZE));
+1192        assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1);
+1193        assert_eq!(dedup.window, 0);
+1194        assert!(!dedup.insert(WINDOW_SIZE + 1));
+1195        assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1);
+1196        assert_eq!(dedup.window, 1 << (WINDOW_SIZE - 2));
+1197    }
+1198
+1199    #[test]
+1200    fn dedup_has_missing() {
+1201        let mut dedup = Dedup::new();
+1202
+1203        dedup.insert(0);
+1204        assert!(!dedup.missing_in_interval(0, 0));
+1205
+1206        dedup.insert(1);
+1207        assert!(!dedup.missing_in_interval(0, 1));
+1208
+1209        dedup.insert(3);
+1210        assert!(dedup.missing_in_interval(1, 3));
+1211
+1212        dedup.insert(4);
+1213        assert!(!dedup.missing_in_interval(3, 4));
+1214        assert!(dedup.missing_in_interval(0, 4));
+1215
+1216        dedup.insert(2);
+1217        assert!(!dedup.missing_in_interval(0, 4));
+1218    }
+1219
+1220    #[test]
+1221    fn dedup_outside_of_window_has_missing() {
+1222        let mut dedup = Dedup::new();
+1223
+1224        for i in 0..140 {
+1225            dedup.insert(i);
+1226        }
+1227
+1228        // 0 and 4 are outside of the window
+1229        assert!(!dedup.missing_in_interval(0, 4));
+1230        dedup.insert(160);
+1231        assert!(!dedup.missing_in_interval(0, 4));
+1232        assert!(!dedup.missing_in_interval(0, 140));
+1233        assert!(dedup.missing_in_interval(0, 160));
+1234    }
+1235
+1236    #[test]
+1237    fn dedup_smallest_missing() {
+1238        let mut dedup = Dedup::new();
+1239
+1240        dedup.insert(0);
+1241        assert_eq!(dedup.smallest_missing_in_interval(0, 0), None);
+1242
+1243        dedup.insert(1);
+1244        assert_eq!(dedup.smallest_missing_in_interval(0, 1), None);
+1245
+1246        dedup.insert(5);
+1247        dedup.insert(7);
+1248        assert_eq!(dedup.smallest_missing_in_interval(0, 7), Some(2));
+1249        assert_eq!(dedup.smallest_missing_in_interval(5, 7), Some(6));
+1250
+1251        dedup.insert(2);
+1252        assert_eq!(dedup.smallest_missing_in_interval(1, 7), Some(3));
+1253
+1254        dedup.insert(170);
+1255        dedup.insert(172);
+1256        dedup.insert(300);
+1257        assert_eq!(dedup.smallest_missing_in_interval(170, 172), None);
+1258
+1259        dedup.insert(500);
+1260        assert_eq!(dedup.smallest_missing_in_interval(0, 500), Some(372));
+1261        assert_eq!(dedup.smallest_missing_in_interval(0, 373), Some(372));
+1262        assert_eq!(dedup.smallest_missing_in_interval(0, 372), None);
+1263    }
+1264
+1265    #[test]
+1266    fn pending_acks_first_packet_is_not_considered_reordered() {
+1267        let mut acks = PendingAcks::new();
+1268        let mut dedup = Dedup::new();
+1269        dedup.insert(0);
+1270        acks.packet_received(Instant::now(), 0, true, &dedup);
+1271        assert!(!acks.immediate_ack_required);
+1272    }
+1273
+1274    #[test]
+1275    fn pending_acks_after_immediate_ack_set() {
+1276        let mut acks = PendingAcks::new();
+1277        let mut dedup = Dedup::new();
+1278
+1279        // Receive ack-eliciting packet
+1280        dedup.insert(0);
+1281        let now = Instant::now();
+1282        acks.insert_one(0, now);
+1283        acks.packet_received(now, 0, true, &dedup);
+1284
+1285        // Sanity check
+1286        assert!(!acks.ranges.is_empty());
+1287        assert!(!acks.can_send());
+1288
+1289        // Can send ACK after max_ack_delay exceeded
+1290        acks.set_immediate_ack_required();
+1291        assert!(acks.can_send());
+1292    }
+1293
+1294    #[test]
+1295    fn pending_acks_ack_delay() {
+1296        let mut acks = PendingAcks::new();
+1297        let mut dedup = Dedup::new();
+1298
+1299        let t1 = Instant::now();
+1300        let t2 = t1 + Duration::from_millis(2);
+1301        let t3 = t2 + Duration::from_millis(5);
+1302        assert_eq!(acks.ack_delay(t1), Duration::from_millis(0));
+1303        assert_eq!(acks.ack_delay(t2), Duration::from_millis(0));
+1304        assert_eq!(acks.ack_delay(t3), Duration::from_millis(0));
+1305
+1306        // In-order packet
+1307        dedup.insert(0);
+1308        acks.insert_one(0, t1);
+1309        acks.packet_received(t1, 0, true, &dedup);
+1310        assert_eq!(acks.ack_delay(t1), Duration::from_millis(0));
+1311        assert_eq!(acks.ack_delay(t2), Duration::from_millis(2));
+1312        assert_eq!(acks.ack_delay(t3), Duration::from_millis(7));
+1313
+1314        // Out of order (higher than expected)
+1315        dedup.insert(3);
+1316        acks.insert_one(3, t2);
+1317        acks.packet_received(t2, 3, true, &dedup);
+1318        assert_eq!(acks.ack_delay(t2), Duration::from_millis(0));
+1319        assert_eq!(acks.ack_delay(t3), Duration::from_millis(5));
+1320
+1321        // Out of order (lower than expected, so previous instant is kept)
+1322        dedup.insert(2);
+1323        acks.insert_one(2, t3);
+1324        acks.packet_received(t3, 2, true, &dedup);
+1325        assert_eq!(acks.ack_delay(t3), Duration::from_millis(5));
+1326    }
+1327
+1328    #[test]
+1329    fn sent_packet_size() {
+1330        // The tracking state of sent packets should be minimal, and not grow
+1331        // over time.
+1332        assert!(std::mem::size_of::<SentPacket>() <= 128);
+1333    }
+1334}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/state.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/state.rs.html new file mode 100644 index 000000000..b180b66df --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/state.rs.html @@ -0,0 +1,357 @@ +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    /// Moves to a closed state after a remote error is received.
+130    ///
+131    /// Panics if the state is later than established.
+132    pub(super) fn move_to_closed<R: Into<CloseReason>>(&mut self, reason: R) {
+133        assert!(
+134            matches!(
+135                self.inner,
+136                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+137            ),
+138            "invalid state transition {:?} -> closed",
+139            self.as_type()
+140        );
+141        let remote_reason = reason.into();
+142        let is_local = false;
+143        trace!(?remote_reason, ?is_local, "connection state: closed");
+144        self.inner = InnerState::Closed {
+145            error_read: false,
+146            remote_reason,
+147            is_local,
+148        };
+149    }
+150
+151    /// Moves to a closed state after a local error.
+152    ///
+153    /// Panics if the state is later than established.
+154    pub(super) fn move_to_closed_local<R: Into<CloseReason>>(&mut self, reason: R) {
+155        assert!(
+156            matches!(
+157                self.inner,
+158                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+159            ),
+160            "invalid state transition {:?} -> closed (local)",
+161            self.as_type()
+162        );
+163        let remote_reason = reason.into();
+164        let is_local = true;
+165        trace!(?remote_reason, ?is_local, "connection state: closed");
+166        self.inner = InnerState::Closed {
+167            error_read: false,
+168            remote_reason,
+169            is_local,
+170        };
+171    }
+172
+173    pub(super) fn is_handshake(&self) -> bool {
+174        matches!(self.inner, InnerState::Handshake(_))
+175    }
+176
+177    pub(super) fn is_established(&self) -> bool {
+178        matches!(self.inner, InnerState::Established)
+179    }
+180
+181    pub(super) fn is_closed(&self) -> bool {
+182        matches!(
+183            self.inner,
+184            InnerState::Closed { .. } | InnerState::Draining { .. } | InnerState::Drained { .. }
+185        )
+186    }
+187
+188    pub(super) fn is_drained(&self) -> bool {
+189        matches!(self.inner, InnerState::Drained { .. })
+190    }
+191
+192    pub(super) fn take_error(&mut self) -> Option<ConnectionError> {
+193        match &mut self.inner {
+194            InnerState::Draining { error, is_local } => {
+195                if !*is_local {
+196                    error.take()
+197                } else {
+198                    None
+199                }
+200            }
+201            InnerState::Drained { error, is_local } => {
+202                if !*is_local {
+203                    error.take()
+204                } else {
+205                    None
+206                }
+207            }
+208            InnerState::Closed {
+209                remote_reason,
+210                is_local: local_reason,
+211                error_read,
+212            } => {
+213                if *error_read {
+214                    None
+215                } else {
+216                    *error_read = true;
+217                    if *local_reason {
+218                        None
+219                    } else {
+220                        Some(remote_reason.clone().into())
+221                    }
+222                }
+223            }
+224            InnerState::Handshake(_) | InnerState::Established => None,
+225        }
+226    }
+227
+228    pub(super) fn as_type(&self) -> StateType {
+229        match self.inner {
+230            InnerState::Handshake(_) => StateType::Handshake,
+231            InnerState::Established => StateType::Established,
+232            InnerState::Closed { .. } => StateType::Closed,
+233            InnerState::Draining { .. } => StateType::Draining,
+234            InnerState::Drained { .. } => StateType::Drained,
+235        }
+236    }
+237}
+238
+239#[derive(Debug, Clone)]
+240pub(super) enum StateType {
+241    Handshake,
+242    Established,
+243    Closed,
+244    Draining,
+245    Drained,
+246}
+247
+248#[derive(Debug, Clone)]
+249pub(super) enum CloseReason {
+250    TransportError(TransportError),
+251    Connection(ConnectionClose),
+252    Application(ApplicationClose),
+253}
+254
+255impl From<TransportError> for CloseReason {
+256    fn from(x: TransportError) -> Self {
+257        Self::TransportError(x)
+258    }
+259}
+260impl From<ConnectionClose> for CloseReason {
+261    fn from(x: ConnectionClose) -> Self {
+262        Self::Connection(x)
+263    }
+264}
+265impl From<ApplicationClose> for CloseReason {
+266    fn from(x: ApplicationClose) -> Self {
+267        Self::Application(x)
+268    }
+269}
+270
+271impl From<Close> for CloseReason {
+272    fn from(value: Close) -> Self {
+273        match value {
+274            Close::Application(reason) => Self::Application(reason),
+275            Close::Connection(reason) => Self::Connection(reason),
+276        }
+277    }
+278}
+279
+280impl From<CloseReason> for ConnectionError {
+281    fn from(value: CloseReason) -> Self {
+282        match value {
+283            CloseReason::TransportError(err) => Self::TransportError(err),
+284            CloseReason::Connection(reason) => Self::ConnectionClosed(reason),
+285            CloseReason::Application(reason) => Self::ApplicationClosed(reason),
+286        }
+287    }
+288}
+289
+290impl From<CloseReason> for Close {
+291    fn from(value: CloseReason) -> Self {
+292        match value {
+293            CloseReason::TransportError(err) => Self::Connection(err.into()),
+294            CloseReason::Connection(reason) => Self::Connection(reason),
+295            CloseReason::Application(reason) => Self::Application(reason),
+296        }
+297    }
+298}
+299
+300#[derive(Debug, Clone)]
+301enum InnerState {
+302    Handshake(Handshake),
+303    Established,
+304    Closed {
+305        /// The reason the remote closed the connection, or the reason we are sending to the remote.
+306        remote_reason: CloseReason,
+307        /// Set to true if we closed the connection locally.
+308        is_local: bool,
+309        /// Did we read this as error already?
+310        error_read: bool,
+311    },
+312    Draining {
+313        /// Why the connection was lost, if it has been.
+314        error: Option<ConnectionError>,
+315        /// Set to true if we closed the connection locally.
+316        is_local: bool,
+317    },
+318    /// Waiting for application to call close so we can dispose of the resources.
+319    Drained {
+320        /// Why the connection was lost, if it has been.
+321        error: Option<ConnectionError>,
+322        /// Set to true if we closed the connection locally.
+323        is_local: bool,
+324    },
+325}
+326
+327#[allow(unreachable_pub)] // fuzzing only
+328#[derive(Debug, Clone)]
+329pub struct Handshake {
+330    /// Whether the remote CID has been set by the peer yet.
+331    ///
+332    /// Always set for servers.
+333    pub(super) rem_cid_set: bool,
+334    /// Stateless retry token received in the first Initial by a server.
+335    ///
+336    /// Must be present in every Initial. Always empty for clients.
+337    pub(super) expected_token: Bytes,
+338    /// First cryptographic message.
+339    ///
+340    /// Only set for clients.
+341    pub(super) client_hello: Option<Bytes>,
+342    /// Whether the server address is allowed to migrate.
+343    ///
+344    /// We allow the server to migrate during the handshake as long as we have not
+345    /// received an authenticated handshake packet: it can send a response from a
+346    /// different address than we sent the initial to.  This allows us to send the
+347    /// initial packet over multiple paths - by means of an IPv6 ULA address that copies
+348    /// the packets sent to it to multiple destinations - and accept one response.
+349    ///
+350    /// This is only ever set to true if for a client which hasn't yet received an
+351    /// authenticated handshake packet.  It is set back to false in
+352    /// [`super::Connection::on_packet_authenticated`].
+353    ///
+354    /// THIS IS NOT RFC 9000 COMPLIANT!  A server is not allowed to migrate addresses,
+355    /// other than using the preferred-address transport parameter.
+356    pub(super) allow_server_migration: bool,
+357}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/stats.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/stats.rs.html new file mode 100644 index 000000000..1cf7bb5d8 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/stats.rs.html @@ -0,0 +1,219 @@ +stats.rs - source

iroh_quinn_proto/connection/
stats.rs

1//! Connection statistics
+2
+3use crate::FrameType;
+4use crate::{Dir, Duration, frame::Frame};
+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)]
+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: &Frame) {
+76        match frame {
+77            Frame::Padding => {}
+78            Frame::Ping => self.ping += 1,
+79            Frame::Ack(_) => self.acks += 1,
+80            Frame::PathAck(_) => self.path_acks += 1,
+81            Frame::ResetStream(_) => self.reset_stream += 1,
+82            Frame::StopSending(_) => self.stop_sending += 1,
+83            Frame::Crypto(_) => self.crypto += 1,
+84            Frame::Datagram(_) => self.datagram += 1,
+85            Frame::NewToken(_) => self.new_token += 1,
+86            Frame::MaxData(_) => self.max_data += 1,
+87            Frame::MaxStreamData { .. } => self.max_stream_data += 1,
+88            Frame::MaxStreams { dir, .. } => {
+89                if *dir == Dir::Bi {
+90                    self.max_streams_bidi += 1;
+91                } else {
+92                    self.max_streams_uni += 1;
+93                }
+94            }
+95            Frame::DataBlocked { .. } => self.data_blocked += 1,
+96            Frame::Stream(_) => self.stream += 1,
+97            Frame::StreamDataBlocked { .. } => self.stream_data_blocked += 1,
+98            Frame::StreamsBlocked { dir, .. } => {
+99                if *dir == Dir::Bi {
+100                    self.streams_blocked_bidi += 1;
+101                } else {
+102                    self.streams_blocked_uni += 1;
+103                }
+104            }
+105            Frame::NewConnectionId(frame) => match frame.path_id {
+106                Some(_) => self.path_new_connection_id += 1,
+107                None => self.new_connection_id += 1,
+108            },
+109            Frame::RetireConnectionId(frame) => match frame.get_type() {
+110                FrameType::RetireConnectionId => self.retire_connection_id += 1,
+111                FrameType::PathRetireConnectionId => self.path_retire_connection_id += 1,
+112                _ => unreachable!(),
+113            },
+114            Frame::PathChallenge(_) => self.path_challenge += 1,
+115            Frame::PathResponse(_) => self.path_response += 1,
+116            Frame::Close(_) => self.connection_close += 1,
+117            Frame::AckFrequency(_) => self.ack_frequency += 1,
+118            Frame::ImmediateAck => self.immediate_ack += 1,
+119            Frame::HandshakeDone => self.handshake_done = self.handshake_done.saturating_add(1),
+120            Frame::ObservedAddr(_) => self.observed_addr += 1,
+121            Frame::PathAbandon(_) => self.path_abandon = self.path_abandon.saturating_add(1),
+122            Frame::PathStatusAvailable(_) => {
+123                self.path_status_available = self.path_status_available.saturating_add(1)
+124            }
+125            Frame::PathStatusBackup(_) => {
+126                self.path_status_backup = self.path_status_backup.saturating_add(1)
+127            }
+128            Frame::MaxPathId(_) => self.max_path_id = self.max_path_id.saturating_add(1),
+129            Frame::PathsBlocked(_) => self.paths_blocked = self.paths_blocked.saturating_add(1),
+130            Frame::PathCidsBlocked(_) => {
+131                self.path_cids_blocked = self.path_cids_blocked.saturating_add(1)
+132            }
+133            Frame::AddAddress(_) => self.add_address = self.add_address.saturating_add(1),
+134            Frame::ReachOut(_) => self.reach_out = self.reach_out.saturating_add(1),
+135            Frame::RemoveAddress(_) => self.remove_address = self.remove_address.saturating_add(1),
+136        }
+137    }
+138}
+139
+140impl std::fmt::Debug for FrameStats {
+141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+142        f.debug_struct("FrameStats")
+143            .field("ACK", &self.acks)
+144            .field("ACK_FREQUENCY", &self.ack_frequency)
+145            .field("CONNECTION_CLOSE", &self.connection_close)
+146            .field("CRYPTO", &self.crypto)
+147            .field("DATA_BLOCKED", &self.data_blocked)
+148            .field("DATAGRAM", &self.datagram)
+149            .field("HANDSHAKE_DONE", &self.handshake_done)
+150            .field("IMMEDIATE_ACK", &self.immediate_ack)
+151            .field("MAX_DATA", &self.max_data)
+152            .field("MAX_PATH_ID", &self.max_path_id)
+153            .field("MAX_STREAM_DATA", &self.max_stream_data)
+154            .field("MAX_STREAMS_BIDI", &self.max_streams_bidi)
+155            .field("MAX_STREAMS_UNI", &self.max_streams_uni)
+156            .field("NEW_CONNECTION_ID", &self.new_connection_id)
+157            .field("NEW_TOKEN", &self.new_token)
+158            .field("PATHS_BLOCKED", &self.paths_blocked)
+159            .field("PATH_ABANDON", &self.path_abandon)
+160            .field("PATH_ACK", &self.path_acks)
+161            .field("PATH_STATUS_AVAILABLE", &self.path_status_available)
+162            .field("PATH_STATUS_BACKUP", &self.path_status_backup)
+163            .field("PATH_CHALLENGE", &self.path_challenge)
+164            .field("PATH_CIDS_BLOCKED", &self.path_cids_blocked)
+165            .field("PATH_NEW_CONNECTION_ID", &self.path_new_connection_id)
+166            .field("PATH_RESPONSE", &self.path_response)
+167            .field("PATH_RETIRE_CONNECTION_ID", &self.path_retire_connection_id)
+168            .field("PING", &self.ping)
+169            .field("RESET_STREAM", &self.reset_stream)
+170            .field("RETIRE_CONNECTION_ID", &self.retire_connection_id)
+171            .field("STREAM_DATA_BLOCKED", &self.stream_data_blocked)
+172            .field("STREAMS_BLOCKED_BIDI", &self.streams_blocked_bidi)
+173            .field("STREAMS_BLOCKED_UNI", &self.streams_blocked_uni)
+174            .field("STOP_SENDING", &self.stop_sending)
+175            .field("STREAM", &self.stream)
+176            .finish()
+177    }
+178}
+179
+180/// Statistics related to a transmission path
+181#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
+182#[non_exhaustive]
+183pub struct PathStats {
+184    /// Current best estimate of this connection's latency (round-trip-time)
+185    pub rtt: Duration,
+186    /// Current congestion window of the connection
+187    pub cwnd: u64,
+188    /// Congestion events on the connection
+189    pub congestion_events: u64,
+190    /// The amount of packets lost on this path
+191    pub lost_packets: u64,
+192    /// The amount of bytes lost on this path
+193    pub lost_bytes: u64,
+194    /// The amount of packets sent on this path
+195    pub sent_packets: u64,
+196    /// The amount of PLPMTUD probe packets sent on this path (also counted by `sent_packets`)
+197    pub sent_plpmtud_probes: u64,
+198    /// The amount of PLPMTUD probe packets lost on this path (ignored by `lost_packets` and
+199    /// `lost_bytes`)
+200    pub lost_plpmtud_probes: u64,
+201    /// The number of times a black hole was detected in the path
+202    pub black_holes_detected: u64,
+203    /// Largest UDP payload size the path currently supports
+204    pub current_mtu: u16,
+205}
+206
+207/// Connection statistics
+208#[derive(Debug, Default, Clone)]
+209#[non_exhaustive]
+210pub struct ConnectionStats {
+211    /// Statistics about UDP datagrams transmitted on a connection
+212    pub udp_tx: UdpStats,
+213    /// Statistics about UDP datagrams received on a connection
+214    pub udp_rx: UdpStats,
+215    /// Statistics about frames transmitted on a connection
+216    pub frame_tx: FrameStats,
+217    /// Statistics about frames received on a connection
+218    pub frame_rx: FrameStats,
+219}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html new file mode 100644 index 000000000..cd8a6a6fe --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html @@ -0,0 +1,528 @@ +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, ThinRetransmits};
+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: BytesSource>(&mut self, source: &mut B) -> Result<Written, WriteError> {
+238        if self.conn_state.is_closed() {
+239            trace!(%self.id, "write blocked; connection draining");
+240            return Err(WriteError::Blocked);
+241        }
+242
+243        let limit = self.state.write_limit();
+244
+245        let max_send_data = self.state.max_send_data(self.id);
+246
+247        let stream = self
+248            .state
+249            .send
+250            .get_mut(&self.id)
+251            .map(get_or_insert_send(max_send_data))
+252            .ok_or(WriteError::ClosedStream)?;
+253
+254        if limit == 0 {
+255            trace!(
+256                stream = %self.id, max_data = self.state.max_data, data_sent = self.state.data_sent,
+257                "write blocked by connection-level flow control or send window"
+258            );
+259            if !stream.connection_blocked {
+260                stream.connection_blocked = true;
+261                self.state.connection_blocked.push(self.id);
+262            }
+263            return Err(WriteError::Blocked);
+264        }
+265
+266        let was_pending = stream.is_pending();
+267        let written = stream.write(source, limit)?;
+268        self.state.data_sent += written.bytes as u64;
+269        self.state.unacked_data += written.bytes as u64;
+270        trace!(stream = %self.id, "wrote {} bytes", written.bytes);
+271        if !was_pending {
+272            self.state.pending.push_pending(self.id, stream.priority);
+273        }
+274        Ok(written)
+275    }
+276
+277    /// Check if this stream was stopped, get the reason if it was
+278    pub fn stopped(&self) -> Result<Option<VarInt>, ClosedStream> {
+279        match self.state.send.get(&self.id).as_ref() {
+280            Some(Some(s)) => Ok(s.stop_reason),
+281            Some(None) => Ok(None),
+282            None => Err(ClosedStream { _private: () }),
+283        }
+284    }
+285
+286    /// Finish a send stream, signalling that no more data will be sent.
+287    ///
+288    /// If this fails, no [`StreamEvent::Finished`] will be generated.
+289    ///
+290    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+291    pub fn finish(&mut self) -> Result<(), FinishError> {
+292        let max_send_data = self.state.max_send_data(self.id);
+293        let stream = self
+294            .state
+295            .send
+296            .get_mut(&self.id)
+297            .map(get_or_insert_send(max_send_data))
+298            .ok_or(FinishError::ClosedStream)?;
+299
+300        let was_pending = stream.is_pending();
+301        stream.finish()?;
+302        if !was_pending {
+303            self.state.pending.push_pending(self.id, stream.priority);
+304        }
+305
+306        Ok(())
+307    }
+308
+309    /// Abandon transmitting data on a stream
+310    ///
+311    /// # Panics
+312    /// - when applied to a receive stream
+313    pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+314        let max_send_data = self.state.max_send_data(self.id);
+315        let stream = self
+316            .state
+317            .send
+318            .get_mut(&self.id)
+319            .map(get_or_insert_send(max_send_data))
+320            .ok_or(ClosedStream { _private: () })?;
+321
+322        if matches!(stream.state, SendState::ResetSent) {
+323            // Redundant reset call
+324            return Err(ClosedStream { _private: () });
+325        }
+326
+327        // Restore the portion of the send window consumed by the data that we aren't about to
+328        // send. We leave flow control alone because the peer's responsible for issuing additional
+329        // credit based on the final offset communicated in the RESET_STREAM frame we send.
+330        self.state.unacked_data -= stream.pending.unacked();
+331        stream.reset();
+332        self.pending.reset_stream.push((self.id, error_code));
+333
+334        // Don't reopen an already-closed stream we haven't forgotten yet
+335        Ok(())
+336    }
+337
+338    /// Set the priority of a stream
+339    ///
+340    /// # Panics
+341    /// - when applied to a receive stream
+342    pub fn set_priority(&mut self, priority: i32) -> Result<(), ClosedStream> {
+343        let max_send_data = self.state.max_send_data(self.id);
+344        let stream = self
+345            .state
+346            .send
+347            .get_mut(&self.id)
+348            .map(get_or_insert_send(max_send_data))
+349            .ok_or(ClosedStream { _private: () })?;
+350
+351        stream.priority = priority;
+352        Ok(())
+353    }
+354
+355    /// Get the priority of a stream
+356    ///
+357    /// # Panics
+358    /// - when applied to a receive stream
+359    pub fn priority(&self) -> Result<i32, ClosedStream> {
+360        let stream = self
+361            .state
+362            .send
+363            .get(&self.id)
+364            .ok_or(ClosedStream { _private: () })?;
+365
+366        Ok(stream.as_ref().map(|s| s.priority).unwrap_or_default())
+367    }
+368}
+369
+370/// A queue of streams with pending outgoing data, sorted by priority
+371struct PendingStreamsQueue {
+372    streams: BinaryHeap<PendingStream>,
+373    /// The next stream to write out. This is `Some` when `TransportConfig::send_fairness(false)` and writing a stream is
+374    /// interrupted while the stream still has some pending data. See `reinsert_pending()`.
+375    next: Option<PendingStream>,
+376    /// A monotonically decreasing counter, used to implement round-robin scheduling for streams of the same priority.
+377    /// Underflowing is not a practical concern, as it is initialized to u64::MAX and only decremented by 1 in `push_pending`
+378    recency: u64,
+379}
+380
+381impl PendingStreamsQueue {
+382    fn new() -> Self {
+383        Self {
+384            streams: BinaryHeap::new(),
+385            next: None,
+386            recency: u64::MAX,
+387        }
+388    }
+389
+390    /// Reinsert a stream that was pending and still contains unsent data.
+391    fn reinsert_pending(&mut self, id: StreamId, priority: i32) {
+392        assert!(self.next.is_none());
+393
+394        self.next = Some(PendingStream {
+395            priority,
+396            recency: self.recency, // the value here doesn't really matter
+397            id,
+398        });
+399    }
+400
+401    /// Push a pending stream ID with the given priority, queued after any already-queued streams for the priority
+402    fn push_pending(&mut self, id: StreamId, priority: i32) {
+403        // Note that in the case where fairness is disabled, if we have a reinserted stream we don't
+404        // bump it even if priority > next.priority. In order to minimize fragmentation we
+405        // always try to complete a stream once part of it has been written.
+406
+407        // As the recency counter is monotonically decreasing, we know that using its value to sort this stream will queue it
+408        // after all other queued streams of the same priority.
+409        // This is enough to implement round-robin scheduling for streams that are still pending even after being handled,
+410        // as in that case they are removed from the `BinaryHeap`, handled, and then immediately reinserted.
+411        self.recency -= 1;
+412        self.streams.push(PendingStream {
+413            priority,
+414            recency: self.recency,
+415            id,
+416        });
+417    }
+418
+419    fn pop(&mut self) -> Option<PendingStream> {
+420        self.next.take().or_else(|| self.streams.pop())
+421    }
+422
+423    fn clear(&mut self) {
+424        self.next = None;
+425        self.streams.clear();
+426    }
+427
+428    fn iter(&self) -> impl Iterator<Item = &PendingStream> {
+429        self.next.iter().chain(self.streams.iter())
+430    }
+431
+432    #[cfg(test)]
+433    fn len(&self) -> usize {
+434        self.streams.len() + self.next.is_some() as usize
+435    }
+436}
+437
+438/// The [`StreamId`] of a stream with pending data queued, ordered by its priority and recency
+439#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
+440struct PendingStream {
+441    /// The priority of the stream
+442    // Note that this field should be kept above the `recency` field, in order for the `Ord` derive to be correct
+443    // (See https://doc.rust-lang.org/stable/std/cmp/trait.Ord.html#derivable)
+444    priority: i32,
+445    /// A tie-breaker for streams of the same priority, used to improve fairness by implementing round-robin scheduling:
+446    /// Larger values are prioritized, so it is initialised to `u64::MAX`, and when a stream writes data, we know
+447    /// that it currently has the highest recency value, so it is deprioritized by setting its recency to 1 less than the
+448    /// previous lowest recency value, such that all other streams of this priority will get processed once before we get back
+449    /// round to this one
+450    recency: u64,
+451    /// The ID of the stream
+452    // The way this type is used ensures that every instance has a unique `recency` value, so this field should be kept below
+453    // the `priority` and `recency` fields, so that it does not interfere with the behaviour of the `Ord` derive
+454    id: StreamId,
+455}
+456
+457/// Application events about streams
+458#[derive(Debug, PartialEq, Eq)]
+459pub enum StreamEvent {
+460    /// One or more new streams has been opened and might be readable
+461    Opened {
+462        /// Directionality for which streams have been opened
+463        dir: Dir,
+464    },
+465    /// A currently open stream likely has data or errors waiting to be read
+466    Readable {
+467        /// Which stream is now readable
+468        id: StreamId,
+469    },
+470    /// A formerly write-blocked stream might be ready for a write or have been stopped
+471    ///
+472    /// Only generated for streams that are currently open.
+473    Writable {
+474        /// Which stream is now writable
+475        id: StreamId,
+476    },
+477    /// A finished stream has been fully acknowledged or stopped
+478    Finished {
+479        /// Which stream has been finished
+480        id: StreamId,
+481    },
+482    /// The peer asked us to stop sending on an outgoing stream
+483    Stopped {
+484        /// Which stream has been stopped
+485        id: StreamId,
+486        /// Error code supplied by the peer
+487        error_code: VarInt,
+488    },
+489    /// At least one new stream of a certain directionality may be opened
+490    Available {
+491        /// Directionality for which streams are newly available
+492        dir: Dir,
+493    },
+494}
+495
+496/// Indicates whether a frame needs to be transmitted
+497///
+498/// This type wraps around bool and uses the `#[must_use]` attribute in order
+499/// to prevent accidental loss of the frame transmission requirement.
+500#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
+501#[must_use = "A frame might need to be enqueued"]
+502pub struct ShouldTransmit(bool);
+503
+504impl ShouldTransmit {
+505    /// Returns whether a frame should be transmitted
+506    pub fn should_transmit(self) -> bool {
+507        self.0
+508    }
+509}
+510
+511/// Error indicating that a stream has not been opened or has already been finished or reset
+512#[derive(Debug, Default, Error, Clone, PartialEq, Eq)]
+513#[error("closed stream")]
+514pub struct ClosedStream {
+515    _private: (),
+516}
+517
+518impl From<ClosedStream> for io::Error {
+519    fn from(x: ClosedStream) -> Self {
+520        Self::new(io::ErrorKind::NotConnected, x)
+521    }
+522}
+523
+524#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+525enum StreamHalf {
+526    Send,
+527    Recv,
+528}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/streams/recv.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/streams/recv.rs.html new file mode 100644 index 000000000..827e51f86 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/streams/recv.rs.html @@ -0,0 +1,543 @@ +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            if end > final_offset || (frame.fin && end != final_offset) {
+62                debug!(end, final_offset, "final size error");
+63                return Err(TransportError::FINAL_SIZE_ERROR(""));
+64            }
+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 && !self.stopped {
+72            if let RecvState::Recv { ref mut size } = self.state {
+73                *size = Some(end);
+74            }
+75        }
+76
+77        self.end = self.end.max(end);
+78        // Don't bother storing data or releasing stream-level flow control credit if the stream's
+79        // already stopped
+80        if !self.stopped {
+81            self.assembler.insert(frame.offset, frame.data, payload_len);
+82        }
+83
+84        Ok((new_bytes, frame.fin && self.stopped))
+85    }
+86
+87    pub(super) fn stop(&mut self) -> Result<(u64, ShouldTransmit), ClosedStream> {
+88        if self.stopped {
+89            return Err(ClosedStream { _private: () });
+90        }
+91
+92        self.stopped = true;
+93        self.assembler.clear();
+94        // Issue flow control credit for unread data
+95        let read_credits = self.end - self.assembler.bytes_read();
+96        // This may send a spurious STOP_SENDING if we've already received all data, but it's a bit
+97        // fiddly to distinguish that from the case where we've received a FIN but are missing some
+98        // data that the peer might still be trying to retransmit, in which case a STOP_SENDING is
+99        // still useful.
+100        Ok((read_credits, ShouldTransmit(self.is_receiving())))
+101    }
+102
+103    /// Returns the window that should be advertised in a `MAX_STREAM_DATA` frame
+104    ///
+105    /// The method returns a tuple which consists of the window that should be
+106    /// announced, as well as a boolean parameter which indicates if a new
+107    /// transmission of the value is recommended. If the boolean value is
+108    /// `false` the new window should only be transmitted if a previous transmission
+109    /// had failed.
+110    pub(super) fn max_stream_data(&mut self, stream_receive_window: u64) -> (u64, ShouldTransmit) {
+111        let max_stream_data = self.assembler.bytes_read() + stream_receive_window;
+112
+113        // Only announce a window update if it's significant enough
+114        // to make it worthwhile sending a MAX_STREAM_DATA frame.
+115        // We use here a fraction of the configured stream receive window to make
+116        // the decision, and accommodate for streams using bigger windows requiring
+117        // less updates. A fixed size would also work - but it would need to be
+118        // smaller than `stream_receive_window` in order to make sure the stream
+119        // does not get stuck.
+120        let diff = max_stream_data - self.sent_max_stream_data;
+121        let transmit = self.can_send_flow_control() && diff >= (stream_receive_window / 8);
+122        (max_stream_data, ShouldTransmit(transmit))
+123    }
+124
+125    /// Records that a `MAX_STREAM_DATA` announcing a certain window was sent
+126    ///
+127    /// This will suppress enqueuing further `MAX_STREAM_DATA` frames unless
+128    /// either the previous transmission was not acknowledged or the window
+129    /// further increased.
+130    pub(super) fn record_sent_max_stream_data(&mut self, sent_value: u64) {
+131        if sent_value > self.sent_max_stream_data {
+132            self.sent_max_stream_data = sent_value;
+133        }
+134    }
+135
+136    /// Whether the total amount of data that the peer will send on this stream is unknown
+137    ///
+138    /// True until we've received either a reset or the final frame.
+139    ///
+140    /// Implies that the sender might benefit from stream-level flow control updates, and we might
+141    /// need to issue connection-level flow control updates due to flow control budget use by this
+142    /// stream in the future, even if it's been stopped.
+143    pub(super) fn final_offset_unknown(&self) -> bool {
+144        matches!(self.state, RecvState::Recv { size: None })
+145    }
+146
+147    /// Whether stream-level flow control updates should be sent for this stream
+148    pub(super) fn can_send_flow_control(&self) -> bool {
+149        // Stream-level flow control is redundant if the sender has already sent the whole stream,
+150        // and moot if we no longer want data on this stream.
+151        self.final_offset_unknown() && !self.stopped
+152    }
+153
+154    /// Whether data is still being accepted from the peer
+155    pub(super) fn is_receiving(&self) -> bool {
+156        matches!(self.state, RecvState::Recv { .. })
+157    }
+158
+159    fn final_offset(&self) -> Option<u64> {
+160        match self.state {
+161            RecvState::Recv { size } => size,
+162            RecvState::ResetRecvd { size, .. } => Some(size),
+163        }
+164    }
+165
+166    /// Returns `false` iff the reset was redundant
+167    pub(super) fn reset(
+168        &mut self,
+169        error_code: VarInt,
+170        final_offset: VarInt,
+171        received: u64,
+172        max_data: u64,
+173    ) -> Result<bool, TransportError> {
+174        // Validate final_offset
+175        if let Some(offset) = self.final_offset() {
+176            if offset != final_offset.into_inner() {
+177                return Err(TransportError::FINAL_SIZE_ERROR("inconsistent value"));
+178            }
+179        } else if self.end > u64::from(final_offset) {
+180            return Err(TransportError::FINAL_SIZE_ERROR(
+181                "lower than high water mark",
+182            ));
+183        }
+184        self.credit_consumed_by(final_offset.into(), received, max_data)?;
+185
+186        if matches!(self.state, RecvState::ResetRecvd { .. }) {
+187            return Ok(false);
+188        }
+189        self.state = RecvState::ResetRecvd {
+190            size: final_offset.into(),
+191            error_code,
+192        };
+193        // Nuke buffers so that future reads fail immediately, which ensures future reads don't
+194        // issue flow control credit redundant to that already issued. We could instead special-case
+195        // reset streams during read, but it's unclear if there's any benefit to retaining data for
+196        // reset streams.
+197        self.assembler.clear();
+198        Ok(true)
+199    }
+200
+201    pub(super) fn reset_code(&self) -> Option<VarInt> {
+202        match self.state {
+203            RecvState::ResetRecvd { error_code, .. } => Some(error_code),
+204            _ => None,
+205        }
+206    }
+207
+208    /// Compute the amount of flow control credit consumed, or return an error if more was consumed
+209    /// than issued
+210    fn credit_consumed_by(
+211        &self,
+212        offset: u64,
+213        received: u64,
+214        max_data: u64,
+215    ) -> Result<u64, TransportError> {
+216        let prev_end = self.end;
+217        let new_bytes = offset.saturating_sub(prev_end);
+218        if offset > self.sent_max_stream_data || received + new_bytes > max_data {
+219            debug!(
+220                received,
+221                new_bytes,
+222                max_data,
+223                offset,
+224                stream_max_data = self.sent_max_stream_data,
+225                "flow control error"
+226            );
+227            return Err(TransportError::FLOW_CONTROL_ERROR(""));
+228        }
+229
+230        Ok(new_bytes)
+231    }
+232}
+233
+234/// Chunks returned from [`RecvStream::read()`][crate::RecvStream::read].
+235///
+236/// ### Note: Finalization Needed
+237/// Bytes read from the stream are not released from the congestion window until
+238/// either [`Self::finalize()`] is called, or this type is dropped.
+239///
+240/// It is recommended that you call [`Self::finalize()`] because it returns a flag
+241/// telling you whether reading from the stream has resulted in the need to transmit a packet.
+242///
+243/// If this type is leaked, the stream will remain blocked on the remote peer until
+244/// another read from the stream is done.
+245pub struct Chunks<'a> {
+246    id: StreamId,
+247    ordered: bool,
+248    streams: &'a mut StreamsState,
+249    pending: &'a mut Retransmits,
+250    state: ChunksState,
+251    read: u64,
+252}
+253
+254impl<'a> Chunks<'a> {
+255    pub(super) fn new(
+256        id: StreamId,
+257        ordered: bool,
+258        streams: &'a mut StreamsState,
+259        pending: &'a mut Retransmits,
+260    ) -> Result<Self, ReadableError> {
+261        let mut entry = match streams.recv.entry(id) {
+262            Entry::Occupied(entry) => entry,
+263            Entry::Vacant(_) => return Err(ReadableError::ClosedStream),
+264        };
+265
+266        let mut recv =
+267            match get_or_insert_recv(streams.stream_receive_window)(entry.get_mut()).stopped {
+268                true => return Err(ReadableError::ClosedStream),
+269                false => entry.remove().unwrap().into_inner(), // this can't fail due to the previous get_or_insert_with
+270            };
+271
+272        recv.assembler.ensure_ordering(ordered)?;
+273        Ok(Self {
+274            id,
+275            ordered,
+276            streams,
+277            pending,
+278            state: ChunksState::Readable(recv),
+279            read: 0,
+280        })
+281    }
+282
+283    /// Next
+284    ///
+285    /// Should call finalize() when done calling this.
+286    pub fn next(&mut self, max_length: usize) -> Result<Option<Chunk>, ReadError> {
+287        let rs = match self.state {
+288            ChunksState::Readable(ref mut rs) => rs,
+289            ChunksState::Reset(error_code) => {
+290                return Err(ReadError::Reset(error_code));
+291            }
+292            ChunksState::Finished => {
+293                return Ok(None);
+294            }
+295            ChunksState::Finalized => panic!("must not call next() after finalize()"),
+296        };
+297
+298        if let Some(chunk) = rs.assembler.read(max_length, self.ordered) {
+299            self.read += chunk.bytes.len() as u64;
+300            return Ok(Some(chunk));
+301        }
+302
+303        match rs.state {
+304            RecvState::ResetRecvd { error_code, .. } => {
+305                debug_assert_eq!(self.read, 0, "reset streams have empty buffers");
+306                let state = mem::replace(&mut self.state, ChunksState::Reset(error_code));
+307                // At this point if we have `rs` self.state must be `ChunksState::Readable`
+308                let recv = match state {
+309                    ChunksState::Readable(recv) => StreamRecv::Open(recv),
+310                    _ => unreachable!("state must be ChunkState::Readable"),
+311                };
+312                self.streams.stream_recv_freed(self.id, recv);
+313                Err(ReadError::Reset(error_code))
+314            }
+315            RecvState::Recv { size } => {
+316                if size == Some(rs.end) && rs.assembler.bytes_read() == rs.end {
+317                    let state = mem::replace(&mut self.state, ChunksState::Finished);
+318                    // At this point if we have `rs` self.state must be `ChunksState::Readable`
+319                    let recv = match state {
+320                        ChunksState::Readable(recv) => StreamRecv::Open(recv),
+321                        _ => unreachable!("state must be ChunkState::Readable"),
+322                    };
+323                    self.streams.stream_recv_freed(self.id, recv);
+324                    Ok(None)
+325                } else {
+326                    // We don't need a distinct `ChunksState` variant for a blocked stream because
+327                    // retrying a read harmlessly re-traces our steps back to returning
+328                    // `Err(Blocked)` again. The buffers can't refill and the stream's own state
+329                    // can't change so long as this `Chunks` exists.
+330                    Err(ReadError::Blocked)
+331                }
+332            }
+333        }
+334    }
+335
+336    /// Mark the read data as consumed from the stream.
+337    ///
+338    /// The number of read bytes will be released from the congestion window,
+339    /// allowing the remote peer to send more data if it was previously blocked.
+340    ///
+341    /// If [`ShouldTransmit::should_transmit()`] returns `true`,
+342    /// a packet needs to be sent to the peer informing them that the stream is unblocked.
+343    /// This means that you should call [`Connection::poll_transmit()`][crate::Connection::poll_transmit]
+344    /// and send the returned packet as soon as is reasonable, to unblock the remote peer.
+345    pub fn finalize(mut self) -> ShouldTransmit {
+346        self.finalize_inner()
+347    }
+348
+349    fn finalize_inner(&mut self) -> ShouldTransmit {
+350        let state = mem::replace(&mut self.state, ChunksState::Finalized);
+351        if let ChunksState::Finalized = state {
+352            // Noop on repeated calls
+353            return ShouldTransmit(false);
+354        }
+355
+356        // We issue additional stream ID credit after the application is notified that a previously
+357        // open stream has finished or been reset and we've therefore disposed of its state, as
+358        // recorded by `stream_freed` calls in `next`.
+359        let mut should_transmit = self.streams.queue_max_stream_id(self.pending);
+360
+361        // If the stream hasn't finished, we may need to issue stream-level flow control credit
+362        if let ChunksState::Readable(mut rs) = state {
+363            let (_, max_stream_data) = rs.max_stream_data(self.streams.stream_receive_window);
+364            should_transmit |= max_stream_data.0;
+365            if max_stream_data.0 {
+366                self.pending.max_stream_data.insert(self.id);
+367            }
+368            // Return the stream to storage for future use
+369            self.streams
+370                .recv
+371                .insert(self.id, Some(StreamRecv::Open(rs)));
+372        }
+373
+374        // Issue connection-level flow control credit for any data we read regardless of state
+375        let max_data = self.streams.add_read_credits(self.read);
+376        self.pending.max_data |= max_data.0;
+377        should_transmit |= max_data.0;
+378        ShouldTransmit(should_transmit)
+379    }
+380}
+381
+382impl Drop for Chunks<'_> {
+383    fn drop(&mut self) {
+384        let _ = self.finalize_inner();
+385    }
+386}
+387
+388enum ChunksState {
+389    Readable(Box<Recv>),
+390    Reset(VarInt),
+391    Finished,
+392    Finalized,
+393}
+394
+395/// Errors triggered when reading from a recv stream
+396#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+397pub enum ReadError {
+398    /// No more data is currently available on this stream.
+399    ///
+400    /// If more data on this stream is received from the peer, an `Event::StreamReadable` will be
+401    /// generated for this stream, indicating that retrying the read might succeed.
+402    #[error("blocked")]
+403    Blocked,
+404    /// The peer abandoned transmitting data on this stream.
+405    ///
+406    /// Carries an application-defined error code.
+407    #[error("reset by peer: code {0}")]
+408    Reset(VarInt),
+409}
+410
+411/// Errors triggered when opening a recv stream for reading
+412#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+413pub enum ReadableError {
+414    /// The stream has not been opened or was already stopped, finished, or reset
+415    #[error("closed stream")]
+416    ClosedStream,
+417    /// Attempted an ordered read following an unordered read
+418    ///
+419    /// Performing an unordered read allows discontinuities to arise in the receive buffer of a
+420    /// stream which cannot be recovered, making further ordered reads impossible.
+421    #[error("ordered read after unordered read")]
+422    IllegalOrderedRead,
+423}
+424
+425impl From<IllegalOrderedRead> for ReadableError {
+426    fn from(_: IllegalOrderedRead) -> Self {
+427        Self::IllegalOrderedRead
+428    }
+429}
+430
+431#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+432enum RecvState {
+433    Recv { size: Option<u64> },
+434    ResetRecvd { size: u64, error_code: VarInt },
+435}
+436
+437impl Default for RecvState {
+438    fn default() -> Self {
+439        Self::Recv { size: None }
+440    }
+441}
+442
+443#[cfg(test)]
+444mod tests {
+445    use bytes::Bytes;
+446
+447    use crate::{Dir, Side};
+448
+449    use super::*;
+450
+451    #[test]
+452    fn reordered_frames_while_stopped() {
+453        const INITIAL_BYTES: u64 = 3;
+454        const INITIAL_OFFSET: u64 = 3;
+455        const RECV_WINDOW: u64 = 8;
+456        let mut s = Recv::new(RECV_WINDOW);
+457        let mut data_recvd = 0;
+458        // Receive bytes 3..6
+459        let (new_bytes, is_closed) = s
+460            .ingest(
+461                frame::Stream {
+462                    id: StreamId::new(Side::Client, Dir::Uni, 0),
+463                    offset: INITIAL_OFFSET,
+464                    fin: false,
+465                    data: Bytes::from_static(&[0; INITIAL_BYTES as usize]),
+466                },
+467                123,
+468                data_recvd,
+469                data_recvd + 1024,
+470            )
+471            .unwrap();
+472        data_recvd += new_bytes;
+473        assert_eq!(new_bytes, INITIAL_OFFSET + INITIAL_BYTES);
+474        assert!(!is_closed);
+475
+476        let (credits, transmit) = s.stop().unwrap();
+477        assert!(transmit.should_transmit());
+478        assert_eq!(
+479            credits,
+480            INITIAL_OFFSET + INITIAL_BYTES,
+481            "full connection flow control credit is issued by stop"
+482        );
+483
+484        let (max_stream_data, transmit) = s.max_stream_data(RECV_WINDOW);
+485        assert!(!transmit.should_transmit());
+486        assert_eq!(
+487            max_stream_data, RECV_WINDOW,
+488            "stream flow control credit isn't issued by stop"
+489        );
+490
+491        // Receive byte 7
+492        let (new_bytes, is_closed) = s
+493            .ingest(
+494                frame::Stream {
+495                    id: StreamId::new(Side::Client, Dir::Uni, 0),
+496                    offset: RECV_WINDOW - 1,
+497                    fin: false,
+498                    data: Bytes::from_static(&[0; 1]),
+499                },
+500                123,
+501                data_recvd,
+502                data_recvd + 1024,
+503            )
+504            .unwrap();
+505        data_recvd += new_bytes;
+506        assert_eq!(new_bytes, RECV_WINDOW - (INITIAL_OFFSET + INITIAL_BYTES));
+507        assert!(!is_closed);
+508
+509        let (max_stream_data, transmit) = s.max_stream_data(RECV_WINDOW);
+510        assert!(!transmit.should_transmit());
+511        assert_eq!(
+512            max_stream_data, RECV_WINDOW,
+513            "stream flow control credit isn't issued after stop"
+514        );
+515
+516        // Receive bytes 0..3
+517        let (new_bytes, is_closed) = s
+518            .ingest(
+519                frame::Stream {
+520                    id: StreamId::new(Side::Client, Dir::Uni, 0),
+521                    offset: 0,
+522                    fin: false,
+523                    data: Bytes::from_static(&[0; INITIAL_OFFSET as usize]),
+524                },
+525                123,
+526                data_recvd,
+527                data_recvd + 1024,
+528            )
+529            .unwrap();
+530        assert_eq!(
+531            new_bytes, 0,
+532            "reordered frames don't issue connection-level flow control for stopped streams"
+533        );
+534        assert!(!is_closed);
+535
+536        let (max_stream_data, transmit) = s.max_stream_data(RECV_WINDOW);
+537        assert!(!transmit.should_transmit());
+538        assert_eq!(
+539            max_stream_data, RECV_WINDOW,
+540            "stream flow control credit isn't issued after stop"
+541        );
+542    }
+543}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/streams/send.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/streams/send.rs.html new file mode 100644 index 000000000..ec091cda4 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/streams/send.rs.html @@ -0,0 +1,402 @@ +send.rs - source

iroh_quinn_proto/connection/streams/
send.rs

1use bytes::Bytes;
+2use thiserror::Error;
+3
+4use crate::{VarInt, connection::send_buffer::SendBuffer, frame};
+5
+6#[derive(Debug)]
+7pub(super) struct Send {
+8    pub(super) max_data: u64,
+9    pub(super) state: SendState,
+10    pub(super) pending: SendBuffer,
+11    pub(super) priority: i32,
+12    /// Whether a frame containing a FIN bit must be transmitted, even if we don't have any new data
+13    pub(super) fin_pending: bool,
+14    /// Whether this stream is in the `connection_blocked` list of `Streams`
+15    pub(super) connection_blocked: bool,
+16    /// The reason the peer wants us to stop, if `STOP_SENDING` was received
+17    pub(super) stop_reason: Option<VarInt>,
+18}
+19
+20impl Send {
+21    pub(super) fn new(max_data: VarInt) -> Box<Self> {
+22        Box::new(Self {
+23            max_data: max_data.into(),
+24            state: SendState::Ready,
+25            pending: SendBuffer::new(),
+26            priority: 0,
+27            fin_pending: false,
+28            connection_blocked: false,
+29            stop_reason: None,
+30        })
+31    }
+32
+33    /// Whether the stream has been reset
+34    pub(super) fn is_reset(&self) -> bool {
+35        matches!(self.state, SendState::ResetSent)
+36    }
+37
+38    pub(super) fn finish(&mut self) -> Result<(), FinishError> {
+39        if let Some(error_code) = self.stop_reason {
+40            Err(FinishError::Stopped(error_code))
+41        } else if self.state == SendState::Ready {
+42            self.state = SendState::DataSent {
+43                finish_acked: false,
+44            };
+45            self.fin_pending = true;
+46            Ok(())
+47        } else {
+48            Err(FinishError::ClosedStream)
+49        }
+50    }
+51
+52    pub(super) fn write<S: BytesSource>(
+53        &mut self,
+54        source: &mut S,
+55        limit: u64,
+56    ) -> Result<Written, WriteError> {
+57        if !self.is_writable() {
+58            return Err(WriteError::ClosedStream);
+59        }
+60        if let Some(error_code) = self.stop_reason {
+61            return Err(WriteError::Stopped(error_code));
+62        }
+63        let budget = self.max_data - self.pending.offset();
+64        if budget == 0 {
+65            return Err(WriteError::Blocked);
+66        }
+67        let mut limit = limit.min(budget) as usize;
+68
+69        let mut result = Written::default();
+70        loop {
+71            let (chunk, chunks_consumed) = source.pop_chunk(limit);
+72            result.chunks += chunks_consumed;
+73            result.bytes += chunk.len();
+74
+75            if chunk.is_empty() {
+76                break;
+77            }
+78
+79            limit -= chunk.len();
+80            self.pending.write(chunk);
+81        }
+82
+83        Ok(result)
+84    }
+85
+86    /// Update stream state due to a reset sent by the local application
+87    pub(super) fn reset(&mut self) {
+88        use SendState::*;
+89        if let DataSent { .. } | Ready = self.state {
+90            self.state = ResetSent;
+91        }
+92    }
+93
+94    /// Handle STOP_SENDING
+95    ///
+96    /// Returns true if the stream was stopped due to this frame, and false
+97    /// if it had been stopped before
+98    pub(super) fn try_stop(&mut self, error_code: VarInt) -> bool {
+99        if self.stop_reason.is_none() {
+100            self.stop_reason = Some(error_code);
+101            true
+102        } else {
+103            false
+104        }
+105    }
+106
+107    /// Returns whether the stream has been finished and all data has been acknowledged by the peer
+108    pub(super) fn ack(&mut self, frame: frame::StreamMeta) -> bool {
+109        self.pending.ack(frame.offsets);
+110        match self.state {
+111            SendState::DataSent {
+112                ref mut finish_acked,
+113            } => {
+114                *finish_acked |= frame.fin;
+115                *finish_acked && self.pending.is_fully_acked()
+116            }
+117            _ => false,
+118        }
+119    }
+120
+121    /// Handle increase to stream-level flow control limit
+122    ///
+123    /// Returns whether the stream was unblocked
+124    pub(super) fn increase_max_data(&mut self, offset: u64) -> bool {
+125        if offset <= self.max_data || self.state != SendState::Ready {
+126            return false;
+127        }
+128        let was_blocked = self.pending.offset() == self.max_data;
+129        self.max_data = offset;
+130        was_blocked
+131    }
+132
+133    pub(super) fn offset(&self) -> u64 {
+134        self.pending.offset()
+135    }
+136
+137    pub(super) fn is_pending(&self) -> bool {
+138        self.pending.has_unsent_data() || self.fin_pending
+139    }
+140
+141    pub(super) fn is_writable(&self) -> bool {
+142        matches!(self.state, SendState::Ready)
+143    }
+144}
+145
+146/// A [`BytesSource`] implementation for `&'a mut [Bytes]`
+147///
+148/// The type allows to dequeue [`Bytes`] chunks from an array of chunks, up to
+149/// a configured limit.
+150pub(crate) struct BytesArray<'a> {
+151    /// The wrapped slice of `Bytes`
+152    chunks: &'a mut [Bytes],
+153    /// The amount of chunks consumed from this source
+154    consumed: usize,
+155}
+156
+157impl<'a> BytesArray<'a> {
+158    pub(crate) fn from_chunks(chunks: &'a mut [Bytes]) -> Self {
+159        Self {
+160            chunks,
+161            consumed: 0,
+162        }
+163    }
+164}
+165
+166impl BytesSource for BytesArray<'_> {
+167    fn pop_chunk(&mut self, limit: usize) -> (Bytes, usize) {
+168        // The loop exists to skip empty chunks while still marking them as
+169        // consumed
+170        let mut chunks_consumed = 0;
+171
+172        while self.consumed < self.chunks.len() {
+173            let chunk = &mut self.chunks[self.consumed];
+174
+175            if chunk.len() <= limit {
+176                let chunk = std::mem::take(chunk);
+177                self.consumed += 1;
+178                chunks_consumed += 1;
+179                if chunk.is_empty() {
+180                    continue;
+181                }
+182                return (chunk, chunks_consumed);
+183            } else if limit > 0 {
+184                let chunk = chunk.split_to(limit);
+185                return (chunk, chunks_consumed);
+186            } else {
+187                break;
+188            }
+189        }
+190
+191        (Bytes::new(), chunks_consumed)
+192    }
+193}
+194
+195/// A [`BytesSource`] implementation for `&[u8]`
+196///
+197/// The type allows to dequeue a single [`Bytes`] chunk, which will be lazily
+198/// created from a reference. This allows to defer the allocation until it is
+199/// known how much data needs to be copied.
+200pub(crate) struct ByteSlice<'a> {
+201    /// The wrapped byte slice
+202    data: &'a [u8],
+203}
+204
+205impl<'a> ByteSlice<'a> {
+206    pub(crate) fn from_slice(data: &'a [u8]) -> Self {
+207        Self { data }
+208    }
+209}
+210
+211impl BytesSource for ByteSlice<'_> {
+212    fn pop_chunk(&mut self, limit: usize) -> (Bytes, usize) {
+213        let limit = limit.min(self.data.len());
+214        if limit == 0 {
+215            return (Bytes::new(), 0);
+216        }
+217
+218        let chunk = Bytes::from(self.data[..limit].to_owned());
+219        self.data = &self.data[chunk.len()..];
+220
+221        let chunks_consumed = usize::from(self.data.is_empty());
+222        (chunk, chunks_consumed)
+223    }
+224}
+225
+226/// A source of one or more buffers which can be converted into `Bytes` buffers on demand
+227///
+228/// The purpose of this data type is to defer conversion as long as possible,
+229/// so that no heap allocation is required in case no data is writable.
+230pub(super) trait BytesSource {
+231    /// Returns the next chunk from the source of owned chunks.
+232    ///
+233    /// This method will consume parts of the source.
+234    /// Calling it will yield `Bytes` elements up to the configured `limit`.
+235    ///
+236    /// The method returns a tuple:
+237    /// - The first item is the yielded `Bytes` element. The element will be
+238    ///   empty if the limit is zero or no more data is available.
+239    /// - The second item returns how many complete chunks inside the source had
+240    ///   had been consumed. This can be less than 1, if a chunk inside the
+241    ///   source had been truncated in order to adhere to the limit. It can also
+242    ///   be more than 1, if zero-length chunks had been skipped.
+243    fn pop_chunk(&mut self, limit: usize) -> (Bytes, usize);
+244}
+245
+246/// Indicates how many bytes and chunks had been transferred in a write operation
+247#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
+248pub struct Written {
+249    /// The amount of bytes which had been written
+250    pub bytes: usize,
+251    /// The amount of full chunks which had been written
+252    ///
+253    /// If a chunk was only partially written, it will not be counted by this field.
+254    pub chunks: usize,
+255}
+256
+257/// Errors triggered while writing to a send stream
+258#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+259pub enum WriteError {
+260    /// The peer is not able to accept additional data, or the connection is congested.
+261    ///
+262    /// If the peer issues additional flow control credit, a [`StreamEvent::Writable`] event will
+263    /// be generated, indicating that retrying the write might succeed.
+264    ///
+265    /// [`StreamEvent::Writable`]: crate::StreamEvent::Writable
+266    #[error("unable to accept further writes")]
+267    Blocked,
+268    /// The peer is no longer accepting data on this stream, and it has been implicitly reset. The
+269    /// stream cannot be finished or further written to.
+270    ///
+271    /// Carries an application-defined error code.
+272    ///
+273    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+274    #[error("stopped by peer: code {0}")]
+275    Stopped(VarInt),
+276    /// The stream has not been opened or has already been finished or reset
+277    #[error("closed stream")]
+278    ClosedStream,
+279}
+280
+281#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+282pub(super) enum SendState {
+283    /// Sending new data
+284    Ready,
+285    /// Stream was finished; now sending retransmits only
+286    DataSent { finish_acked: bool },
+287    /// Sent RESET
+288    ResetSent,
+289}
+290
+291/// Reasons why attempting to finish a stream might fail
+292#[derive(Debug, Error, Clone, PartialEq, Eq)]
+293pub enum FinishError {
+294    /// The peer is no longer accepting data on this stream. No
+295    /// [`StreamEvent::Finished`] event will be emitted for this stream.
+296    ///
+297    /// Carries an application-defined error code.
+298    ///
+299    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+300    #[error("stopped by peer: code {0}")]
+301    Stopped(VarInt),
+302    /// The stream has not been opened or was already finished or reset
+303    #[error("closed stream")]
+304    ClosedStream,
+305}
+306
+307#[cfg(test)]
+308mod tests {
+309    use super::*;
+310
+311    #[test]
+312    fn bytes_array() {
+313        let full = b"Hello World 123456789 ABCDEFGHJIJKLMNOPQRSTUVWXYZ".to_owned();
+314        for limit in 0..full.len() {
+315            let mut chunks = [
+316                Bytes::from_static(b""),
+317                Bytes::from_static(b"Hello "),
+318                Bytes::from_static(b"Wo"),
+319                Bytes::from_static(b""),
+320                Bytes::from_static(b"r"),
+321                Bytes::from_static(b"ld"),
+322                Bytes::from_static(b""),
+323                Bytes::from_static(b" 12345678"),
+324                Bytes::from_static(b"9 ABCDE"),
+325                Bytes::from_static(b"F"),
+326                Bytes::from_static(b"GHJIJKLMNOPQRSTUVWXYZ"),
+327            ];
+328            let num_chunks = chunks.len();
+329            let last_chunk_len = chunks[chunks.len() - 1].len();
+330
+331            let mut array = BytesArray::from_chunks(&mut chunks);
+332
+333            let mut buf = Vec::new();
+334            let mut chunks_popped = 0;
+335            let mut chunks_consumed = 0;
+336            let mut remaining = limit;
+337            loop {
+338                let (chunk, consumed) = array.pop_chunk(remaining);
+339                chunks_consumed += consumed;
+340
+341                if !chunk.is_empty() {
+342                    buf.extend_from_slice(&chunk);
+343                    remaining -= chunk.len();
+344                    chunks_popped += 1;
+345                } else {
+346                    break;
+347                }
+348            }
+349
+350            assert_eq!(&buf[..], &full[..limit]);
+351
+352            if limit == full.len() {
+353                // Full consumption of the last chunk
+354                assert_eq!(chunks_consumed, num_chunks);
+355                // Since there are empty chunks, we consume more than there are popped
+356                assert_eq!(chunks_consumed, chunks_popped + 3);
+357            } else if limit > full.len() - last_chunk_len {
+358                // Partial consumption of the last chunk
+359                assert_eq!(chunks_consumed, num_chunks - 1);
+360                assert_eq!(chunks_consumed, chunks_popped + 2);
+361            }
+362        }
+363    }
+364
+365    #[test]
+366    fn byte_slice() {
+367        let full = b"Hello World 123456789 ABCDEFGHJIJKLMNOPQRSTUVWXYZ".to_owned();
+368        for limit in 0..full.len() {
+369            let mut array = ByteSlice::from_slice(&full[..]);
+370
+371            let mut buf = Vec::new();
+372            let mut chunks_popped = 0;
+373            let mut chunks_consumed = 0;
+374            let mut remaining = limit;
+375            loop {
+376                let (chunk, consumed) = array.pop_chunk(remaining);
+377                chunks_consumed += consumed;
+378
+379                if !chunk.is_empty() {
+380                    buf.extend_from_slice(&chunk);
+381                    remaining -= chunk.len();
+382                    chunks_popped += 1;
+383                } else {
+384                    break;
+385                }
+386            }
+387
+388            assert_eq!(&buf[..], &full[..limit]);
+389            if limit != 0 {
+390                assert_eq!(chunks_popped, 1);
+391            } else {
+392                assert_eq!(chunks_popped, 0);
+393            }
+394
+395            if limit == full.len() {
+396                assert_eq!(chunks_consumed, 1);
+397            } else {
+398                assert_eq!(chunks_consumed, 0);
+399            }
+400        }
+401    }
+402}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/streams/state.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/streams/state.rs.html new file mode 100644 index 000000000..5ad3dee95 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/streams/state.rs.html @@ -0,0 +1,2099 @@ +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 bytes::BufMut;
+8use rustc_hash::FxHashMap;
+9use tracing::{debug, trace};
+10
+11use super::{
+12    PendingStreamsQueue, Recv, Retransmits, Send, SendState, ShouldTransmit, StreamEvent,
+13    StreamHalf, ThinRetransmits,
+14};
+15use crate::{
+16    Dir, MAX_STREAM_COUNT, Side, StreamId, TransportError, VarInt,
+17    coding::{BufMutExt, Encodable},
+18    connection::{qlog::QlogSentPacket, stats::FrameStats},
+19    frame::{self, Frame, FrameStruct, StreamMetaVec},
+20    transport_parameters::TransportParameters,
+21};
+22
+23/// Wrapper around `Recv` that facilitates reusing `Recv` instances
+24#[derive(Debug)]
+25pub(super) enum StreamRecv {
+26    /// A `Recv` that is ready to be opened
+27    Free(Box<Recv>),
+28    /// A `Recv` that has been opened
+29    Open(Box<Recv>),
+30}
+31
+32impl StreamRecv {
+33    /// Returns a reference to the inner `Recv` if the stream is open
+34    pub(super) fn as_open_recv(&self) -> Option<&Recv> {
+35        match self {
+36            Self::Open(r) => Some(r),
+37            _ => None,
+38        }
+39    }
+40
+41    // Returns a mutable reference to the inner `Recv` if the stream is open
+42    pub(super) fn as_open_recv_mut(&mut self) -> Option<&mut Recv> {
+43        match self {
+44            Self::Open(r) => Some(r),
+45            _ => None,
+46        }
+47    }
+48
+49    // Returns the inner `Recv`
+50    pub(super) fn into_inner(self) -> Box<Recv> {
+51        match self {
+52            Self::Free(r) | Self::Open(r) => r,
+53        }
+54    }
+55
+56    // Reinitialize the stream so the inner `Recv` can be reused
+57    pub(super) fn free(self, initial_max_data: u64) -> Self {
+58        match self {
+59            Self::Free(_) => unreachable!("Self::Free on reinit()"),
+60            Self::Open(mut recv) => {
+61                recv.reinit(initial_max_data);
+62                Self::Free(recv)
+63            }
+64        }
+65    }
+66}
+67
+68#[allow(unreachable_pub)] // fuzzing only
+69pub struct StreamsState {
+70    pub(super) side: Side,
+71    // Set of streams that are currently open, or could be immediately opened by the peer
+72    pub(super) send: FxHashMap<StreamId, Option<Box<Send>>>,
+73    pub(super) recv: FxHashMap<StreamId, Option<StreamRecv>>,
+74    pub(super) free_recv: Vec<StreamRecv>,
+75    pub(super) next: [u64; 2],
+76    /// Maximum number of locally-initiated streams that may be opened over the lifetime of the
+77    /// connection so far, per direction
+78    pub(super) max: [u64; 2],
+79    /// Maximum number of remotely-initiated streams that may be opened over the lifetime of the
+80    /// connection so far, per direction
+81    pub(super) max_remote: [u64; 2],
+82    /// Value of `max_remote` most recently transmitted to the peer in a `MAX_STREAMS` frame
+83    sent_max_remote: [u64; 2],
+84    /// Number of streams that we've given the peer permission to open and which aren't fully closed
+85    pub(super) allocated_remote_count: [u64; 2],
+86    /// Size of the desired stream flow control window. May be smaller than `allocated_remote_count`
+87    /// due to `set_max_concurrent` calls.
+88    max_concurrent_remote_count: [u64; 2],
+89    /// Whether `max_concurrent_remote_count` has ever changed
+90    flow_control_adjusted: bool,
+91    /// Lowest remotely-initiated stream index that haven't actually been opened by the peer
+92    pub(super) next_remote: [u64; 2],
+93    /// Whether the remote endpoint has opened any streams the application doesn't know about yet,
+94    /// per directionality
+95    opened: [bool; 2],
+96    // Next to report to the application, once opened
+97    pub(super) next_reported_remote: [u64; 2],
+98    /// Number of outbound streams
+99    ///
+100    /// This differs from `self.send.len()` in that it does not include streams that the peer is
+101    /// permitted to open but which have not yet been opened.
+102    pub(super) send_streams: usize,
+103    /// Streams with outgoing data queued, sorted by priority
+104    pub(super) pending: PendingStreamsQueue,
+105
+106    events: VecDeque<StreamEvent>,
+107    /// Streams blocked on connection-level flow control or stream window space
+108    ///
+109    /// Streams are only added to this list when a write fails.
+110    pub(super) connection_blocked: Vec<StreamId>,
+111    /// Connection-level flow control budget dictated by the peer
+112    pub(super) max_data: u64,
+113    /// The initial receive window
+114    receive_window: u64,
+115    /// Limit on incoming data, which is transmitted through `MAX_DATA` frames
+116    local_max_data: u64,
+117    /// The last value of `MAX_DATA` which had been queued for transmission in
+118    /// an outgoing `MAX_DATA` frame
+119    sent_max_data: VarInt,
+120    /// Sum of current offsets of all send streams.
+121    pub(super) data_sent: u64,
+122    /// Sum of end offsets of all receive streams. Includes gaps, so it's an upper bound.
+123    data_recvd: u64,
+124    /// Total quantity of unacknowledged outgoing data
+125    pub(super) unacked_data: u64,
+126    /// Configured upper bound for `unacked_data`.
+127    ///
+128    /// Note this may be less than `unacked_data` if the user has set a new value.
+129    pub(super) send_window: u64,
+130    /// Configured upper bound for how much unacked data the peer can send us per stream
+131    pub(super) stream_receive_window: u64,
+132
+133    // Pertinent state from the TransportParameters supplied by the peer
+134    initial_max_stream_data_uni: VarInt,
+135    initial_max_stream_data_bidi_local: VarInt,
+136    initial_max_stream_data_bidi_remote: VarInt,
+137
+138    /// The shrink to be applied to local_max_data when receive_window is shrunk
+139    receive_window_shrink_debt: u64,
+140}
+141
+142impl StreamsState {
+143    #[allow(unreachable_pub)] // fuzzing only
+144    pub fn new(
+145        side: Side,
+146        max_remote_uni: VarInt,
+147        max_remote_bi: VarInt,
+148        send_window: u64,
+149        receive_window: VarInt,
+150        stream_receive_window: VarInt,
+151    ) -> Self {
+152        let mut this = Self {
+153            side,
+154            send: FxHashMap::default(),
+155            recv: FxHashMap::default(),
+156            free_recv: Vec::new(),
+157            next: [0, 0],
+158            max: [0, 0],
+159            max_remote: [max_remote_bi.into(), max_remote_uni.into()],
+160            sent_max_remote: [max_remote_bi.into(), max_remote_uni.into()],
+161            allocated_remote_count: [max_remote_bi.into(), max_remote_uni.into()],
+162            max_concurrent_remote_count: [max_remote_bi.into(), max_remote_uni.into()],
+163            flow_control_adjusted: false,
+164            next_remote: [0, 0],
+165            opened: [false, false],
+166            next_reported_remote: [0, 0],
+167            send_streams: 0,
+168            pending: PendingStreamsQueue::new(),
+169            events: VecDeque::new(),
+170            connection_blocked: Vec::new(),
+171            max_data: 0,
+172            receive_window: receive_window.into(),
+173            local_max_data: receive_window.into(),
+174            sent_max_data: receive_window,
+175            data_sent: 0,
+176            data_recvd: 0,
+177            unacked_data: 0,
+178            send_window,
+179            stream_receive_window: stream_receive_window.into(),
+180            initial_max_stream_data_uni: 0u32.into(),
+181            initial_max_stream_data_bidi_local: 0u32.into(),
+182            initial_max_stream_data_bidi_remote: 0u32.into(),
+183            receive_window_shrink_debt: 0,
+184        };
+185
+186        for dir in Dir::iter() {
+187            for i in 0..this.max_remote[dir as usize] {
+188                this.insert(true, StreamId::new(!side, dir, i));
+189            }
+190        }
+191
+192        this
+193    }
+194
+195    pub(crate) fn set_params(&mut self, params: &TransportParameters) {
+196        self.initial_max_stream_data_uni = params.initial_max_stream_data_uni;
+197        self.initial_max_stream_data_bidi_local = params.initial_max_stream_data_bidi_local;
+198        self.initial_max_stream_data_bidi_remote = params.initial_max_stream_data_bidi_remote;
+199        self.max[Dir::Bi as usize] = params.initial_max_streams_bidi.into();
+200        self.max[Dir::Uni as usize] = params.initial_max_streams_uni.into();
+201        self.received_max_data(params.initial_max_data);
+202        for i in 0..self.max_remote[Dir::Bi as usize] {
+203            let id = StreamId::new(!self.side, Dir::Bi, i);
+204            if let Some(s) = self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+205                s.max_data = params.initial_max_stream_data_bidi_local.into();
+206            }
+207        }
+208    }
+209
+210    /// Ensure we have space for at least a full flow control window of remotely-initiated streams
+211    /// to be open, and notify the peer if the window has moved
+212    fn ensure_remote_streams(&mut self, dir: Dir) {
+213        let new_count = self.max_concurrent_remote_count[dir as usize]
+214            .saturating_sub(self.allocated_remote_count[dir as usize]);
+215        for i in 0..new_count {
+216            let id = StreamId::new(!self.side, dir, self.max_remote[dir as usize] + i);
+217            self.insert(true, id);
+218        }
+219        self.allocated_remote_count[dir as usize] += new_count;
+220        self.max_remote[dir as usize] += new_count;
+221    }
+222
+223    pub(crate) fn zero_rtt_rejected(&mut self) {
+224        // Revert to initial state for outgoing streams
+225        for dir in Dir::iter() {
+226            for i in 0..self.next[dir as usize] {
+227                // We don't bother calling `stream_freed` here because we explicitly reset affected
+228                // counters below.
+229                let id = StreamId::new(self.side, dir, i);
+230                self.send.remove(&id).unwrap();
+231                if let Dir::Bi = dir {
+232                    self.recv.remove(&id).unwrap();
+233                }
+234            }
+235            self.next[dir as usize] = 0;
+236
+237            // If 0-RTT was rejected, any flow control frames we sent were lost.
+238            if self.flow_control_adjusted {
+239                // Conservative approximation of whatever we sent in transport parameters
+240                self.sent_max_remote[dir as usize] = 0;
+241            }
+242        }
+243
+244        self.pending.clear();
+245        self.send_streams = 0;
+246        self.data_sent = 0;
+247        self.connection_blocked.clear();
+248    }
+249
+250    /// Process incoming stream frame
+251    ///
+252    /// If successful, returns whether a `MAX_DATA` frame needs to be transmitted
+253    pub(crate) fn received(
+254        &mut self,
+255        frame: frame::Stream,
+256        payload_len: usize,
+257    ) -> Result<ShouldTransmit, TransportError> {
+258        let id = frame.id;
+259        self.validate_receive_id(id).inspect_err(|_e| {
+260            debug!("received illegal STREAM frame");
+261        })?;
+262
+263        let rs = match self
+264            .recv
+265            .get_mut(&id)
+266            .map(get_or_insert_recv(self.stream_receive_window))
+267        {
+268            Some(rs) => rs,
+269            None => {
+270                trace!("dropping frame for closed stream");
+271                return Ok(ShouldTransmit(false));
+272            }
+273        };
+274
+275        if !rs.is_receiving() {
+276            trace!("dropping frame for finished stream");
+277            return Ok(ShouldTransmit(false));
+278        }
+279
+280        let (new_bytes, closed) =
+281            rs.ingest(frame, payload_len, self.data_recvd, self.local_max_data)?;
+282        self.data_recvd = self.data_recvd.saturating_add(new_bytes);
+283
+284        if !rs.stopped {
+285            self.on_stream_frame(true, id);
+286            return Ok(ShouldTransmit(false));
+287        }
+288
+289        // Stopped streams become closed instantly on FIN, so check whether we need to clean up
+290        if closed {
+291            let rs = self.recv.remove(&id).flatten().unwrap();
+292            self.stream_recv_freed(id, rs);
+293        }
+294
+295        // We don't buffer data on stopped streams, so issue flow control credit immediately
+296        Ok(self.add_read_credits(new_bytes))
+297    }
+298
+299    /// Process incoming RESET_STREAM frame
+300    ///
+301    /// If successful, returns whether a `MAX_DATA` frame needs to be transmitted
+302    #[allow(unreachable_pub)] // fuzzing only
+303    pub fn received_reset(
+304        &mut self,
+305        frame: frame::ResetStream,
+306    ) -> Result<ShouldTransmit, TransportError> {
+307        let frame::ResetStream {
+308            id,
+309            error_code,
+310            final_offset,
+311        } = frame;
+312        self.validate_receive_id(id).inspect_err(|_e| {
+313            debug!("received illegal RESET_STREAM frame");
+314        })?;
+315
+316        let rs = match self
+317            .recv
+318            .get_mut(&id)
+319            .map(get_or_insert_recv(self.stream_receive_window))
+320        {
+321            Some(stream) => stream,
+322            None => {
+323                trace!("received RESET_STREAM on closed stream");
+324                return Ok(ShouldTransmit(false));
+325            }
+326        };
+327
+328        // State transition
+329        if !rs.reset(
+330            error_code,
+331            final_offset,
+332            self.data_recvd,
+333            self.local_max_data,
+334        )? {
+335            // Redundant reset
+336            return Ok(ShouldTransmit(false));
+337        }
+338        let bytes_read = rs.assembler.bytes_read();
+339        let stopped = rs.stopped;
+340        let end = rs.end;
+341        if stopped {
+342            // Stopped streams should be disposed immediately on reset
+343            let rs = self.recv.remove(&id).flatten().unwrap();
+344            self.stream_recv_freed(id, rs);
+345        }
+346        self.on_stream_frame(!stopped, id);
+347
+348        // Update connection-level flow control
+349        Ok(if bytes_read != final_offset.into_inner() {
+350            // bytes_read is always <= end, so this won't underflow.
+351            self.data_recvd = self
+352                .data_recvd
+353                .saturating_add(u64::from(final_offset) - end);
+354            self.add_read_credits(u64::from(final_offset) - bytes_read)
+355        } else {
+356            ShouldTransmit(false)
+357        })
+358    }
+359
+360    /// Process incoming `STOP_SENDING` frame
+361    #[allow(unreachable_pub)] // fuzzing only
+362    pub fn received_stop_sending(&mut self, id: StreamId, error_code: VarInt) {
+363        let max_send_data = self.max_send_data(id);
+364        let stream = match self
+365            .send
+366            .get_mut(&id)
+367            .map(get_or_insert_send(max_send_data))
+368        {
+369            Some(ss) => ss,
+370            None => return,
+371        };
+372
+373        if stream.try_stop(error_code) {
+374            self.events
+375                .push_back(StreamEvent::Stopped { id, error_code });
+376            self.on_stream_frame(false, id);
+377        }
+378    }
+379
+380    pub(crate) fn reset_acked(&mut self, id: StreamId) {
+381        match self.send.entry(id) {
+382            hash_map::Entry::Vacant(_) => {}
+383            hash_map::Entry::Occupied(e) => {
+384                if let Some(SendState::ResetSent) = e.get().as_ref().map(|s| s.state) {
+385                    e.remove_entry();
+386                    self.stream_freed(id, StreamHalf::Send);
+387                }
+388            }
+389        }
+390    }
+391
+392    /// Whether any stream data is queued, regardless of control frames
+393    pub(crate) fn can_send_stream_data(&self) -> bool {
+394        // Reset streams may linger in the pending stream list, but will never produce stream frames
+395        self.pending.iter().any(|stream| {
+396            self.send
+397                .get(&stream.id)
+398                .and_then(|s| s.as_ref())
+399                .is_some_and(|s| !s.is_reset())
+400        })
+401    }
+402
+403    /// Whether MAX_STREAM_DATA frames could be sent for stream `id`
+404    pub(crate) fn can_send_flow_control(&self, id: StreamId) -> bool {
+405        self.recv
+406            .get(&id)
+407            .and_then(|s| s.as_ref())
+408            .and_then(|s| s.as_open_recv())
+409            .is_some_and(|s| s.can_send_flow_control())
+410    }
+411
+412    pub(in crate::connection) fn write_control_frames(
+413        &mut self,
+414        buf: &mut impl BufMut,
+415        pending: &mut Retransmits,
+416        retransmits: &mut ThinRetransmits,
+417        stats: &mut FrameStats,
+418        #[allow(unused)] qlog: &mut QlogSentPacket,
+419    ) {
+420        // RESET_STREAM
+421        while buf.remaining_mut() > frame::ResetStream::SIZE_BOUND {
+422            let (id, error_code) = match pending.reset_stream.pop() {
+423                Some(x) => x,
+424                None => break,
+425            };
+426            let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+427                Some(x) => x,
+428                None => continue,
+429            };
+430            trace!(stream = %id, "RESET_STREAM");
+431            retransmits
+432                .get_or_create()
+433                .reset_stream
+434                .push((id, error_code));
+435            let frame = frame::ResetStream {
+436                id,
+437                error_code,
+438                final_offset: VarInt::try_from(stream.offset()).expect("impossibly large offset"),
+439            };
+440            frame.encode(buf);
+441            qlog.frame(&Frame::ResetStream(frame));
+442            stats.reset_stream += 1;
+443        }
+444
+445        // STOP_SENDING
+446        while buf.remaining_mut() > frame::StopSending::SIZE_BOUND {
+447            let frame = match pending.stop_sending.pop() {
+448                Some(x) => x,
+449                None => break,
+450            };
+451            // We may need to transmit STOP_SENDING even for streams whose state we have discarded,
+452            // because we are able to discard local state for stopped streams immediately upon
+453            // receiving FIN, even if the peer still has arbitrarily large amounts of data to
+454            // (re)transmit due to loss or unconventional sending strategy. We could fine-tune this
+455            // a little by dropping the frame if we specifically know the stream's been reset by the
+456            // peer, but we discard that information as soon as the application consumes it, so it
+457            // can't be relied upon regardless.
+458            trace!(stream = %frame.id, "STOP_SENDING");
+459            frame.encode(buf);
+460            qlog.frame(&Frame::StopSending(frame));
+461            retransmits.get_or_create().stop_sending.push(frame);
+462            stats.stop_sending += 1;
+463        }
+464
+465        // MAX_DATA
+466        if pending.max_data && buf.remaining_mut() > 9 {
+467            pending.max_data = false;
+468
+469            // `local_max_data` can grow bigger than `VarInt`.
+470            // For transmission inside QUIC frames we need to clamp it to the
+471            // maximum allowed `VarInt` size.
+472            let max = VarInt::try_from(self.local_max_data).unwrap_or(VarInt::MAX);
+473
+474            trace!(value = max.into_inner(), "MAX_DATA");
+475            if max > self.sent_max_data {
+476                // Record that a `MAX_DATA` announcing a certain window was sent. This will
+477                // suppress enqueuing further `MAX_DATA` frames unless either the previous
+478                // transmission was not acknowledged or the window further increased.
+479                self.sent_max_data = max;
+480            }
+481
+482            retransmits.get_or_create().max_data = true;
+483            buf.write(frame::FrameType::MaxData);
+484            buf.write(max);
+485            qlog.frame(&Frame::MaxData(max));
+486            stats.max_data += 1;
+487        }
+488
+489        // MAX_STREAM_DATA
+490        while buf.remaining_mut() > 17 {
+491            let id = match pending.max_stream_data.iter().next() {
+492                Some(x) => *x,
+493                None => break,
+494            };
+495            pending.max_stream_data.remove(&id);
+496            let rs = match self
+497                .recv
+498                .get_mut(&id)
+499                .and_then(|s| s.as_mut())
+500                .and_then(|s| s.as_open_recv_mut())
+501            {
+502                Some(x) => x,
+503                None => continue,
+504            };
+505            if !rs.can_send_flow_control() {
+506                continue;
+507            }
+508            retransmits.get_or_create().max_stream_data.insert(id);
+509
+510            let (max, _) = rs.max_stream_data(self.stream_receive_window);
+511            rs.record_sent_max_stream_data(max);
+512
+513            trace!(stream = %id, max = max, "MAX_STREAM_DATA");
+514            buf.write(frame::FrameType::MaxStreamData);
+515            buf.write(id);
+516            buf.write_var(max);
+517            qlog.frame(&Frame::MaxStreamData { id, offset: max });
+518            stats.max_stream_data += 1;
+519        }
+520
+521        // MAX_STREAMS
+522        for dir in Dir::iter() {
+523            if !pending.max_stream_id[dir as usize] || buf.remaining_mut() <= 9 {
+524                continue;
+525            }
+526
+527            pending.max_stream_id[dir as usize] = false;
+528            retransmits.get_or_create().max_stream_id[dir as usize] = true;
+529            self.sent_max_remote[dir as usize] = self.max_remote[dir as usize];
+530            trace!(
+531                value = self.max_remote[dir as usize],
+532                "MAX_STREAMS ({:?})", dir
+533            );
+534            buf.write(match dir {
+535                Dir::Uni => frame::FrameType::MaxStreamsUni,
+536                Dir::Bi => frame::FrameType::MaxStreamsBidi,
+537            });
+538            let count = self.max_remote[dir as usize];
+539            buf.write_var(count);
+540            qlog.frame(&Frame::MaxStreams { dir, count });
+541            match dir {
+542                Dir::Uni => stats.max_streams_uni += 1,
+543                Dir::Bi => stats.max_streams_bidi += 1,
+544            }
+545        }
+546    }
+547
+548    pub(crate) fn write_stream_frames(
+549        &mut self,
+550        buf: &mut impl BufMut,
+551        fair: bool,
+552        #[allow(unused)] qlog: &mut QlogSentPacket,
+553    ) -> StreamMetaVec {
+554        let mut stream_frames = StreamMetaVec::new();
+555        while buf.remaining_mut() > frame::Stream::SIZE_BOUND {
+556            // Pop the stream of the highest priority that currently has pending data. If
+557            // the stream still has some pending data left after writing, it will be
+558            // reinserted, otherwise not
+559            let Some(stream) = self.pending.pop() else {
+560                break;
+561            };
+562
+563            let id = stream.id;
+564
+565            let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+566                Some(s) => s,
+567                // Stream was reset with pending data and the reset was acknowledged
+568                None => continue,
+569            };
+570
+571            // Reset streams aren't removed from the pending list and still exist while the peer
+572            // hasn't acknowledged the reset, but should not generate STREAM frames, so we need to
+573            // check for them explicitly.
+574            if stream.is_reset() {
+575                continue;
+576            }
+577
+578            // Now that we know the `StreamId`, we can better account for how many bytes
+579            // are required to encode it.
+580            let max_buf_size = buf.remaining_mut() - 1 - VarInt::size(id.into());
+581            let (offsets, encode_length) = stream.pending.poll_transmit(max_buf_size);
+582            let fin = offsets.end == stream.pending.offset()
+583                && matches!(stream.state, SendState::DataSent { .. });
+584            if fin {
+585                stream.fin_pending = false;
+586            }
+587
+588            if stream.is_pending() {
+589                // If the stream still has pending data, reinsert it, possibly with an updated priority value
+590                // Fairness with other streams is achieved by implementing round-robin scheduling,
+591                // so that the other streams will have a chance to write data
+592                // before we touch this stream again.
+593                if fair {
+594                    self.pending.push_pending(id, stream.priority);
+595                } else {
+596                    self.pending.reinsert_pending(id, stream.priority);
+597                }
+598            }
+599
+600            let meta = frame::StreamMeta { id, offsets, fin };
+601            trace!(id = %meta.id, off = meta.offsets.start, len = meta.offsets.end - meta.offsets.start, fin = meta.fin, "STREAM");
+602            meta.encoder(encode_length).encode(buf);
+603            qlog.frame_stream(&meta);
+604
+605            stream.pending.get_into(meta.offsets.clone(), buf);
+606            stream_frames.push(meta);
+607        }
+608
+609        stream_frames
+610    }
+611
+612    /// Notify the application that new streams were opened or a stream became readable.
+613    fn on_stream_frame(&mut self, notify_readable: bool, stream: StreamId) {
+614        if stream.initiator() == self.side {
+615            // Notifying about the opening of locally-initiated streams would be redundant.
+616            if notify_readable {
+617                self.events.push_back(StreamEvent::Readable { id: stream });
+618            }
+619            return;
+620        }
+621        let next = &mut self.next_remote[stream.dir() as usize];
+622        if stream.index() >= *next {
+623            *next = stream.index() + 1;
+624            self.opened[stream.dir() as usize] = true;
+625        } else if notify_readable {
+626            self.events.push_back(StreamEvent::Readable { id: stream });
+627        }
+628    }
+629
+630    pub(crate) fn received_ack_of(&mut self, frame: frame::StreamMeta) {
+631        let mut entry = match self.send.entry(frame.id) {
+632            hash_map::Entry::Vacant(_) => return,
+633            hash_map::Entry::Occupied(e) => e,
+634        };
+635
+636        let stream = match entry.get_mut().as_mut() {
+637            Some(s) => s,
+638            None => {
+639                // Because we only call this after sending data on this stream,
+640                // this closure should be unreachable. If we did somehow screw that up,
+641                // then we might hit an underflow below with unpredictable effects down
+642                // the line. Best to short-circuit.
+643                return;
+644            }
+645        };
+646
+647        if stream.is_reset() {
+648            // We account for outstanding data on reset streams at time of reset
+649            return;
+650        }
+651        let id = frame.id;
+652        self.unacked_data -= frame.offsets.end - frame.offsets.start;
+653        if !stream.ack(frame) {
+654            // The stream is unfinished or may still need retransmits
+655            return;
+656        }
+657
+658        entry.remove_entry();
+659        self.stream_freed(id, StreamHalf::Send);
+660        self.events.push_back(StreamEvent::Finished { id });
+661    }
+662
+663    pub(crate) fn retransmit(&mut self, frame: frame::StreamMeta) {
+664        let stream = match self.send.get_mut(&frame.id).and_then(|s| s.as_mut()) {
+665            // Loss of data on a closed stream is a noop
+666            None => return,
+667            Some(x) => x,
+668        };
+669        if !stream.is_pending() {
+670            self.pending.push_pending(frame.id, stream.priority);
+671        }
+672        stream.fin_pending |= frame.fin;
+673        stream.pending.retransmit(frame.offsets);
+674    }
+675
+676    pub(crate) fn retransmit_all_for_0rtt(&mut self) {
+677        for dir in Dir::iter() {
+678            for index in 0..self.next[dir as usize] {
+679                let id = StreamId::new(Side::Client, dir, index);
+680                let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+681                    Some(stream) => stream,
+682                    None => continue,
+683                };
+684                if stream.pending.is_fully_acked() && !stream.fin_pending {
+685                    // Stream data can't be acked in 0-RTT, so we must not have sent anything on
+686                    // this stream
+687                    continue;
+688                }
+689                if !stream.is_pending() {
+690                    self.pending.push_pending(id, stream.priority);
+691                }
+692                stream.pending.retransmit_all_for_0rtt();
+693            }
+694        }
+695    }
+696
+697    pub(crate) fn received_max_streams(
+698        &mut self,
+699        dir: Dir,
+700        count: u64,
+701    ) -> Result<(), TransportError> {
+702        if count > MAX_STREAM_COUNT {
+703            return Err(TransportError::FRAME_ENCODING_ERROR(
+704                "unrepresentable stream limit",
+705            ));
+706        }
+707
+708        let current = &mut self.max[dir as usize];
+709        if count > *current {
+710            *current = count;
+711            self.events.push_back(StreamEvent::Available { dir });
+712        }
+713
+714        Ok(())
+715    }
+716
+717    /// Handle increase to connection-level flow control limit
+718    pub(crate) fn received_max_data(&mut self, n: VarInt) {
+719        self.max_data = self.max_data.max(n.into());
+720    }
+721
+722    pub(crate) fn received_max_stream_data(
+723        &mut self,
+724        id: StreamId,
+725        offset: u64,
+726    ) -> Result<(), TransportError> {
+727        if id.initiator() != self.side && id.dir() == Dir::Uni {
+728            debug!("got MAX_STREAM_DATA on recv-only {}", id);
+729            return Err(TransportError::STREAM_STATE_ERROR(
+730                "MAX_STREAM_DATA on recv-only stream",
+731            ));
+732        }
+733
+734        let write_limit = self.write_limit();
+735        let max_send_data = self.max_send_data(id);
+736        if let Some(ss) = self
+737            .send
+738            .get_mut(&id)
+739            .map(get_or_insert_send(max_send_data))
+740        {
+741            if ss.increase_max_data(offset) {
+742                if write_limit > 0 {
+743                    self.events.push_back(StreamEvent::Writable { id });
+744                } else if !ss.connection_blocked {
+745                    // The stream is still blocked on the connection flow control
+746                    // window. In order to get unblocked when the window relaxes
+747                    // it needs to be in the connection blocked list.
+748                    ss.connection_blocked = true;
+749                    self.connection_blocked.push(id);
+750                }
+751            }
+752        } else if id.initiator() == self.side && self.is_local_unopened(id) {
+753            debug!("got MAX_STREAM_DATA on unopened {}", id);
+754            return Err(TransportError::STREAM_STATE_ERROR(
+755                "MAX_STREAM_DATA on unopened stream",
+756            ));
+757        }
+758
+759        self.on_stream_frame(false, id);
+760        Ok(())
+761    }
+762
+763    /// Returns the maximum amount of data this is allowed to be written on the connection
+764    pub(crate) fn write_limit(&self) -> u64 {
+765        (self.max_data - self.data_sent)
+766            // `send_window` can be set after construction to something *less* than `unacked_data`
+767            .min(self.send_window.saturating_sub(self.unacked_data))
+768    }
+769
+770    /// Yield stream events
+771    pub(crate) fn poll(&mut self) -> Option<StreamEvent> {
+772        if let Some(dir) = Dir::iter().find(|&i| mem::replace(&mut self.opened[i as usize], false))
+773        {
+774            return Some(StreamEvent::Opened { dir });
+775        }
+776
+777        if self.write_limit() > 0 {
+778            while let Some(id) = self.connection_blocked.pop() {
+779                let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+780                    None => continue,
+781                    Some(s) => s,
+782                };
+783
+784                debug_assert!(stream.connection_blocked);
+785                stream.connection_blocked = false;
+786
+787                // If it's no longer sensible to write to a stream (even to detect an error) then don't
+788                // report it.
+789                if stream.is_writable() && stream.max_data > stream.offset() {
+790                    return Some(StreamEvent::Writable { id });
+791                }
+792            }
+793        }
+794
+795        self.events.pop_front()
+796    }
+797
+798    /// Queues MAX_STREAM_ID frames in `pending` if needed
+799    ///
+800    /// Returns whether any frames were queued.
+801    pub(crate) fn queue_max_stream_id(&mut self, pending: &mut Retransmits) -> bool {
+802        let mut queued = false;
+803        for dir in Dir::iter() {
+804            let diff = self.max_remote[dir as usize] - self.sent_max_remote[dir as usize];
+805            // To reduce traffic, only announce updates if at least 1/8 of the flow control window
+806            // has been consumed.
+807            if diff > self.max_concurrent_remote_count[dir as usize] / 8 {
+808                pending.max_stream_id[dir as usize] = true;
+809                queued = true;
+810            }
+811        }
+812        queued
+813    }
+814
+815    /// Check for errors entailed by the peer's use of `id` as a send stream
+816    fn validate_receive_id(&mut self, id: StreamId) -> Result<(), TransportError> {
+817        if self.side == id.initiator() {
+818            match id.dir() {
+819                Dir::Uni => {
+820                    return Err(TransportError::STREAM_STATE_ERROR(
+821                        "illegal operation on send-only stream",
+822                    ));
+823                }
+824                Dir::Bi if id.index() >= self.next[Dir::Bi as usize] => {
+825                    return Err(TransportError::STREAM_STATE_ERROR(
+826                        "operation on unopened stream",
+827                    ));
+828                }
+829                Dir::Bi => {}
+830            };
+831        } else {
+832            let limit = self.max_remote[id.dir() as usize];
+833            if id.index() >= limit {
+834                return Err(TransportError::STREAM_LIMIT_ERROR(""));
+835            }
+836        }
+837        Ok(())
+838    }
+839
+840    /// Whether a locally initiated stream has never been open
+841    pub(crate) fn is_local_unopened(&self, id: StreamId) -> bool {
+842        id.index() >= self.next[id.dir() as usize]
+843    }
+844
+845    pub(crate) fn set_max_concurrent(&mut self, dir: Dir, count: VarInt) {
+846        self.flow_control_adjusted = true;
+847        self.max_concurrent_remote_count[dir as usize] = count.into();
+848        self.ensure_remote_streams(dir);
+849    }
+850
+851    pub(crate) fn max_concurrent(&self, dir: Dir) -> u64 {
+852        self.allocated_remote_count[dir as usize]
+853    }
+854
+855    pub(crate) fn set_send_window(&mut self, send_window: u64) {
+856        self.send_window = send_window;
+857    }
+858
+859    /// Set the receive_window and returns whether the receive_window has been
+860    /// expanded or shrunk: true if expanded, false if shrunk.
+861    pub(crate) fn set_receive_window(&mut self, receive_window: VarInt) -> bool {
+862        let receive_window = receive_window.into();
+863        let mut expanded = false;
+864        if receive_window > self.receive_window {
+865            self.local_max_data = self
+866                .local_max_data
+867                .saturating_add(receive_window - self.receive_window);
+868            expanded = true;
+869        } else {
+870            let diff = self.receive_window - receive_window;
+871            self.receive_window_shrink_debt = self.receive_window_shrink_debt.saturating_add(diff);
+872        }
+873        self.receive_window = receive_window;
+874        expanded
+875    }
+876
+877    pub(super) fn insert(&mut self, remote: bool, id: StreamId) {
+878        let bi = id.dir() == Dir::Bi;
+879        // bidirectional OR (unidirectional AND NOT remote)
+880        if bi || !remote {
+881            assert!(self.send.insert(id, None).is_none());
+882        }
+883        // bidirectional OR (unidirectional AND remote)
+884        if bi || remote {
+885            let recv = self.free_recv.pop();
+886            assert!(self.recv.insert(id, recv).is_none());
+887        }
+888    }
+889
+890    /// Adds credits to the connection flow control window
+891    ///
+892    /// Returns whether a `MAX_DATA` frame should be enqueued as soon as possible.
+893    /// This will only be the case if the window update would is significant
+894    /// enough. As soon as a window update with a `MAX_DATA` frame has been
+895    /// queued, the [`Recv::record_sent_max_stream_data`] function should be called to
+896    /// suppress sending further updates until the window increases significantly
+897    /// again.
+898    pub(super) fn add_read_credits(&mut self, credits: u64) -> ShouldTransmit {
+899        if credits > self.receive_window_shrink_debt {
+900            let net_credits = credits - self.receive_window_shrink_debt;
+901            self.local_max_data = self.local_max_data.saturating_add(net_credits);
+902            self.receive_window_shrink_debt = 0;
+903        } else {
+904            self.receive_window_shrink_debt -= credits;
+905        }
+906
+907        if self.local_max_data > VarInt::MAX.into_inner() {
+908            return ShouldTransmit(false);
+909        }
+910
+911        // Only announce a window update if it's significant enough
+912        // to make it worthwhile sending a MAX_DATA frame.
+913        // We use a fraction of the configured connection receive window to make
+914        // the decision, to accommodate for connection using bigger windows requiring
+915        // less updates.
+916        let diff = self.local_max_data - self.sent_max_data.into_inner();
+917        ShouldTransmit(diff >= (self.receive_window / 8))
+918    }
+919
+920    /// Update counters for removal of a stream
+921    pub(super) fn stream_freed(&mut self, id: StreamId, half: StreamHalf) {
+922        if id.initiator() != self.side {
+923            let fully_free = id.dir() == Dir::Uni
+924                || match half {
+925                    StreamHalf::Send => !self.recv.contains_key(&id),
+926                    StreamHalf::Recv => !self.send.contains_key(&id),
+927                };
+928            if fully_free {
+929                self.allocated_remote_count[id.dir() as usize] -= 1;
+930                self.ensure_remote_streams(id.dir());
+931            }
+932        }
+933        if half == StreamHalf::Send {
+934            self.send_streams -= 1;
+935        }
+936    }
+937
+938    pub(super) fn stream_recv_freed(&mut self, id: StreamId, recv: StreamRecv) {
+939        self.free_recv.push(recv.free(self.stream_receive_window));
+940        self.stream_freed(id, StreamHalf::Recv);
+941    }
+942
+943    pub(super) fn max_send_data(&self, id: StreamId) -> VarInt {
+944        let remote = self.side != id.initiator();
+945        match id.dir() {
+946            Dir::Uni => self.initial_max_stream_data_uni,
+947            // Remote/local appear reversed here because the transport parameters are named from
+948            // the perspective of the peer.
+949            Dir::Bi if remote => self.initial_max_stream_data_bidi_local,
+950            Dir::Bi => self.initial_max_stream_data_bidi_remote,
+951        }
+952    }
+953}
+954
+955#[inline]
+956pub(super) fn get_or_insert_send(
+957    max_data: VarInt,
+958) -> impl Fn(&mut Option<Box<Send>>) -> &mut Box<Send> {
+959    move |opt| opt.get_or_insert_with(|| Send::new(max_data))
+960}
+961
+962#[inline]
+963pub(super) fn get_or_insert_recv(
+964    initial_max_data: u64,
+965) -> impl FnMut(&mut Option<StreamRecv>) -> &mut Recv {
+966    move |opt| {
+967        *opt = opt.take().map(|s| match s {
+968            StreamRecv::Free(recv) => StreamRecv::Open(recv),
+969            s => s,
+970        });
+971        opt.get_or_insert_with(|| StreamRecv::Open(Recv::new(initial_max_data)))
+972            .as_open_recv_mut()
+973            .unwrap()
+974    }
+975}
+976
+977#[cfg(test)]
+978mod tests {
+979    use super::*;
+980    use crate::{
+981        ReadableError, RecvStream, SendStream, TransportErrorCode, WriteError,
+982        connection::State as ConnState, connection::Streams,
+983    };
+984    use bytes::Bytes;
+985
+986    fn make(side: Side) -> StreamsState {
+987        StreamsState::new(
+988            side,
+989            128u32.into(),
+990            128u32.into(),
+991            1024 * 1024,
+992            (1024 * 1024u32).into(),
+993            (1024 * 1024u32).into(),
+994        )
+995    }
+996
+997    #[test]
+998    fn trivial_flow_control() {
+999        let mut client = StreamsState::new(
+1000            Side::Client,
+1001            1u32.into(),
+1002            1u32.into(),
+1003            1024 * 1024,
+1004            (1024 * 1024u32).into(),
+1005            (1024 * 1024u32).into(),
+1006        );
+1007        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1008        let initial_max = client.local_max_data;
+1009        const MESSAGE_SIZE: usize = 2048;
+1010        assert_eq!(
+1011            client
+1012                .received(
+1013                    frame::Stream {
+1014                        id,
+1015                        offset: 0,
+1016                        fin: true,
+1017                        data: Bytes::from_static(&[0; MESSAGE_SIZE]),
+1018                    },
+1019                    2048
+1020                )
+1021                .unwrap(),
+1022            ShouldTransmit(false)
+1023        );
+1024        assert_eq!(client.data_recvd, 2048);
+1025        assert_eq!(client.local_max_data - initial_max, 0);
+1026
+1027        let mut pending = Retransmits::default();
+1028        let mut recv = RecvStream {
+1029            id,
+1030            state: &mut client,
+1031            pending: &mut pending,
+1032        };
+1033
+1034        let mut chunks = recv.read(true).unwrap();
+1035        assert_eq!(
+1036            chunks.next(MESSAGE_SIZE).unwrap().unwrap().bytes.len(),
+1037            MESSAGE_SIZE
+1038        );
+1039        assert!(chunks.next(0).unwrap().is_none());
+1040        let should_transmit = chunks.finalize();
+1041        assert!(should_transmit.0);
+1042        assert!(pending.max_stream_id[Dir::Uni as usize]);
+1043        assert_eq!(client.local_max_data - initial_max, MESSAGE_SIZE as u64);
+1044    }
+1045
+1046    #[test]
+1047    fn reset_flow_control() {
+1048        let mut client = make(Side::Client);
+1049        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1050        let initial_max = client.local_max_data;
+1051        assert_eq!(
+1052            client
+1053                .received(
+1054                    frame::Stream {
+1055                        id,
+1056                        offset: 0,
+1057                        fin: false,
+1058                        data: Bytes::from_static(&[0; 2048]),
+1059                    },
+1060                    2048
+1061                )
+1062                .unwrap(),
+1063            ShouldTransmit(false)
+1064        );
+1065        assert_eq!(client.data_recvd, 2048);
+1066        assert_eq!(client.local_max_data - initial_max, 0);
+1067
+1068        let mut pending = Retransmits::default();
+1069        let mut recv = RecvStream {
+1070            id,
+1071            state: &mut client,
+1072            pending: &mut pending,
+1073        };
+1074
+1075        let mut chunks = recv.read(true).unwrap();
+1076        chunks.next(1024).unwrap();
+1077        let _ = chunks.finalize();
+1078        assert_eq!(client.local_max_data - initial_max, 1024);
+1079        assert_eq!(
+1080            client
+1081                .received_reset(frame::ResetStream {
+1082                    id,
+1083                    error_code: 0u32.into(),
+1084                    final_offset: 4096u32.into(),
+1085                })
+1086                .unwrap(),
+1087            ShouldTransmit(false)
+1088        );
+1089
+1090        assert_eq!(client.data_recvd, 4096);
+1091        assert_eq!(client.local_max_data - initial_max, 4096);
+1092
+1093        // Ensure reading after a reset doesn't issue redundant credit
+1094        let mut recv = RecvStream {
+1095            id,
+1096            state: &mut client,
+1097            pending: &mut pending,
+1098        };
+1099        let mut chunks = recv.read(true).unwrap();
+1100        assert_eq!(
+1101            chunks.next(1024).unwrap_err(),
+1102            crate::ReadError::Reset(0u32.into())
+1103        );
+1104        let _ = chunks.finalize();
+1105        assert_eq!(client.data_recvd, 4096);
+1106        assert_eq!(client.local_max_data - initial_max, 4096);
+1107    }
+1108
+1109    #[test]
+1110    fn reset_after_empty_frame_flow_control() {
+1111        let mut client = make(Side::Client);
+1112        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1113        let initial_max = client.local_max_data;
+1114        assert_eq!(
+1115            client
+1116                .received(
+1117                    frame::Stream {
+1118                        id,
+1119                        offset: 4096,
+1120                        fin: false,
+1121                        data: Bytes::from_static(&[0; 0]),
+1122                    },
+1123                    0
+1124                )
+1125                .unwrap(),
+1126            ShouldTransmit(false)
+1127        );
+1128        assert_eq!(client.data_recvd, 4096);
+1129        assert_eq!(client.local_max_data - initial_max, 0);
+1130        assert_eq!(
+1131            client
+1132                .received_reset(frame::ResetStream {
+1133                    id,
+1134                    error_code: 0u32.into(),
+1135                    final_offset: 4096u32.into(),
+1136                })
+1137                .unwrap(),
+1138            ShouldTransmit(false)
+1139        );
+1140        assert_eq!(client.data_recvd, 4096);
+1141        assert_eq!(client.local_max_data - initial_max, 4096);
+1142    }
+1143
+1144    #[test]
+1145    fn duplicate_reset_flow_control() {
+1146        let mut client = make(Side::Client);
+1147        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1148        assert_eq!(
+1149            client
+1150                .received_reset(frame::ResetStream {
+1151                    id,
+1152                    error_code: 0u32.into(),
+1153                    final_offset: 4096u32.into(),
+1154                })
+1155                .unwrap(),
+1156            ShouldTransmit(false)
+1157        );
+1158        assert_eq!(client.data_recvd, 4096);
+1159        assert_eq!(
+1160            client
+1161                .received_reset(frame::ResetStream {
+1162                    id,
+1163                    error_code: 0u32.into(),
+1164                    final_offset: 4096u32.into(),
+1165                })
+1166                .unwrap(),
+1167            ShouldTransmit(false)
+1168        );
+1169        assert_eq!(client.data_recvd, 4096);
+1170    }
+1171
+1172    #[test]
+1173    fn recv_stopped() {
+1174        let mut client = make(Side::Client);
+1175        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1176        let initial_max = client.local_max_data;
+1177        assert_eq!(
+1178            client
+1179                .received(
+1180                    frame::Stream {
+1181                        id,
+1182                        offset: 0,
+1183                        fin: false,
+1184                        data: Bytes::from_static(&[0; 32]),
+1185                    },
+1186                    32
+1187                )
+1188                .unwrap(),
+1189            ShouldTransmit(false)
+1190        );
+1191        assert_eq!(client.local_max_data, initial_max);
+1192
+1193        let mut pending = Retransmits::default();
+1194        let mut recv = RecvStream {
+1195            id,
+1196            state: &mut client,
+1197            pending: &mut pending,
+1198        };
+1199
+1200        recv.stop(0u32.into()).unwrap();
+1201        assert_eq!(recv.pending.stop_sending.len(), 1);
+1202        assert!(!recv.pending.max_data);
+1203
+1204        assert!(recv.stop(0u32.into()).is_err());
+1205        assert_eq!(recv.read(true).err(), Some(ReadableError::ClosedStream));
+1206        assert_eq!(recv.read(false).err(), Some(ReadableError::ClosedStream));
+1207
+1208        assert_eq!(client.local_max_data - initial_max, 32);
+1209        assert_eq!(
+1210            client
+1211                .received(
+1212                    frame::Stream {
+1213                        id,
+1214                        offset: 32,
+1215                        fin: true,
+1216                        data: Bytes::from_static(&[0; 16]),
+1217                    },
+1218                    16
+1219                )
+1220                .unwrap(),
+1221            ShouldTransmit(false)
+1222        );
+1223        assert_eq!(client.local_max_data - initial_max, 48);
+1224        assert!(!client.recv.contains_key(&id));
+1225    }
+1226
+1227    #[test]
+1228    fn stopped_reset() {
+1229        let mut client = make(Side::Client);
+1230        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1231        // Server opens stream
+1232        assert_eq!(
+1233            client
+1234                .received(
+1235                    frame::Stream {
+1236                        id,
+1237                        offset: 0,
+1238                        fin: false,
+1239                        data: Bytes::from_static(&[0; 32])
+1240                    },
+1241                    32
+1242                )
+1243                .unwrap(),
+1244            ShouldTransmit(false)
+1245        );
+1246
+1247        let mut pending = Retransmits::default();
+1248        let mut recv = RecvStream {
+1249            id,
+1250            state: &mut client,
+1251            pending: &mut pending,
+1252        };
+1253
+1254        recv.stop(0u32.into()).unwrap();
+1255        assert_eq!(pending.stop_sending.len(), 1);
+1256        assert!(!pending.max_data);
+1257
+1258        // Server complies
+1259        let prev_max = client.max_remote[Dir::Uni as usize];
+1260        assert_eq!(
+1261            client
+1262                .received_reset(frame::ResetStream {
+1263                    id,
+1264                    error_code: 0u32.into(),
+1265                    final_offset: 32u32.into(),
+1266                })
+1267                .unwrap(),
+1268            ShouldTransmit(false)
+1269        );
+1270        assert!(!client.recv.contains_key(&id), "stream state is freed");
+1271        assert_eq!(client.max_remote[Dir::Uni as usize], prev_max + 1);
+1272    }
+1273
+1274    #[test]
+1275    fn send_stopped() {
+1276        let mut server = make(Side::Server);
+1277        server.set_params(&TransportParameters {
+1278            initial_max_streams_uni: 1u32.into(),
+1279            initial_max_data: 42u32.into(),
+1280            initial_max_stream_data_uni: 42u32.into(),
+1281            ..TransportParameters::default()
+1282        });
+1283
+1284        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1285        let id = Streams {
+1286            state: &mut server,
+1287            conn_state: &state,
+1288        }
+1289        .open(Dir::Uni)
+1290        .unwrap();
+1291
+1292        let mut stream = SendStream {
+1293            id,
+1294            state: &mut server,
+1295            pending: &mut pending,
+1296            conn_state: &state,
+1297        };
+1298
+1299        let error_code = 0u32.into();
+1300        stream.state.received_stop_sending(id, error_code);
+1301        assert!(
+1302            stream
+1303                .state
+1304                .events
+1305                .contains(&StreamEvent::Stopped { id, error_code })
+1306        );
+1307        stream.state.events.clear();
+1308
+1309        assert_eq!(stream.write(&[]), Err(WriteError::Stopped(error_code)));
+1310
+1311        stream.reset(0u32.into()).unwrap();
+1312        assert_eq!(stream.write(&[]), Err(WriteError::ClosedStream));
+1313
+1314        // A duplicate frame is a no-op
+1315        stream.state.received_stop_sending(id, error_code);
+1316        assert!(stream.state.events.is_empty());
+1317    }
+1318
+1319    #[test]
+1320    fn final_offset_flow_control() {
+1321        let mut client = make(Side::Client);
+1322        assert_eq!(
+1323            client
+1324                .received_reset(frame::ResetStream {
+1325                    id: StreamId::new(Side::Server, Dir::Uni, 0),
+1326                    error_code: 0u32.into(),
+1327                    final_offset: VarInt::MAX,
+1328                })
+1329                .unwrap_err()
+1330                .code,
+1331            TransportErrorCode::FLOW_CONTROL_ERROR
+1332        );
+1333    }
+1334
+1335    #[test]
+1336    fn stream_priority() {
+1337        let mut server = make(Side::Server);
+1338        server.set_params(&TransportParameters {
+1339            initial_max_streams_bidi: 3u32.into(),
+1340            initial_max_data: 10u32.into(),
+1341            initial_max_stream_data_bidi_remote: 10u32.into(),
+1342            ..TransportParameters::default()
+1343        });
+1344
+1345        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1346        let mut streams = Streams {
+1347            state: &mut server,
+1348            conn_state: &state,
+1349        };
+1350
+1351        let id_high = streams.open(Dir::Bi).unwrap();
+1352        let id_mid = streams.open(Dir::Bi).unwrap();
+1353        let id_low = streams.open(Dir::Bi).unwrap();
+1354
+1355        let mut mid = SendStream {
+1356            id: id_mid,
+1357            state: &mut server,
+1358            pending: &mut pending,
+1359            conn_state: &state,
+1360        };
+1361        mid.write(b"mid").unwrap();
+1362
+1363        let mut low = SendStream {
+1364            id: id_low,
+1365            state: &mut server,
+1366            pending: &mut pending,
+1367            conn_state: &state,
+1368        };
+1369        low.set_priority(-1).unwrap();
+1370        low.write(b"low").unwrap();
+1371
+1372        let mut high = SendStream {
+1373            id: id_high,
+1374            state: &mut server,
+1375            pending: &mut pending,
+1376            conn_state: &state,
+1377        };
+1378        high.set_priority(1).unwrap();
+1379        high.write(b"high").unwrap();
+1380
+1381        let mut buf = Vec::with_capacity(40);
+1382        let meta = server.write_stream_frames(&mut buf, true, &mut Default::default());
+1383        assert_eq!(meta[0].id, id_high);
+1384        assert_eq!(meta[1].id, id_mid);
+1385        assert_eq!(meta[2].id, id_low);
+1386
+1387        assert!(!server.can_send_stream_data());
+1388        assert_eq!(server.pending.len(), 0);
+1389    }
+1390
+1391    #[test]
+1392    fn requeue_stream_priority() {
+1393        let mut server = make(Side::Server);
+1394        server.set_params(&TransportParameters {
+1395            initial_max_streams_bidi: 3u32.into(),
+1396            initial_max_data: 1000u32.into(),
+1397            initial_max_stream_data_bidi_remote: 1000u32.into(),
+1398            ..TransportParameters::default()
+1399        });
+1400
+1401        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1402        let mut streams = Streams {
+1403            state: &mut server,
+1404            conn_state: &state,
+1405        };
+1406
+1407        let id_high = streams.open(Dir::Bi).unwrap();
+1408        let id_mid = streams.open(Dir::Bi).unwrap();
+1409
+1410        let mut mid = SendStream {
+1411            id: id_mid,
+1412            state: &mut server,
+1413            pending: &mut pending,
+1414            conn_state: &state,
+1415        };
+1416        assert_eq!(mid.write(b"mid").unwrap(), 3);
+1417        assert_eq!(server.pending.len(), 1);
+1418
+1419        let mut high = SendStream {
+1420            id: id_high,
+1421            state: &mut server,
+1422            pending: &mut pending,
+1423            conn_state: &state,
+1424        };
+1425        high.set_priority(1).unwrap();
+1426        assert_eq!(high.write(&[0; 200]).unwrap(), 200);
+1427        assert_eq!(server.pending.len(), 2);
+1428
+1429        // Requeue the high priority stream to lowest priority. The initial send
+1430        // still uses high priority since it's queued that way. After that it will
+1431        // switch to low priority
+1432        let mut high = SendStream {
+1433            id: id_high,
+1434            state: &mut server,
+1435            pending: &mut pending,
+1436            conn_state: &state,
+1437        };
+1438        high.set_priority(-1).unwrap();
+1439
+1440        let mut buf = Vec::with_capacity(1000).limit(40);
+1441        let meta = server.write_stream_frames(&mut buf, true, &mut Default::default());
+1442        assert_eq!(meta.len(), 1);
+1443        assert_eq!(meta[0].id, id_high);
+1444
+1445        // After requeuing we should end up with 2 priorities - not 3
+1446        assert_eq!(server.pending.len(), 2);
+1447
+1448        let mut buf = buf.into_inner();
+1449
+1450        // Send the remaining data. The initial mid priority one should go first now
+1451        let meta = server.write_stream_frames(&mut buf, true, &mut Default::default());
+1452        assert_eq!(meta.len(), 2);
+1453        assert_eq!(meta[0].id, id_mid);
+1454        assert_eq!(meta[1].id, id_high);
+1455
+1456        assert!(!server.can_send_stream_data());
+1457        assert_eq!(server.pending.len(), 0);
+1458    }
+1459
+1460    #[test]
+1461    fn same_stream_priority() {
+1462        for fair in [true, false] {
+1463            let mut server = make(Side::Server);
+1464            server.set_params(&TransportParameters {
+1465                initial_max_streams_bidi: 3u32.into(),
+1466                initial_max_data: 300u32.into(),
+1467                initial_max_stream_data_bidi_remote: 300u32.into(),
+1468                ..TransportParameters::default()
+1469            });
+1470
+1471            let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1472            let mut streams = Streams {
+1473                state: &mut server,
+1474                conn_state: &state,
+1475            };
+1476
+1477            // a, b and c all have the same priority
+1478            let id_a = streams.open(Dir::Bi).unwrap();
+1479            let id_b = streams.open(Dir::Bi).unwrap();
+1480            let id_c = streams.open(Dir::Bi).unwrap();
+1481
+1482            let mut stream_a = SendStream {
+1483                id: id_a,
+1484                state: &mut server,
+1485                pending: &mut pending,
+1486                conn_state: &state,
+1487            };
+1488            stream_a.write(&[b'a'; 100]).unwrap();
+1489
+1490            let mut stream_b = SendStream {
+1491                id: id_b,
+1492                state: &mut server,
+1493                pending: &mut pending,
+1494                conn_state: &state,
+1495            };
+1496            stream_b.write(&[b'b'; 100]).unwrap();
+1497
+1498            let mut stream_c = SendStream {
+1499                id: id_c,
+1500                state: &mut server,
+1501                pending: &mut pending,
+1502                conn_state: &state,
+1503            };
+1504            stream_c.write(&[b'c'; 100]).unwrap();
+1505
+1506            let mut metas = vec![];
+1507            let mut buf = Vec::with_capacity(1024);
+1508
+1509            // loop until all the streams are written
+1510            loop {
+1511                let mut chunk_buf = buf.limit(40);
+1512                let meta =
+1513                    server.write_stream_frames(&mut chunk_buf, fair, &mut Default::default());
+1514                if meta.is_empty() {
+1515                    break;
+1516                }
+1517                metas.extend(meta);
+1518                buf = chunk_buf.into_inner();
+1519            }
+1520
+1521            assert!(!server.can_send_stream_data());
+1522            assert_eq!(server.pending.len(), 0);
+1523
+1524            let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>();
+1525            if fair {
+1526                // When fairness is enabled, if we run out of buffer space to write out a stream,
+1527                // the stream is re-queued after all the streams with the same priority.
+1528                assert_eq!(
+1529                    stream_ids,
+1530                    vec![id_a, id_b, id_c, id_a, id_b, id_c, id_a, id_b, id_c]
+1531                );
+1532            } else {
+1533                // When fairness is disabled the stream is re-queued before all the other streams
+1534                // with the same priority.
+1535                assert_eq!(
+1536                    stream_ids,
+1537                    vec![id_a, id_a, id_a, id_b, id_b, id_b, id_c, id_c, id_c]
+1538                );
+1539            }
+1540        }
+1541    }
+1542
+1543    #[test]
+1544    fn unfair_priority_bump() {
+1545        let mut server = make(Side::Server);
+1546        server.set_params(&TransportParameters {
+1547            initial_max_streams_bidi: 3u32.into(),
+1548            initial_max_data: 300u32.into(),
+1549            initial_max_stream_data_bidi_remote: 300u32.into(),
+1550            ..TransportParameters::default()
+1551        });
+1552
+1553        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1554        let mut streams = Streams {
+1555            state: &mut server,
+1556            conn_state: &state,
+1557        };
+1558
+1559        // a, and b have the same priority, c has higher priority
+1560        let id_a = streams.open(Dir::Bi).unwrap();
+1561        let id_b = streams.open(Dir::Bi).unwrap();
+1562        let id_c = streams.open(Dir::Bi).unwrap();
+1563
+1564        let mut stream_a = SendStream {
+1565            id: id_a,
+1566            state: &mut server,
+1567            pending: &mut pending,
+1568            conn_state: &state,
+1569        };
+1570        stream_a.write(&[b'a'; 100]).unwrap();
+1571
+1572        let mut stream_b = SendStream {
+1573            id: id_b,
+1574            state: &mut server,
+1575            pending: &mut pending,
+1576            conn_state: &state,
+1577        };
+1578        stream_b.write(&[b'b'; 100]).unwrap();
+1579
+1580        let mut metas = vec![];
+1581        let buf = Vec::with_capacity(1024);
+1582
+1583        // Write the first chunk of stream_a
+1584        let mut chunk_buf = buf.limit(40);
+1585        let meta = server.write_stream_frames(&mut chunk_buf, false, &mut Default::default());
+1586        let mut buf = chunk_buf.into_inner();
+1587        assert!(!meta.is_empty());
+1588        metas.extend(meta);
+1589
+1590        // Queue stream_c which has higher priority
+1591        let mut stream_c = SendStream {
+1592            id: id_c,
+1593            state: &mut server,
+1594            pending: &mut pending,
+1595            conn_state: &state,
+1596        };
+1597        stream_c.set_priority(1).unwrap();
+1598        stream_c.write(&[b'b'; 100]).unwrap();
+1599
+1600        // loop until all the streams are written
+1601        loop {
+1602            let mut chunk_buf = buf.limit(40);
+1603            let meta = server.write_stream_frames(&mut chunk_buf, false, &mut Default::default());
+1604            buf = chunk_buf.into_inner();
+1605            if meta.is_empty() {
+1606                break;
+1607            }
+1608            metas.extend(meta);
+1609        }
+1610
+1611        assert!(!server.can_send_stream_data());
+1612        assert_eq!(server.pending.len(), 0);
+1613
+1614        let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>();
+1615        assert_eq!(
+1616            stream_ids,
+1617            // stream_c bumps stream_b but doesn't bump stream_a which had already been partly
+1618            // written out
+1619            vec![id_a, id_a, id_a, id_c, id_c, id_c, id_b, id_b, id_b]
+1620        );
+1621    }
+1622
+1623    #[test]
+1624    fn stop_finished() {
+1625        let mut client = make(Side::Client);
+1626        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1627        // Server finishes stream
+1628        let _ = client
+1629            .received(
+1630                frame::Stream {
+1631                    id,
+1632                    offset: 0,
+1633                    fin: true,
+1634                    data: Bytes::from_static(&[0; 32]),
+1635                },
+1636                32,
+1637            )
+1638            .unwrap();
+1639        let mut pending = Retransmits::default();
+1640        let mut stream = RecvStream {
+1641            id,
+1642            state: &mut client,
+1643            pending: &mut pending,
+1644        };
+1645        stream.stop(0u32.into()).unwrap();
+1646        assert!(client.recv.get_mut(&id).is_none(), "stream is freed");
+1647    }
+1648
+1649    // Verify that a stream that's been reset doesn't cause the appearance of pending data
+1650    #[test]
+1651    fn reset_stream_cannot_send() {
+1652        let mut server = make(Side::Server);
+1653        server.set_params(&TransportParameters {
+1654            initial_max_streams_uni: 1u32.into(),
+1655            initial_max_data: 42u32.into(),
+1656            initial_max_stream_data_uni: 42u32.into(),
+1657            ..TransportParameters::default()
+1658        });
+1659        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1660        let mut streams = Streams {
+1661            state: &mut server,
+1662            conn_state: &state,
+1663        };
+1664
+1665        let id = streams.open(Dir::Uni).unwrap();
+1666        let mut stream = SendStream {
+1667            id,
+1668            state: &mut server,
+1669            pending: &mut pending,
+1670            conn_state: &state,
+1671        };
+1672        stream.write(b"hello").unwrap();
+1673        stream.reset(0u32.into()).unwrap();
+1674
+1675        assert_eq!(pending.reset_stream, &[(id, 0u32.into())]);
+1676        assert!(!server.can_send_stream_data());
+1677    }
+1678
+1679    #[test]
+1680    fn stream_limit_fixed() {
+1681        let mut client = make(Side::Client);
+1682        // Open streams 0-127
+1683        assert_eq!(
+1684            client.received(
+1685                frame::Stream {
+1686                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1687                    offset: 0,
+1688                    fin: true,
+1689                    data: Bytes::from_static(&[]),
+1690                },
+1691                0
+1692            ),
+1693            Ok(ShouldTransmit(false))
+1694        );
+1695        // Try to open stream 128, exceeding limit
+1696        assert_eq!(
+1697            client
+1698                .received(
+1699                    frame::Stream {
+1700                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1701                        offset: 0,
+1702                        fin: true,
+1703                        data: Bytes::from_static(&[]),
+1704                    },
+1705                    0
+1706                )
+1707                .unwrap_err()
+1708                .code,
+1709            TransportErrorCode::STREAM_LIMIT_ERROR
+1710        );
+1711
+1712        // Free stream 127
+1713        let mut pending = Retransmits::default();
+1714        let mut stream = RecvStream {
+1715            id: StreamId::new(Side::Server, Dir::Uni, 127),
+1716            state: &mut client,
+1717            pending: &mut pending,
+1718        };
+1719        stream.stop(0u32.into()).unwrap();
+1720
+1721        // Open stream 128
+1722        assert_eq!(
+1723            client.received(
+1724                frame::Stream {
+1725                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1726                    offset: 0,
+1727                    fin: true,
+1728                    data: Bytes::from_static(&[]),
+1729                },
+1730                0
+1731            ),
+1732            Ok(ShouldTransmit(false))
+1733        );
+1734    }
+1735
+1736    #[test]
+1737    fn stream_limit_grows() {
+1738        let mut client = make(Side::Client);
+1739        // Open streams 0-127
+1740        assert_eq!(
+1741            client.received(
+1742                frame::Stream {
+1743                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1744                    offset: 0,
+1745                    fin: true,
+1746                    data: Bytes::from_static(&[]),
+1747                },
+1748                0
+1749            ),
+1750            Ok(ShouldTransmit(false))
+1751        );
+1752        // Try to open stream 128, exceeding limit
+1753        assert_eq!(
+1754            client
+1755                .received(
+1756                    frame::Stream {
+1757                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1758                        offset: 0,
+1759                        fin: true,
+1760                        data: Bytes::from_static(&[]),
+1761                    },
+1762                    0
+1763                )
+1764                .unwrap_err()
+1765                .code,
+1766            TransportErrorCode::STREAM_LIMIT_ERROR
+1767        );
+1768
+1769        // Relax limit by one
+1770        client.set_max_concurrent(Dir::Uni, 129u32.into());
+1771
+1772        // Open stream 128
+1773        assert_eq!(
+1774            client.received(
+1775                frame::Stream {
+1776                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1777                    offset: 0,
+1778                    fin: true,
+1779                    data: Bytes::from_static(&[]),
+1780                },
+1781                0
+1782            ),
+1783            Ok(ShouldTransmit(false))
+1784        );
+1785    }
+1786
+1787    #[test]
+1788    fn stream_limit_shrinks() {
+1789        let mut client = make(Side::Client);
+1790        // Open streams 0-127
+1791        assert_eq!(
+1792            client.received(
+1793                frame::Stream {
+1794                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1795                    offset: 0,
+1796                    fin: true,
+1797                    data: Bytes::from_static(&[]),
+1798                },
+1799                0
+1800            ),
+1801            Ok(ShouldTransmit(false))
+1802        );
+1803
+1804        // Tighten limit by one
+1805        client.set_max_concurrent(Dir::Uni, 127u32.into());
+1806
+1807        // Free stream 127
+1808        let mut pending = Retransmits::default();
+1809        let mut stream = RecvStream {
+1810            id: StreamId::new(Side::Server, Dir::Uni, 127),
+1811            state: &mut client,
+1812            pending: &mut pending,
+1813        };
+1814        stream.stop(0u32.into()).unwrap();
+1815
+1816        // Try to open stream 128, still exceeding limit
+1817        assert_eq!(
+1818            client
+1819                .received(
+1820                    frame::Stream {
+1821                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1822                        offset: 0,
+1823                        fin: true,
+1824                        data: Bytes::from_static(&[]),
+1825                    },
+1826                    0
+1827                )
+1828                .unwrap_err()
+1829                .code,
+1830            TransportErrorCode::STREAM_LIMIT_ERROR
+1831        );
+1832
+1833        // Free stream 126
+1834        assert_eq!(
+1835            client.received_reset(frame::ResetStream {
+1836                id: StreamId::new(Side::Server, Dir::Uni, 126),
+1837                error_code: 0u32.into(),
+1838                final_offset: 0u32.into(),
+1839            }),
+1840            Ok(ShouldTransmit(false))
+1841        );
+1842        let mut pending = Retransmits::default();
+1843        let mut stream = RecvStream {
+1844            id: StreamId::new(Side::Server, Dir::Uni, 126),
+1845            state: &mut client,
+1846            pending: &mut pending,
+1847        };
+1848        stream.stop(0u32.into()).unwrap();
+1849
+1850        // Open stream 128
+1851        assert_eq!(
+1852            client.received(
+1853                frame::Stream {
+1854                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1855                    offset: 0,
+1856                    fin: true,
+1857                    data: Bytes::from_static(&[]),
+1858                },
+1859                0
+1860            ),
+1861            Ok(ShouldTransmit(false))
+1862        );
+1863    }
+1864
+1865    #[test]
+1866    fn remote_stream_capacity() {
+1867        let mut client = make(Side::Client);
+1868        for _ in 0..2 {
+1869            client.set_max_concurrent(Dir::Uni, 200u32.into());
+1870            client.set_max_concurrent(Dir::Bi, 201u32.into());
+1871            assert_eq!(client.recv.len(), 200 + 201);
+1872            assert_eq!(client.max_remote[Dir::Uni as usize], 200);
+1873            assert_eq!(client.max_remote[Dir::Bi as usize], 201);
+1874        }
+1875    }
+1876
+1877    #[test]
+1878    fn expand_receive_window() {
+1879        let mut server = make(Side::Server);
+1880        let new_receive_window = 2 * server.receive_window as u32;
+1881        let expanded = server.set_receive_window(new_receive_window.into());
+1882        assert!(expanded);
+1883        assert_eq!(server.receive_window, new_receive_window as u64);
+1884        assert_eq!(server.local_max_data, new_receive_window as u64);
+1885        assert_eq!(server.receive_window_shrink_debt, 0);
+1886        let prev_local_max_data = server.local_max_data;
+1887
+1888        // credit, expecting all of them added to local_max_data
+1889        let credits = 1024u64;
+1890        let should_transmit = server.add_read_credits(credits);
+1891        assert_eq!(server.receive_window_shrink_debt, 0);
+1892        assert_eq!(server.local_max_data, prev_local_max_data + credits);
+1893        assert!(should_transmit.should_transmit());
+1894    }
+1895
+1896    #[test]
+1897    fn shrink_receive_window() {
+1898        let mut server = make(Side::Server);
+1899        let new_receive_window = server.receive_window as u32 / 2;
+1900        let prev_local_max_data = server.local_max_data;
+1901
+1902        // shrink the receive_winbow, local_max_data is not expected to be changed
+1903        let shrink_diff = server.receive_window - new_receive_window as u64;
+1904        let expanded = server.set_receive_window(new_receive_window.into());
+1905        assert!(!expanded);
+1906        assert_eq!(server.receive_window, new_receive_window as u64);
+1907        assert_eq!(server.local_max_data, prev_local_max_data);
+1908        assert_eq!(server.receive_window_shrink_debt, shrink_diff);
+1909        let prev_local_max_data = server.local_max_data;
+1910
+1911        // credit twice, local_max_data does not change as it is absorbed by receive_window_shrink_debt
+1912        let credits = 1024u64;
+1913        for _ in 0..2 {
+1914            let expected_receive_window_shrink_debt = server.receive_window_shrink_debt - credits;
+1915            let should_transmit = server.add_read_credits(credits);
+1916            assert_eq!(
+1917                server.receive_window_shrink_debt,
+1918                expected_receive_window_shrink_debt
+1919            );
+1920            assert_eq!(server.local_max_data, prev_local_max_data);
+1921            assert!(!should_transmit.should_transmit());
+1922        }
+1923
+1924        // credit again which exceeds all remaining expected_receive_window_shrink_debt
+1925        let credits = 1024 * 512;
+1926        let prev_local_max_data = server.local_max_data;
+1927        let expected_local_max_data =
+1928            server.local_max_data + (credits - server.receive_window_shrink_debt);
+1929        let _should_transmit = server.add_read_credits(credits);
+1930        assert_eq!(server.receive_window_shrink_debt, 0);
+1931        assert_eq!(server.local_max_data, expected_local_max_data);
+1932        assert!(server.local_max_data > prev_local_max_data);
+1933
+1934        // credit again, all should be added to local_max_data
+1935        let credits = 1024 * 512;
+1936        let expected_local_max_data = server.local_max_data + credits;
+1937        let should_transmit = server.add_read_credits(credits);
+1938        assert_eq!(server.receive_window_shrink_debt, 0);
+1939        assert_eq!(server.local_max_data, expected_local_max_data);
+1940        assert!(should_transmit.should_transmit());
+1941    }
+1942
+1943    #[test]
+1944    fn expand_send_window() {
+1945        let mut server = make(Side::Server);
+1946
+1947        let initial_send_window = server.send_window;
+1948        let larger_send_window = initial_send_window * 2;
+1949
+1950        // Set `initial_max_data` larger than `send_window` so we're limited by local flow control
+1951        server.set_params(&TransportParameters {
+1952            initial_max_data: VarInt::MAX,
+1953            initial_max_stream_data_uni: VarInt::MAX,
+1954            initial_max_streams_uni: VarInt::from_u32(100),
+1955            ..TransportParameters::default()
+1956        });
+1957
+1958        assert_eq!(server.write_limit(), initial_send_window);
+1959        assert_eq!(server.poll(), None);
+1960
+1961        let mut retransmits = Retransmits::default();
+1962        let conn_state = ConnState::established();
+1963
+1964        let stream_id = Streams {
+1965            state: &mut server,
+1966            conn_state: &conn_state,
+1967        }
+1968        .open(Dir::Uni)
+1969        .expect("should be able to open a stream");
+1970
+1971        let mut stream = SendStream {
+1972            id: stream_id,
+1973            state: &mut server,
+1974            pending: &mut retransmits,
+1975            conn_state: &conn_state,
+1976        };
+1977
+1978        // Check that the stream accepts `initial_send_window` bytes
+1979        let initial_send_len = initial_send_window as usize;
+1980        let data = vec![0xFFu8; initial_send_len];
+1981
+1982        assert_eq!(stream.write(&data), Ok(initial_send_len));
+1983
+1984        // Try to write the same data again, observe that it's blocked
+1985        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+1986
+1987        // Check that we get a `Writable` event after increasing the send window
+1988        stream.state.set_send_window(larger_send_window);
+1989        assert_eq!(
+1990            stream.state.poll(),
+1991            Some(StreamEvent::Writable { id: stream_id })
+1992        );
+1993
+1994        // Check that the stream accepts the exact same amount of data again
+1995        assert_eq!(stream.write(&data), Ok(initial_send_len));
+1996        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+1997
+1998        assert_eq!(stream.state.poll(), None);
+1999
+2000        // Ack the data
+2001        stream.state.received_ack_of(frame::StreamMeta {
+2002            id: stream_id,
+2003            offsets: 0..larger_send_window,
+2004            fin: false,
+2005        });
+2006
+2007        assert_eq!(
+2008            stream.state.poll(),
+2009            Some(StreamEvent::Writable { id: stream_id })
+2010        );
+2011
+2012        // Check that our full send window is available again
+2013        assert_eq!(stream.write(&data), Ok(initial_send_len));
+2014        assert_eq!(stream.write(&data), Ok(initial_send_len));
+2015        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2016    }
+2017
+2018    #[test]
+2019    fn shrink_send_window() {
+2020        let mut server = make(Side::Server);
+2021
+2022        let initial_send_window = server.send_window;
+2023        let smaller_send_window = server.send_window / 2;
+2024
+2025        // Set `initial_max_data` larger than `send_window` so we're limited by local flow control
+2026        server.set_params(&TransportParameters {
+2027            initial_max_data: VarInt::MAX,
+2028            initial_max_stream_data_uni: VarInt::MAX,
+2029            initial_max_streams_uni: VarInt::from_u32(100),
+2030            ..TransportParameters::default()
+2031        });
+2032
+2033        assert_eq!(server.write_limit(), initial_send_window);
+2034        assert_eq!(server.poll(), None);
+2035
+2036        let mut retransmits = Retransmits::default();
+2037        let conn_state = ConnState::established();
+2038
+2039        let stream_id = Streams {
+2040            state: &mut server,
+2041            conn_state: &conn_state,
+2042        }
+2043        .open(Dir::Uni)
+2044        .expect("should be able to open a stream");
+2045
+2046        let mut stream = SendStream {
+2047            id: stream_id,
+2048            state: &mut server,
+2049            pending: &mut retransmits,
+2050            conn_state: &conn_state,
+2051        };
+2052
+2053        let initial_send_len = initial_send_window as usize;
+2054
+2055        let data = vec![0xFFu8; initial_send_len];
+2056
+2057        // Assert that the full send window is accepted
+2058        assert_eq!(stream.write(&data), Ok(initial_send_len));
+2059        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2060
+2061        assert_eq!(stream.state.write_limit(), 0);
+2062        assert_eq!(stream.state.poll(), None);
+2063
+2064        // Shrink our send window, assert that it's still not writable
+2065        stream.state.set_send_window(smaller_send_window);
+2066        assert_eq!(stream.state.write_limit(), 0);
+2067        assert_eq!(stream.state.poll(), None);
+2068
+2069        // Assert that data is still not accepted
+2070        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2071
+2072        // Ack some data, assert that writes are still not accepted due to outstanding sends
+2073        stream.state.received_ack_of(frame::StreamMeta {
+2074            id: stream_id,
+2075            offsets: 0..smaller_send_window,
+2076            fin: false,
+2077        });
+2078
+2079        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2080
+2081        // Ack the rest of the data
+2082        stream.state.received_ack_of(frame::StreamMeta {
+2083            id: stream_id,
+2084            offsets: smaller_send_window..initial_send_window,
+2085            fin: false,
+2086        });
+2087
+2088        // This should generate a `Writable` event
+2089        assert_eq!(
+2090            stream.state.poll(),
+2091            Some(StreamEvent::Writable { id: stream_id })
+2092        );
+2093        assert_eq!(stream.state.write_limit(), smaller_send_window);
+2094
+2095        // Assert that only `smaller_send_window` bytes are accepted
+2096        assert_eq!(stream.write(&data), Ok(smaller_send_window as usize));
+2097        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2098    }
+2099}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/timer.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/timer.rs.html new file mode 100644 index 000000000..c55615733 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/timer.rs.html @@ -0,0 +1,496 @@ +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                if key == k {
+140                    return el.take().map(|(_, v)| v);
+141                }
+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    pub(super) fn set(&mut self, timer: Timer, time: Instant, qlog: QlogSinkWithTime<'_>) {
+254        match timer {
+255            Timer::Conn(timer) => {
+256                self.generic[timer as usize] = Some(time);
+257            }
+258            Timer::PerPath(path_id, timer) => match self.path_timers.get_mut(&path_id) {
+259                None => {
+260                    let mut table = PathTimerTable::default();
+261                    table.set(timer, time);
+262                    self.path_timers.insert(path_id, table);
+263                }
+264                Some(table) => {
+265                    table.set(timer, time);
+266                }
+267            },
+268        }
+269        qlog.emit_timer_set(timer, time);
+270    }
+271
+272    pub(super) fn set_or_stop(
+273        &mut self,
+274        timer: Timer,
+275        time: Option<Instant>,
+276        qlog: QlogSinkWithTime<'_>,
+277    ) {
+278        match time {
+279            Some(time) => self.set(timer, time, qlog),
+280            None => self.stop(timer, qlog),
+281        }
+282    }
+283
+284    pub(super) fn stop(&mut self, timer: Timer, qlog: QlogSinkWithTime<'_>) {
+285        match timer {
+286            Timer::Conn(timer) => {
+287                self.generic[timer as usize] = None;
+288            }
+289            Timer::PerPath(path_id, timer) => {
+290                if let Some(e) = self.path_timers.get_mut(&path_id) {
+291                    e.stop(timer);
+292                }
+293            }
+294        }
+295        qlog.emit_timer_stop(timer);
+296    }
+297
+298    /// Stops all per-path timers
+299    pub(super) fn stop_per_path(&mut self, path_id: PathId, qlog: QlogSinkWithTime<'_>) {
+300        for timer in PathTimer::VALUES {
+301            if let Some(e) = self.path_timers.get_mut(&path_id) {
+302                e.stop(timer);
+303                qlog.emit_timer_stop(Timer::PerPath(path_id, timer));
+304            }
+305        }
+306    }
+307
+308    /// Get the next queued timeout
+309    pub(super) fn peek(&mut self) -> Option<Instant> {
+310        // TODO: this is currently linear in the number of paths
+311
+312        let min_generic = self.generic.iter().filter_map(|&x| x).min();
+313        let min_path = self
+314            .path_timers
+315            .values()
+316            .flat_map(|p| p.timers.iter().filter_map(|&x| x))
+317            .min();
+318
+319        match (min_generic, min_path) {
+320            (None, None) => None,
+321            (Some(val), None) => Some(val),
+322            (Some(a), Some(b)) => Some(a.min(b)),
+323            (None, Some(val)) => Some(val),
+324        }
+325    }
+326
+327    /// Remove the next timer up until `now`, including it
+328    pub(super) fn expire_before(
+329        &mut self,
+330        now: Instant,
+331        qlog: &QlogSink,
+332    ) -> Option<(Timer, Instant)> {
+333        let (timer, instant) = self.expire_before_inner(now)?;
+334        qlog.with_time(now).emit_timer_expire(timer);
+335        Some((timer, instant))
+336    }
+337
+338    fn expire_before_inner(&mut self, now: Instant) -> Option<(Timer, Instant)> {
+339        // TODO: this is currently linear in the number of paths
+340
+341        for timer in ConnTimer::VALUES {
+342            if self.generic[timer as usize].is_some()
+343                && self.generic[timer as usize].expect("checked") <= now
+344            {
+345                return self.generic[timer as usize]
+346                    .take()
+347                    .map(|time| (Timer::Conn(timer), time));
+348            }
+349        }
+350
+351        let mut res = None;
+352        for (path_id, timers) in self.path_timers.iter_mut() {
+353            if let Some((timer, time)) = timers.expire_before(now) {
+354                res = Some((Timer::PerPath(*path_id, timer), time));
+355                break;
+356            }
+357        }
+358
+359        // clear out old timers
+360        self.path_timers
+361            .retain(|_path_id, timers| timers.timers.iter().any(|t| t.is_some()));
+362        res
+363    }
+364
+365    pub(super) fn reset(&mut self) {
+366        for timer in ConnTimer::VALUES {
+367            self.generic[timer as usize] = None;
+368        }
+369        self.path_timers.clear();
+370    }
+371
+372    #[cfg(test)]
+373    pub(super) fn values(&self) -> Vec<(Timer, Instant)> {
+374        let mut values = Vec::new();
+375
+376        for timer in ConnTimer::VALUES {
+377            if let Some(time) = self.generic[timer as usize] {
+378                values.push((Timer::Conn(timer), time));
+379            }
+380        }
+381
+382        for timer in PathTimer::VALUES {
+383            for (path_id, timers) in self.path_timers.iter() {
+384                if let Some(time) = timers.timers[timer as usize] {
+385                    values.push((Timer::PerPath(*path_id, timer), time));
+386                }
+387            }
+388        }
+389
+390        values
+391    }
+392}
+393
+394#[cfg(test)]
+395mod tests {
+396    use std::time::Duration;
+397
+398    use crate::connection::qlog::QlogSink;
+399
+400    use super::*;
+401
+402    #[test]
+403    fn timer_table() {
+404        let mut timers = TimerTable::default();
+405        let sec = Duration::from_secs(1);
+406        let now = Instant::now() + Duration::from_secs(10);
+407        timers.set(
+408            Timer::Conn(ConnTimer::Idle),
+409            now - 3 * sec,
+410            QlogSink::default().with_time(now),
+411        );
+412        timers.set(
+413            Timer::Conn(ConnTimer::Close),
+414            now - 2 * sec,
+415            QlogSink::default().with_time(now),
+416        );
+417
+418        assert_eq!(timers.peek(), Some(now - 3 * sec));
+419        assert_eq!(
+420            timers.expire_before(now, &QlogSink::default()),
+421            Some((Timer::Conn(ConnTimer::Idle), now - 3 * sec))
+422        );
+423        assert_eq!(
+424            timers.expire_before(now, &QlogSink::default()),
+425            Some((Timer::Conn(ConnTimer::Close), now - 2 * sec))
+426        );
+427        assert_eq!(timers.expire_before(now, &QlogSink::default()), None);
+428    }
+429
+430    #[test]
+431    fn test_small_map() {
+432        let mut map = SmallMap::<usize, usize, 2>::default();
+433
+434        // inserts only on the stack
+435        assert_eq!(map.insert(1, 1), None);
+436        assert!(map.heap.is_none());
+437        assert_eq!(map.insert(2, 2), None);
+438        assert!(map.heap.is_none());
+439
+440        // replace on the stack
+441        assert_eq!(map.insert(1, 2), Some(1));
+442
+443        assert_eq!(map.remove(&1), Some(2));
+444        assert_eq!(map.insert(3, 3), None);
+445        assert!(map.heap.is_none());
+446
+447        // spill
+448        assert_eq!(map.insert(4, 4), None);
+449        assert!(map.heap.is_some());
+450
+451        assert_eq!(
+452            map.iter()
+453                .map(|(&a, &b)| (a, b))
+454                .collect::<Vec<(usize, usize)>>(),
+455            vec![(3, 3), (2, 2), (4, 4)]
+456        );
+457        assert_eq!(
+458            map.iter()
+459                .map(|(a, b)| (*a, *b))
+460                .collect::<Vec<(usize, usize)>>(),
+461            map.iter_mut()
+462                .map(|(a, b)| (*a, *b))
+463                .collect::<Vec<(usize, usize)>>(),
+464        );
+465
+466        assert_eq!(map.heap.as_ref().unwrap().len(), 1);
+467
+468        for i in 0..10 {
+469            map.insert(10 + i, 10 + i);
+470        }
+471        assert_eq!(map.heap.as_ref().unwrap().len(), 11);
+472        map.retain(|k, _v| *k < 10);
+473
+474        assert_eq!(map.heap.as_ref().unwrap().len(), 1);
+475
+476        assert_eq!(
+477            map.iter()
+478                .map(|(&a, &b)| (a, b))
+479                .collect::<Vec<(usize, usize)>>(),
+480            vec![(3, 3), (2, 2), (4, 4)]
+481        );
+482
+483        assert_eq!(
+484            map.iter()
+485                .map(|(a, b)| (*a, *b))
+486                .collect::<Vec<(usize, usize)>>(),
+487            map.iter_mut()
+488                .map(|(a, b)| (*a, *b))
+489                .collect::<Vec<(usize, usize)>>(),
+490        );
+491
+492        map.clear();
+493        assert_eq!(map.iter().collect::<Vec<_>>(), Vec::new());
+494        assert!(map.heap.is_none());
+495    }
+496}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/connection/transmit_buf.rs.html b/pr/299/docs/src/iroh_quinn_proto/connection/transmit_buf.rs.html new file mode 100644 index 000000000..2cc42bb77 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/connection/transmit_buf.rs.html @@ -0,0 +1,244 @@ +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        Self {
+64            buf,
+65            datagram_start: 0,
+66            buf_capacity: 0,
+67            max_datagrams,
+68            num_datagrams: 0,
+69            segment_size: mtu,
+70        }
+71    }
+72
+73    pub(super) fn set_segment_size(&mut self, mtu: usize) {
+74        debug_assert!(
+75            self.datagram_start == 0 && self.buf_capacity == 0 && self.num_datagrams == 0,
+76            "can only change the segment size if nothing has been written yet"
+77        );
+78
+79        self.segment_size = mtu;
+80    }
+81
+82    /// Starts a datagram with a custom datagram size
+83    ///
+84    /// This is a specialized version of [`TransmitBuf::start_new_datagram`] which sets the
+85    /// datagram size. Useful for e.g. PATH_CHALLENGE, tail-loss probes or MTU probes.
+86    ///
+87    /// After the first datagram you can never increase the segment size. If you decrease
+88    /// the size of a datagram in a batch, it must be the last datagram of the batch.
+89    pub(super) fn start_new_datagram_with_size(&mut self, datagram_size: usize) {
+90        // Only reserve space for this datagram, usually it is the last one in the batch.
+91        let max_capacity_hint = datagram_size;
+92        self.new_datagram_inner(datagram_size, max_capacity_hint)
+93    }
+94
+95    /// Starts a new datagram in the transmit buffer
+96    ///
+97    /// If this starts the second datagram the segment size will be set to the size of the
+98    /// first datagram.
+99    ///
+100    /// If the underlying buffer does not have enough capacity yet this will allocate enough
+101    /// capacity for all the datagrams allowed in a single batch. Use
+102    /// [`TransmitBuf::start_new_datagram_with_size`] if you know you will need less.
+103    pub(super) fn start_new_datagram(&mut self) {
+104        // We reserve the maximum space for sending `max_datagrams` upfront to avoid any
+105        // reallocations if more datagrams have to be appended later on.  Benchmarks have
+106        // shown a 5-10% throughput improvement compared to continuously resizing the
+107        // datagram buffer. While this will lead to over-allocation for small transmits
+108        // (e.g. purely containing ACKs), modern memory allocators (e.g. mimalloc and
+109        // jemalloc) will pool certain allocation sizes and therefore this is still rather
+110        // efficient.
+111        let max_capacity_hint = self.max_datagrams.get() * self.segment_size;
+112        self.new_datagram_inner(self.segment_size, max_capacity_hint)
+113    }
+114
+115    fn new_datagram_inner(&mut self, datagram_size: usize, max_capacity_hint: usize) {
+116        debug_assert!(self.num_datagrams < self.max_datagrams.into());
+117        if self.num_datagrams == 1 {
+118            // Set the segment size to the size of the first datagram.
+119            self.segment_size = self.buf.len();
+120        }
+121        if self.num_datagrams >= 1 {
+122            debug_assert!(datagram_size <= self.segment_size);
+123            if datagram_size < self.segment_size {
+124                // If this is a GSO batch and this datagram is smaller than the segment
+125                // size, this must be the last datagram in the batch.
+126                self.max_datagrams = NonZeroUsize::MIN.saturating_add(self.num_datagrams);
+127            }
+128        }
+129        self.datagram_start = self.buf.len();
+130        debug_assert_eq!(
+131            self.datagram_start % self.segment_size,
+132            0,
+133            "datagrams in a GSO batch must be aligned to the segment size"
+134        );
+135        self.buf_capacity = self.datagram_start + datagram_size;
+136        if self.buf_capacity > self.buf.capacity() {
+137            self.buf
+138                .reserve_exact(max_capacity_hint.saturating_sub(self.buf.capacity()));
+139        }
+140        self.num_datagrams += 1;
+141    }
+142
+143    /// Clips the datagram size to the current size
+144    ///
+145    /// Only valid for the first datagram, when the datagram might be smaller than the
+146    /// segment size. Needed before estimating the available space in the next datagram
+147    /// based on [`TransmitBuf::segment_size`].
+148    ///
+149    /// Use [`TransmitBuf::start_new_datagram_with_size`] if you need to reduce the size of
+150    /// the last datagram in a batch.
+151    pub(super) fn clip_datagram_size(&mut self) {
+152        debug_assert_eq!(self.num_datagrams, 1);
+153        if self.buf.len() < self.segment_size {
+154            trace!(
+155                segment_size = self.buf.len(),
+156                prev_segment_size = self.segment_size,
+157                "clipped datagram size"
+158            );
+159        }
+160        self.segment_size = self.buf.len();
+161        self.buf_capacity = self.buf.len();
+162    }
+163
+164    /// Returns the GSO segment size
+165    ///
+166    /// This is also the maximum size datagrams are allowed to be. The first and last
+167    /// datagram in a batch are allowed to be smaller however. After the first datagram the
+168    /// segment size is clipped to the size of the first datagram.
+169    ///
+170    /// If the last datagram was created using [`TransmitBuf::start_new_datagram_with_size`]
+171    /// the the segment size will be greater than the current datagram is allowed to be.
+172    /// Thus [`TransmitBuf::datagram_remaining_mut`] should be used if you need to know the
+173    /// amount of data that can be written into the datagram.
+174    pub(super) fn segment_size(&self) -> usize {
+175        self.segment_size
+176    }
+177
+178    /// Returns the number of datagrams written into the buffer
+179    ///
+180    /// The last datagram is not necessarily finished yet.
+181    pub(super) fn num_datagrams(&self) -> usize {
+182        self.num_datagrams
+183    }
+184
+185    /// Returns the maximum number of datagrams allowed to be written into the buffer
+186    pub(super) fn max_datagrams(&self) -> NonZeroUsize {
+187        self.max_datagrams
+188    }
+189
+190    /// Returns the start offset of the current datagram in the buffer
+191    ///
+192    /// In other words, this offset contains the first byte of the current datagram.
+193    pub(super) fn datagram_start_offset(&self) -> usize {
+194        self.datagram_start
+195    }
+196
+197    /// Returns the maximum offset in the buffer allowed for the current datagram
+198    ///
+199    /// The first and last datagram in a batch are allowed to be smaller then the maximum
+200    /// size. All datagrams in between need to be exactly this size.
+201    pub(super) fn datagram_max_offset(&self) -> usize {
+202        self.buf_capacity
+203    }
+204
+205    /// Returns the number of bytes that may still be written into this datagram
+206    pub(super) fn datagram_remaining_mut(&self) -> usize {
+207        self.buf_capacity.saturating_sub(self.buf.len())
+208    }
+209
+210    /// Returns `true` if the buffer did not have anything written into it
+211    pub(super) fn is_empty(&self) -> bool {
+212        self.len() == 0
+213    }
+214
+215    /// The number of bytes written into the buffer so far
+216    pub(super) fn len(&self) -> usize {
+217        self.buf.len()
+218    }
+219
+220    /// Returns the already written bytes in the buffer
+221    pub(super) fn as_mut_slice(&mut self) -> &mut [u8] {
+222        self.buf.as_mut_slice()
+223    }
+224}
+225
+226unsafe impl BufMut for TransmitBuf<'_> {
+227    fn remaining_mut(&self) -> usize {
+228        self.buf.remaining_mut()
+229    }
+230
+231    unsafe fn advance_mut(&mut self, cnt: usize) {
+232        unsafe { self.buf.advance_mut(cnt) };
+233    }
+234
+235    fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
+236        self.buf.chunk_mut()
+237    }
+238}
+239
+240impl BufLen for TransmitBuf<'_> {
+241    fn len(&self) -> usize {
+242        self.len()
+243    }
+244}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/constant_time.rs.html b/pr/299/docs/src/iroh_quinn_proto/constant_time.rs.html new file mode 100644 index 000000000..1e5f93742 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/crypto.rs.html b/pr/299/docs/src/iroh_quinn_proto/crypto.rs.html new file mode 100644 index 000000000..ab9b332e2 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/crypto/ring_like.rs.html b/pr/299/docs/src/iroh_quinn_proto/crypto/ring_like.rs.html new file mode 100644 index 000000000..e4c1ba9cb --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/crypto/rustls.rs.html b/pr/299/docs/src/iroh_quinn_proto/crypto/rustls.rs.html new file mode 100644 index 000000000..406497d9e --- /dev/null +++ b/pr/299/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::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::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/299/docs/src/iroh_quinn_proto/endpoint.rs.html b/pr/299/docs/src/iroh_quinn_proto/endpoint.rs.html new file mode 100644 index 000000000..db1df6a36 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/endpoint.rs.html @@ -0,0 +1,1376 @@ +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    ///
+65    /// If `rng_seed` is provided, it will be used to initialize the endpoint's rng (having priority
+66    /// over the rng seed configured in [`EndpointConfig`]). Note that the `rng_seed` parameter will
+67    /// be removed in a future release, so prefer setting it to `None` and configuring rng seeds
+68    /// using [`EndpointConfig::rng_seed`].
+69    pub fn new(
+70        config: Arc<EndpointConfig>,
+71        server_config: Option<Arc<ServerConfig>>,
+72        allow_mtud: bool,
+73        rng_seed: Option<[u8; 32]>,
+74    ) -> Self {
+75        let rng_seed = rng_seed.or(config.rng_seed);
+76        Self {
+77            rng: rng_seed.map_or(StdRng::from_os_rng(), StdRng::from_seed),
+78            index: ConnectionIndex::default(),
+79            connections: Slab::new(),
+80            local_cid_generator: (config.connection_id_generator_factory.as_ref())(),
+81            config,
+82            server_config,
+83            allow_mtud,
+84            last_stateless_reset: None,
+85            incoming_buffers: Slab::new(),
+86            all_incoming_buffers_total_bytes: 0,
+87        }
+88    }
+89
+90    /// Replace the server configuration, affecting new incoming connections only
+91    pub fn set_server_config(&mut self, server_config: Option<Arc<ServerConfig>>) {
+92        self.server_config = server_config;
+93    }
+94
+95    /// Process `EndpointEvent`s emitted from related `Connection`s
+96    ///
+97    /// In turn, processing this event may return a `ConnectionEvent` for the same `Connection`.
+98    pub fn handle_event(
+99        &mut self,
+100        ch: ConnectionHandle,
+101        event: EndpointEvent,
+102    ) -> Option<ConnectionEvent> {
+103        use EndpointEventInner::*;
+104        match event.0 {
+105            NeedIdentifiers(path_id, now, n) => {
+106                return Some(self.send_new_identifiers(path_id, now, ch, n));
+107            }
+108            ResetToken(path_id, remote, token) => {
+109                if let Some(old) = self.connections[ch]
+110                    .reset_token
+111                    .insert(path_id, (remote, token))
+112                {
+113                    self.index.connection_reset_tokens.remove(old.0, old.1);
+114                }
+115                if self.index.connection_reset_tokens.insert(remote, token, ch) {
+116                    warn!("duplicate reset token");
+117                }
+118            }
+119            RetireResetToken(path_id) => {
+120                if let Some(old) = self.connections[ch].reset_token.remove(&path_id) {
+121                    self.index.connection_reset_tokens.remove(old.0, old.1);
+122                }
+123            }
+124            RetireConnectionId(now, path_id, seq, allow_more_cids) => {
+125                if let Some(cid) = self.connections[ch]
+126                    .loc_cids
+127                    .get_mut(&path_id)
+128                    .and_then(|pcid| pcid.cids.remove(&seq))
+129                {
+130                    trace!(%path_id, "local CID retired {}: {}", seq, cid);
+131                    self.index.retire(cid);
+132                    if allow_more_cids {
+133                        return Some(self.send_new_identifiers(path_id, now, ch, 1));
+134                    }
+135                }
+136            }
+137            Drained => {
+138                if let Some(conn) = self.connections.try_remove(ch.0) {
+139                    self.index.remove(&conn);
+140                } else {
+141                    // This indicates a bug in downstream code, which could cause spurious
+142                    // connection loss instead of this error if the CID was (re)allocated prior to
+143                    // the illegal call.
+144                    error!(id = ch.0, "unknown connection drained");
+145                }
+146            }
+147        }
+148        None
+149    }
+150
+151    /// Process an incoming UDP datagram
+152    pub fn handle(
+153        &mut self,
+154        now: Instant,
+155        network_path: FourTuple,
+156        ecn: Option<EcnCodepoint>,
+157        data: BytesMut,
+158        buf: &mut Vec<u8>,
+159    ) -> Option<DatagramEvent> {
+160        // Partially decode packet or short-circuit if unable
+161        let datagram_len = data.len();
+162        let mut event = match PartialDecode::new(
+163            data,
+164            &FixedLengthConnectionIdParser::new(self.local_cid_generator.cid_len()),
+165            &self.config.supported_versions,
+166            self.config.grease_quic_bit,
+167        ) {
+168            Ok((first_decode, remaining)) => DatagramConnectionEvent {
+169                now,
+170                network_path,
+171                path_id: PathId::ZERO, // Corrected later for existing paths
+172                ecn,
+173                first_decode,
+174                remaining,
+175            },
+176            Err(PacketDecodeError::UnsupportedVersion {
+177                src_cid,
+178                dst_cid,
+179                version,
+180            }) => {
+181                if self.server_config.is_none() {
+182                    debug!("dropping packet with unsupported version");
+183                    return None;
+184                }
+185                trace!("sending version negotiation");
+186                // Negotiate versions
+187                Header::VersionNegotiate {
+188                    random: self.rng.random::<u8>() | 0x40,
+189                    src_cid: dst_cid,
+190                    dst_cid: src_cid,
+191                }
+192                .encode(buf);
+193                // Grease with a reserved version
+194                buf.write::<u32>(match version {
+195                    0x0a1a_2a3a => 0x0a1a_2a4a,
+196                    _ => 0x0a1a_2a3a,
+197                });
+198                for &version in &self.config.supported_versions {
+199                    buf.write(version);
+200                }
+201                return Some(DatagramEvent::Response(Transmit {
+202                    destination: network_path.remote,
+203                    ecn: None,
+204                    size: buf.len(),
+205                    segment_size: None,
+206                    src_ip: network_path.local_ip,
+207                }));
+208            }
+209            Err(e) => {
+210                trace!("malformed header: {}", e);
+211                return None;
+212            }
+213        };
+214
+215        let dst_cid = event.first_decode.dst_cid();
+216
+217        if let Some(route_to) = self.index.get(&network_path, &event.first_decode) {
+218            event.path_id = match route_to {
+219                RouteDatagramTo::Incoming(_) => PathId::ZERO,
+220                RouteDatagramTo::Connection(_, path_id) => path_id,
+221            };
+222            match route_to {
+223                RouteDatagramTo::Incoming(incoming_idx) => {
+224                    let incoming_buffer = &mut self.incoming_buffers[incoming_idx];
+225                    let config = &self.server_config.as_ref().unwrap();
+226
+227                    if incoming_buffer
+228                        .total_bytes
+229                        .checked_add(datagram_len as u64)
+230                        .is_some_and(|n| n <= config.incoming_buffer_size)
+231                        && self
+232                            .all_incoming_buffers_total_bytes
+233                            .checked_add(datagram_len as u64)
+234                            .is_some_and(|n| n <= config.incoming_buffer_size_total)
+235                    {
+236                        incoming_buffer.datagrams.push(event);
+237                        incoming_buffer.total_bytes += datagram_len as u64;
+238                        self.all_incoming_buffers_total_bytes += datagram_len as u64;
+239                    }
+240
+241                    None
+242                }
+243                RouteDatagramTo::Connection(ch, _path_id) => Some(DatagramEvent::ConnectionEvent(
+244                    ch,
+245                    ConnectionEvent(ConnectionEventInner::Datagram(event)),
+246                )),
+247            }
+248        } else if event.first_decode.initial_header().is_some() {
+249            // Potentially create a new connection
+250
+251            self.handle_first_packet(datagram_len, event, network_path, buf)
+252        } else if event.first_decode.has_long_header() {
+253            debug!(
+254                "ignoring non-initial packet for unknown connection {}",
+255                dst_cid
+256            );
+257            None
+258        } else if !event.first_decode.is_initial()
+259            && self.local_cid_generator.validate(dst_cid).is_err()
+260        {
+261            debug!("dropping packet with invalid CID");
+262            None
+263        } else if dst_cid.is_empty() {
+264            trace!("dropping unrecognized short packet without ID");
+265            None
+266        } else {
+267            // If we got this far, we're receiving a seemingly valid packet for an unknown
+268            // connection. Send a stateless reset if possible.
+269            self.stateless_reset(now, datagram_len, network_path, dst_cid, buf)
+270                .map(DatagramEvent::Response)
+271        }
+272    }
+273
+274    /// Builds a stateless reset packet to respond with
+275    fn stateless_reset(
+276        &mut self,
+277        now: Instant,
+278        inciting_dgram_len: usize,
+279        network_path: FourTuple,
+280        dst_cid: ConnectionId,
+281        buf: &mut Vec<u8>,
+282    ) -> Option<Transmit> {
+283        if self
+284            .last_stateless_reset
+285            .is_some_and(|last| last + self.config.min_reset_interval > now)
+286        {
+287            debug!("ignoring unexpected packet within minimum stateless reset interval");
+288            return None;
+289        }
+290
+291        /// Minimum amount of padding for the stateless reset to look like a short-header packet
+292        const MIN_PADDING_LEN: usize = 5;
+293
+294        // Prevent amplification attacks and reset loops by ensuring we pad to at most 1 byte
+295        // smaller than the inciting packet.
+296        let max_padding_len = match inciting_dgram_len.checked_sub(RESET_TOKEN_SIZE) {
+297            Some(headroom) if headroom > MIN_PADDING_LEN => headroom - 1,
+298            _ => {
+299                debug!(
+300                    "ignoring unexpected {} byte packet: not larger than minimum stateless reset size",
+301                    inciting_dgram_len
+302                );
+303                return None;
+304            }
+305        };
+306
+307        debug!(%dst_cid, %network_path.remote, "sending stateless reset");
+308        self.last_stateless_reset = Some(now);
+309        // Resets with at least this much padding can't possibly be distinguished from real packets
+310        const IDEAL_MIN_PADDING_LEN: usize = MIN_PADDING_LEN + MAX_CID_SIZE;
+311        let padding_len = if max_padding_len <= IDEAL_MIN_PADDING_LEN {
+312            max_padding_len
+313        } else {
+314            self.rng
+315                .random_range(IDEAL_MIN_PADDING_LEN..max_padding_len)
+316        };
+317        buf.reserve(padding_len + RESET_TOKEN_SIZE);
+318        buf.resize(padding_len, 0);
+319        self.rng.fill_bytes(&mut buf[0..padding_len]);
+320        buf[0] = 0b0100_0000 | (buf[0] >> 2);
+321        buf.extend_from_slice(&ResetToken::new(&*self.config.reset_key, dst_cid));
+322
+323        debug_assert!(buf.len() < inciting_dgram_len);
+324
+325        Some(Transmit {
+326            destination: network_path.remote,
+327            ecn: None,
+328            size: buf.len(),
+329            segment_size: None,
+330            src_ip: network_path.local_ip,
+331        })
+332    }
+333
+334    /// Initiate a connection
+335    pub fn connect(
+336        &mut self,
+337        now: Instant,
+338        config: ClientConfig,
+339        remote: SocketAddr,
+340        server_name: &str,
+341    ) -> Result<(ConnectionHandle, Connection), ConnectError> {
+342        if self.cids_exhausted() {
+343            return Err(ConnectError::CidsExhausted);
+344        }
+345        if remote.port() == 0 || remote.ip().is_unspecified() {
+346            return Err(ConnectError::InvalidRemoteAddress(remote));
+347        }
+348        if !self.config.supported_versions.contains(&config.version) {
+349            return Err(ConnectError::UnsupportedVersion);
+350        }
+351
+352        let remote_id = (config.initial_dst_cid_provider)();
+353        trace!(initial_dcid = %remote_id);
+354
+355        let ch = ConnectionHandle(self.connections.vacant_key());
+356        let loc_cid = self.new_cid(ch, PathId::ZERO);
+357        let params = TransportParameters::new(
+358            &config.transport,
+359            &self.config,
+360            self.local_cid_generator.as_ref(),
+361            loc_cid,
+362            None,
+363            &mut self.rng,
+364        );
+365        let tls = config
+366            .crypto
+367            .start_session(config.version, server_name, &params)?;
+368
+369        let conn = self.add_connection(
+370            ch,
+371            config.version,
+372            remote_id,
+373            loc_cid,
+374            remote_id,
+375            FourTuple {
+376                remote,
+377                local_ip: None,
+378            },
+379            now,
+380            tls,
+381            config.transport,
+382            SideArgs::Client {
+383                token_store: config.token_store,
+384                server_name: server_name.into(),
+385            },
+386            &params,
+387        );
+388        Ok((ch, conn))
+389    }
+390
+391    /// Generates new CIDs and creates message to send to the connection state
+392    fn send_new_identifiers(
+393        &mut self,
+394        path_id: PathId,
+395        now: Instant,
+396        ch: ConnectionHandle,
+397        num: u64,
+398    ) -> ConnectionEvent {
+399        let mut ids = vec![];
+400        for _ in 0..num {
+401            let id = self.new_cid(ch, path_id);
+402            let cid_meta = self.connections[ch].loc_cids.entry(path_id).or_default();
+403            let sequence = cid_meta.issued;
+404            cid_meta.issued += 1;
+405            cid_meta.cids.insert(sequence, id);
+406            ids.push(IssuedCid {
+407                path_id,
+408                sequence,
+409                id,
+410                reset_token: ResetToken::new(&*self.config.reset_key, id),
+411            });
+412        }
+413        ConnectionEvent(ConnectionEventInner::NewIdentifiers(
+414            ids,
+415            now,
+416            self.local_cid_generator.cid_len(),
+417            self.local_cid_generator.cid_lifetime(),
+418        ))
+419    }
+420
+421    /// Generate a connection ID for `ch`
+422    fn new_cid(&mut self, ch: ConnectionHandle, path_id: PathId) -> ConnectionId {
+423        loop {
+424            let cid = self.local_cid_generator.generate_cid();
+425            if cid.is_empty() {
+426                // Zero-length CID; nothing to track
+427                debug_assert_eq!(self.local_cid_generator.cid_len(), 0);
+428                return cid;
+429            }
+430            if let hash_map::Entry::Vacant(e) = self.index.connection_ids.entry(cid) {
+431                e.insert((ch, path_id));
+432                break cid;
+433            }
+434        }
+435    }
+436
+437    fn handle_first_packet(
+438        &mut self,
+439        datagram_len: usize,
+440        event: DatagramConnectionEvent,
+441        network_path: FourTuple,
+442        buf: &mut Vec<u8>,
+443    ) -> Option<DatagramEvent> {
+444        let dst_cid = event.first_decode.dst_cid();
+445        let header = event.first_decode.initial_header().unwrap();
+446
+447        let Some(server_config) = &self.server_config else {
+448            debug!("packet for unrecognized connection {}", dst_cid);
+449            return self
+450                .stateless_reset(event.now, datagram_len, network_path, dst_cid, buf)
+451                .map(DatagramEvent::Response);
+452        };
+453
+454        if datagram_len < MIN_INITIAL_SIZE as usize {
+455            debug!("ignoring short initial for connection {}", dst_cid);
+456            return None;
+457        }
+458
+459        let crypto = match server_config.crypto.initial_keys(header.version, dst_cid) {
+460            Ok(keys) => keys,
+461            Err(UnsupportedVersion) => {
+462                // This probably indicates that the user set supported_versions incorrectly in
+463                // `EndpointConfig`.
+464                debug!(
+465                    "ignoring initial packet version {:#x} unsupported by cryptographic layer",
+466                    header.version
+467                );
+468                return None;
+469            }
+470        };
+471
+472        if let Err(reason) = self.early_validate_first_packet(header) {
+473            return Some(DatagramEvent::Response(self.initial_close(
+474                header.version,
+475                network_path,
+476                &crypto,
+477                header.src_cid,
+478                reason,
+479                buf,
+480            )));
+481        }
+482
+483        let packet = match event.first_decode.finish(Some(&*crypto.header.remote)) {
+484            Ok(packet) => packet,
+485            Err(e) => {
+486                trace!("unable to decode initial packet: {}", e);
+487                return None;
+488            }
+489        };
+490
+491        if !packet.reserved_bits_valid() {
+492            debug!("dropping connection attempt with invalid reserved bits");
+493            return None;
+494        }
+495
+496        let Header::Initial(header) = packet.header else {
+497            panic!("non-initial packet in handle_first_packet()");
+498        };
+499
+500        let server_config = self.server_config.as_ref().unwrap().clone();
+501
+502        let token = match IncomingToken::from_header(&header, &server_config, network_path.remote) {
+503            Ok(token) => token,
+504            Err(InvalidRetryTokenError) => {
+505                debug!("rejecting invalid retry token");
+506                return Some(DatagramEvent::Response(self.initial_close(
+507                    header.version,
+508                    network_path,
+509                    &crypto,
+510                    header.src_cid,
+511                    TransportError::INVALID_TOKEN(""),
+512                    buf,
+513                )));
+514            }
+515        };
+516
+517        let incoming_idx = self.incoming_buffers.insert(IncomingBuffer::default());
+518        self.index
+519            .insert_initial_incoming(header.dst_cid, incoming_idx);
+520
+521        Some(DatagramEvent::NewConnection(Incoming {
+522            received_at: event.now,
+523            network_path,
+524            ecn: event.ecn,
+525            packet: InitialPacket {
+526                header,
+527                header_data: packet.header_data,
+528                payload: packet.payload,
+529            },
+530            rest: event.remaining,
+531            crypto,
+532            token,
+533            incoming_idx,
+534            improper_drop_warner: IncomingImproperDropWarner,
+535        }))
+536    }
+537
+538    /// Attempt to accept this incoming connection (an error may still occur)
+539    // box err to avoid clippy::result_large_err
+540    pub fn accept(
+541        &mut self,
+542        mut incoming: Incoming,
+543        now: Instant,
+544        buf: &mut Vec<u8>,
+545        server_config: Option<Arc<ServerConfig>>,
+546    ) -> Result<(ConnectionHandle, Connection), Box<AcceptError>> {
+547        let remote_address_validated = incoming.remote_address_validated();
+548        incoming.improper_drop_warner.dismiss();
+549        let incoming_buffer = self.incoming_buffers.remove(incoming.incoming_idx);
+550        self.all_incoming_buffers_total_bytes -= incoming_buffer.total_bytes;
+551
+552        let packet_number = incoming.packet.header.number.expand(0);
+553        let InitialHeader {
+554            src_cid,
+555            dst_cid,
+556            version,
+557            ..
+558        } = incoming.packet.header;
+559        let server_config =
+560            server_config.unwrap_or_else(|| self.server_config.as_ref().unwrap().clone());
+561
+562        if server_config
+563            .transport
+564            .max_idle_timeout
+565            .is_some_and(|timeout| {
+566                incoming.received_at + Duration::from_millis(timeout.into()) <= now
+567            })
+568        {
+569            debug!("abandoning accept of stale initial");
+570            self.index.remove_initial(dst_cid);
+571            return Err(Box::new(AcceptError {
+572                cause: ConnectionError::TimedOut,
+573                response: None,
+574            }));
+575        }
+576
+577        if self.cids_exhausted() {
+578            debug!("refusing connection");
+579            self.index.remove_initial(dst_cid);
+580            return Err(Box::new(AcceptError {
+581                cause: ConnectionError::CidsExhausted,
+582                response: Some(self.initial_close(
+583                    version,
+584                    incoming.network_path,
+585                    &incoming.crypto,
+586                    src_cid,
+587                    TransportError::CONNECTION_REFUSED(""),
+588                    buf,
+589                )),
+590            }));
+591        }
+592
+593        if incoming
+594            .crypto
+595            .packet
+596            .remote
+597            .decrypt(
+598                PathId::ZERO,
+599                packet_number,
+600                &incoming.packet.header_data,
+601                &mut incoming.packet.payload,
+602            )
+603            .is_err()
+604        {
+605            debug!(packet_number, "failed to authenticate initial packet");
+606            self.index.remove_initial(dst_cid);
+607            return Err(Box::new(AcceptError {
+608                cause: TransportError::PROTOCOL_VIOLATION("authentication failed").into(),
+609                response: None,
+610            }));
+611        };
+612
+613        let ch = ConnectionHandle(self.connections.vacant_key());
+614        let loc_cid = self.new_cid(ch, PathId::ZERO);
+615        let mut params = TransportParameters::new(
+616            &server_config.transport,
+617            &self.config,
+618            self.local_cid_generator.as_ref(),
+619            loc_cid,
+620            Some(&server_config),
+621            &mut self.rng,
+622        );
+623        params.stateless_reset_token = Some(ResetToken::new(&*self.config.reset_key, loc_cid));
+624        params.original_dst_cid = Some(incoming.token.orig_dst_cid);
+625        params.retry_src_cid = incoming.token.retry_src_cid;
+626        let mut pref_addr_cid = None;
+627        if server_config.has_preferred_address() {
+628            let cid = self.new_cid(ch, PathId::ZERO);
+629            pref_addr_cid = Some(cid);
+630            params.preferred_address = Some(PreferredAddress {
+631                address_v4: server_config.preferred_address_v4,
+632                address_v6: server_config.preferred_address_v6,
+633                connection_id: cid,
+634                stateless_reset_token: ResetToken::new(&*self.config.reset_key, cid),
+635            });
+636        }
+637
+638        let tls = server_config.crypto.clone().start_session(version, &params);
+639        let transport_config = server_config.transport.clone();
+640        let mut conn = self.add_connection(
+641            ch,
+642            version,
+643            dst_cid,
+644            loc_cid,
+645            src_cid,
+646            incoming.network_path,
+647            incoming.received_at,
+648            tls,
+649            transport_config,
+650            SideArgs::Server {
+651                server_config,
+652                pref_addr_cid,
+653                path_validated: remote_address_validated,
+654            },
+655            &params,
+656        );
+657        self.index.insert_initial(dst_cid, ch);
+658
+659        match conn.handle_first_packet(
+660            incoming.received_at,
+661            incoming.network_path,
+662            incoming.ecn,
+663            packet_number,
+664            incoming.packet,
+665            incoming.rest,
+666        ) {
+667            Ok(()) => {
+668                trace!(id = ch.0, icid = %dst_cid, "new connection");
+669
+670                for event in incoming_buffer.datagrams {
+671                    conn.handle_event(ConnectionEvent(ConnectionEventInner::Datagram(event)))
+672                }
+673
+674                Ok((ch, conn))
+675            }
+676            Err(e) => {
+677                debug!("handshake failed: {}", e);
+678                self.handle_event(ch, EndpointEvent(EndpointEventInner::Drained));
+679                let response = match e {
+680                    ConnectionError::TransportError(ref e) => Some(self.initial_close(
+681                        version,
+682                        incoming.network_path,
+683                        &incoming.crypto,
+684                        src_cid,
+685                        e.clone(),
+686                        buf,
+687                    )),
+688                    _ => None,
+689                };
+690                Err(Box::new(AcceptError { cause: e, response }))
+691            }
+692        }
+693    }
+694
+695    /// Check if we should refuse a connection attempt regardless of the packet's contents
+696    fn early_validate_first_packet(
+697        &mut self,
+698        header: &ProtectedInitialHeader,
+699    ) -> Result<(), TransportError> {
+700        let config = &self.server_config.as_ref().unwrap();
+701        if self.cids_exhausted() || self.incoming_buffers.len() >= config.max_incoming {
+702            return Err(TransportError::CONNECTION_REFUSED(""));
+703        }
+704
+705        // RFC9000 §7.2 dictates that initial (client-chosen) destination CIDs must be at least 8
+706        // bytes. If this is a Retry packet, then the length must instead match our usual CID
+707        // length. If we ever issue non-Retry address validation tokens via `NEW_TOKEN`, then we'll
+708        // also need to validate CID length for those after decoding the token.
+709        if header.dst_cid.len() < 8
+710            && (header.token_pos.is_empty()
+711                || header.dst_cid.len() != self.local_cid_generator.cid_len())
+712        {
+713            debug!(
+714                "rejecting connection due to invalid DCID length {}",
+715                header.dst_cid.len()
+716            );
+717            return Err(TransportError::PROTOCOL_VIOLATION(
+718                "invalid destination CID length",
+719            ));
+720        }
+721
+722        Ok(())
+723    }
+724
+725    /// Reject this incoming connection attempt
+726    pub fn refuse(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Transmit {
+727        self.clean_up_incoming(&incoming);
+728        incoming.improper_drop_warner.dismiss();
+729
+730        self.initial_close(
+731            incoming.packet.header.version,
+732            incoming.network_path,
+733            &incoming.crypto,
+734            incoming.packet.header.src_cid,
+735            TransportError::CONNECTION_REFUSED(""),
+736            buf,
+737        )
+738    }
+739
+740    /// Respond with a retry packet, requiring the client to retry with address validation
+741    ///
+742    /// Errors if `incoming.may_retry()` is false.
+743    pub fn retry(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Result<Transmit, RetryError> {
+744        if !incoming.may_retry() {
+745            return Err(RetryError(Box::new(incoming)));
+746        }
+747
+748        self.clean_up_incoming(&incoming);
+749        incoming.improper_drop_warner.dismiss();
+750
+751        let server_config = self.server_config.as_ref().unwrap();
+752
+753        // First Initial
+754        // The peer will use this as the DCID of its following Initials. Initial DCIDs are
+755        // looked up separately from Handshake/Data DCIDs, so there is no risk of collision
+756        // with established connections. In the unlikely event that a collision occurs
+757        // between two connections in the initial phase, both will fail fast and may be
+758        // retried by the application layer.
+759        let loc_cid = self.local_cid_generator.generate_cid();
+760
+761        let payload = TokenPayload::Retry {
+762            address: incoming.network_path.remote,
+763            orig_dst_cid: incoming.packet.header.dst_cid,
+764            issued: server_config.time_source.now(),
+765        };
+766        let token = Token::new(payload, &mut self.rng).encode(&*server_config.token_key);
+767
+768        let header = Header::Retry {
+769            src_cid: loc_cid,
+770            dst_cid: incoming.packet.header.src_cid,
+771            version: incoming.packet.header.version,
+772        };
+773
+774        let encode = header.encode(buf);
+775        buf.put_slice(&token);
+776        buf.extend_from_slice(&server_config.crypto.retry_tag(
+777            incoming.packet.header.version,
+778            incoming.packet.header.dst_cid,
+779            buf,
+780        ));
+781        encode.finish(buf, &*incoming.crypto.header.local, None);
+782
+783        Ok(Transmit {
+784            destination: incoming.network_path.remote,
+785            ecn: None,
+786            size: buf.len(),
+787            segment_size: None,
+788            src_ip: incoming.network_path.local_ip,
+789        })
+790    }
+791
+792    /// Ignore this incoming connection attempt, not sending any packet in response
+793    ///
+794    /// Doing this actively, rather than merely dropping the [`Incoming`], is necessary to prevent
+795    /// memory leaks due to state within [`Endpoint`] tracking the incoming connection.
+796    pub fn ignore(&mut self, incoming: Incoming) {
+797        self.clean_up_incoming(&incoming);
+798        incoming.improper_drop_warner.dismiss();
+799    }
+800
+801    /// Clean up endpoint data structures associated with an `Incoming`.
+802    fn clean_up_incoming(&mut self, incoming: &Incoming) {
+803        self.index.remove_initial(incoming.packet.header.dst_cid);
+804        let incoming_buffer = self.incoming_buffers.remove(incoming.incoming_idx);
+805        self.all_incoming_buffers_total_bytes -= incoming_buffer.total_bytes;
+806    }
+807
+808    fn add_connection(
+809        &mut self,
+810        ch: ConnectionHandle,
+811        version: u32,
+812        init_cid: ConnectionId,
+813        loc_cid: ConnectionId,
+814        rem_cid: ConnectionId,
+815        network_path: FourTuple,
+816        now: Instant,
+817        tls: Box<dyn crypto::Session>,
+818        transport_config: Arc<TransportConfig>,
+819        side_args: SideArgs,
+820        // Only used for qlog.
+821        params: &TransportParameters,
+822    ) -> Connection {
+823        let mut rng_seed = [0; 32];
+824        self.rng.fill_bytes(&mut rng_seed);
+825        let side = side_args.side();
+826        let pref_addr_cid = side_args.pref_addr_cid();
+827
+828        let qlog =
+829            transport_config.create_qlog_sink(side_args.side(), network_path.remote, init_cid, now);
+830
+831        qlog.emit_connection_started(
+832            now,
+833            loc_cid,
+834            rem_cid,
+835            network_path.remote,
+836            network_path.local_ip,
+837            params,
+838        );
+839
+840        let conn = Connection::new(
+841            self.config.clone(),
+842            transport_config,
+843            init_cid,
+844            loc_cid,
+845            rem_cid,
+846            network_path,
+847            tls,
+848            self.local_cid_generator.as_ref(),
+849            now,
+850            version,
+851            self.allow_mtud,
+852            rng_seed,
+853            side_args,
+854            qlog,
+855        );
+856
+857        let mut path_cids = PathLocalCids::default();
+858        path_cids.cids.insert(path_cids.issued, loc_cid);
+859        path_cids.issued += 1;
+860
+861        if let Some(cid) = pref_addr_cid {
+862            debug_assert_eq!(path_cids.issued, 1, "preferred address cid seq must be 1");
+863            path_cids.cids.insert(path_cids.issued, cid);
+864            path_cids.issued += 1;
+865        }
+866
+867        let id = self.connections.insert(ConnectionMeta {
+868            init_cid,
+869            loc_cids: FxHashMap::from_iter([(PathId::ZERO, path_cids)]),
+870            network_path,
+871            side,
+872            reset_token: Default::default(),
+873        });
+874        debug_assert_eq!(id, ch.0, "connection handle allocation out of sync");
+875
+876        self.index.insert_conn(network_path, loc_cid, ch, side);
+877
+878        conn
+879    }
+880
+881    fn initial_close(
+882        &mut self,
+883        version: u32,
+884        network_path: FourTuple,
+885        crypto: &Keys,
+886        remote_id: ConnectionId,
+887        reason: TransportError,
+888        buf: &mut Vec<u8>,
+889    ) -> Transmit {
+890        // We don't need to worry about CID collisions in initial closes because the peer
+891        // shouldn't respond, and if it does, and the CID collides, we'll just drop the
+892        // unexpected response.
+893        let local_id = self.local_cid_generator.generate_cid();
+894        let number = PacketNumber::U8(0);
+895        let header = Header::Initial(InitialHeader {
+896            dst_cid: remote_id,
+897            src_cid: local_id,
+898            number,
+899            token: Bytes::new(),
+900            version,
+901        });
+902
+903        let partial_encode = header.encode(buf);
+904        let max_len =
+905            INITIAL_MTU as usize - partial_encode.header_len - crypto.packet.local.tag_len();
+906        frame::Close::from(reason).encoder(max_len).encode(buf);
+907        buf.resize(buf.len() + crypto.packet.local.tag_len(), 0);
+908        partial_encode.finish(
+909            buf,
+910            &*crypto.header.local,
+911            Some((0, Default::default(), &*crypto.packet.local)),
+912        );
+913        Transmit {
+914            destination: network_path.remote,
+915            ecn: None,
+916            size: buf.len(),
+917            segment_size: None,
+918            src_ip: network_path.local_ip,
+919        }
+920    }
+921
+922    /// Access the configuration used by this endpoint
+923    pub fn config(&self) -> &EndpointConfig {
+924        &self.config
+925    }
+926
+927    /// Number of connections that are currently open
+928    pub fn open_connections(&self) -> usize {
+929        self.connections.len()
+930    }
+931
+932    /// Counter for the number of bytes currently used
+933    /// in the buffers for Initial and 0-RTT messages for pending incoming connections
+934    pub fn incoming_buffer_bytes(&self) -> u64 {
+935        self.all_incoming_buffers_total_bytes
+936    }
+937
+938    #[cfg(test)]
+939    pub(crate) fn known_connections(&self) -> usize {
+940        let x = self.connections.len();
+941        debug_assert_eq!(x, self.index.connection_ids_initial.len());
+942        // Not all connections have known reset tokens
+943        debug_assert!(x >= self.index.connection_reset_tokens.0.len());
+944        // Not all connections have unique remotes, and 0-length CIDs might not be in use.
+945        debug_assert!(x >= self.index.incoming_connection_remotes.len());
+946        debug_assert!(x >= self.index.outgoing_connection_remotes.len());
+947        x
+948    }
+949
+950    #[cfg(test)]
+951    pub(crate) fn known_cids(&self) -> usize {
+952        self.index.connection_ids.len()
+953    }
+954
+955    /// Whether we've used up 3/4 of the available CID space
+956    ///
+957    /// We leave some space unused so that `new_cid` can be relied upon to finish quickly. We don't
+958    /// bother to check when CID longer than 4 bytes are used because 2^40 connections is a lot.
+959    fn cids_exhausted(&self) -> bool {
+960        self.local_cid_generator.cid_len() <= 4
+961            && self.local_cid_generator.cid_len() != 0
+962            && (2usize.pow(self.local_cid_generator.cid_len() as u32 * 8)
+963                - self.index.connection_ids.len())
+964                < 2usize.pow(self.local_cid_generator.cid_len() as u32 * 8 - 2)
+965    }
+966}
+967
+968impl fmt::Debug for Endpoint {
+969    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+970        fmt.debug_struct("Endpoint")
+971            .field("rng", &self.rng)
+972            .field("index", &self.index)
+973            .field("connections", &self.connections)
+974            .field("config", &self.config)
+975            .field("server_config", &self.server_config)
+976            // incoming_buffers too large
+977            .field("incoming_buffers.len", &self.incoming_buffers.len())
+978            .field(
+979                "all_incoming_buffers_total_bytes",
+980                &self.all_incoming_buffers_total_bytes,
+981            )
+982            .finish()
+983    }
+984}
+985
+986/// Buffered Initial and 0-RTT messages for a pending incoming connection
+987#[derive(Default)]
+988struct IncomingBuffer {
+989    datagrams: Vec<DatagramConnectionEvent>,
+990    total_bytes: u64,
+991}
+992
+993/// Part of protocol state incoming datagrams can be routed to
+994#[derive(Copy, Clone, Debug)]
+995enum RouteDatagramTo {
+996    Incoming(usize),
+997    Connection(ConnectionHandle, PathId),
+998}
+999
+1000/// Maps packets to existing connections
+1001#[derive(Default, Debug)]
+1002struct ConnectionIndex {
+1003    /// Identifies connections based on the initial DCID the peer utilized
+1004    ///
+1005    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1006    ///
+1007    /// Used by the server, not the client.
+1008    connection_ids_initial: HashMap<ConnectionId, RouteDatagramTo>,
+1009    /// Identifies connections based on locally created CIDs
+1010    ///
+1011    /// Uses a cheaper hash function since keys are locally created
+1012    connection_ids: FxHashMap<ConnectionId, (ConnectionHandle, PathId)>,
+1013    /// Identifies incoming connections with zero-length CIDs
+1014    ///
+1015    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1016    incoming_connection_remotes: HashMap<FourTuple, ConnectionHandle>,
+1017    /// Identifies outgoing connections with zero-length CIDs
+1018    ///
+1019    /// We don't yet support explicit source addresses for client connections, and zero-length CIDs
+1020    /// require a unique 4-tuple, so at most one client connection with zero-length local CIDs
+1021    /// may be established per remote. We must omit the local address from the key because we don't
+1022    /// necessarily know what address we're sending from, and hence receiving at.
+1023    ///
+1024    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1025    // TODO(matheus23): It's possible this could be changed now that we track the full 4-tuple on the client side, too.
+1026    outgoing_connection_remotes: HashMap<SocketAddr, ConnectionHandle>,
+1027    /// Reset tokens provided by the peer for the CID each connection is currently sending to
+1028    ///
+1029    /// Incoming stateless resets do not have correct CIDs, so we need this to identify the correct
+1030    /// recipient, if any.
+1031    connection_reset_tokens: ResetTokenTable,
+1032}
+1033
+1034impl ConnectionIndex {
+1035    /// Associate an incoming connection with its initial destination CID
+1036    fn insert_initial_incoming(&mut self, dst_cid: ConnectionId, incoming_key: usize) {
+1037        if dst_cid.is_empty() {
+1038            return;
+1039        }
+1040        self.connection_ids_initial
+1041            .insert(dst_cid, RouteDatagramTo::Incoming(incoming_key));
+1042    }
+1043
+1044    /// Remove an association with an initial destination CID
+1045    fn remove_initial(&mut self, dst_cid: ConnectionId) {
+1046        if dst_cid.is_empty() {
+1047            return;
+1048        }
+1049        let removed = self.connection_ids_initial.remove(&dst_cid);
+1050        debug_assert!(removed.is_some());
+1051    }
+1052
+1053    /// Associate a connection with its initial destination CID
+1054    fn insert_initial(&mut self, dst_cid: ConnectionId, connection: ConnectionHandle) {
+1055        if dst_cid.is_empty() {
+1056            return;
+1057        }
+1058        self.connection_ids_initial.insert(
+1059            dst_cid,
+1060            RouteDatagramTo::Connection(connection, PathId::ZERO),
+1061        );
+1062    }
+1063
+1064    /// Associate a connection with its first locally-chosen destination CID if used, or otherwise
+1065    /// its current 4-tuple
+1066    fn insert_conn(
+1067        &mut self,
+1068        network_path: FourTuple,
+1069        dst_cid: ConnectionId,
+1070        connection: ConnectionHandle,
+1071        side: Side,
+1072    ) {
+1073        match dst_cid.len() {
+1074            0 => match side {
+1075                Side::Server => {
+1076                    self.incoming_connection_remotes
+1077                        .insert(network_path, connection);
+1078                }
+1079                Side::Client => {
+1080                    self.outgoing_connection_remotes
+1081                        .insert(network_path.remote, connection);
+1082                }
+1083            },
+1084            _ => {
+1085                self.connection_ids
+1086                    .insert(dst_cid, (connection, PathId::ZERO));
+1087            }
+1088        }
+1089    }
+1090
+1091    /// Discard a connection ID
+1092    fn retire(&mut self, dst_cid: ConnectionId) {
+1093        self.connection_ids.remove(&dst_cid);
+1094    }
+1095
+1096    /// Remove all references to a connection
+1097    fn remove(&mut self, conn: &ConnectionMeta) {
+1098        if conn.side.is_server() {
+1099            self.remove_initial(conn.init_cid);
+1100        }
+1101        for cid in conn.loc_cids.values().flat_map(|pcids| pcids.cids.values()) {
+1102            self.connection_ids.remove(cid);
+1103        }
+1104        self.incoming_connection_remotes.remove(&conn.network_path);
+1105        self.outgoing_connection_remotes
+1106            .remove(&conn.network_path.remote);
+1107        for (remote, token) in conn.reset_token.values() {
+1108            self.connection_reset_tokens.remove(*remote, *token);
+1109        }
+1110    }
+1111
+1112    /// Find the existing connection that `datagram` should be routed to, if any
+1113    fn get(&self, network_path: &FourTuple, datagram: &PartialDecode) -> Option<RouteDatagramTo> {
+1114        if !datagram.dst_cid().is_empty() {
+1115            if let Some(&(ch, path_id)) = self.connection_ids.get(&datagram.dst_cid()) {
+1116                return Some(RouteDatagramTo::Connection(ch, path_id));
+1117            }
+1118        }
+1119        if datagram.is_initial() || datagram.is_0rtt() {
+1120            if let Some(&ch) = self.connection_ids_initial.get(&datagram.dst_cid()) {
+1121                return Some(ch);
+1122            }
+1123        }
+1124        if datagram.dst_cid().is_empty() {
+1125            if let Some(&ch) = self.incoming_connection_remotes.get(network_path) {
+1126                // Never multipath because QUIC-MULTIPATH 1.1 mandates the use of non-zero
+1127                // length CIDs.  So this is always PathId::ZERO.
+1128                return Some(RouteDatagramTo::Connection(ch, PathId::ZERO));
+1129            }
+1130            if let Some(&ch) = self.outgoing_connection_remotes.get(&network_path.remote) {
+1131                // Like above, QUIC-MULTIPATH 1.1 mandates the use of non-zero length CIDs.
+1132                return Some(RouteDatagramTo::Connection(ch, PathId::ZERO));
+1133            }
+1134        }
+1135        let data = datagram.data();
+1136        if data.len() < RESET_TOKEN_SIZE {
+1137            return None;
+1138        }
+1139        // For stateless resets the PathId is meaningless since it closes the entire
+1140        // connection regardless of path.  So use PathId::ZERO.
+1141        self.connection_reset_tokens
+1142            .get(network_path.remote, &data[data.len() - RESET_TOKEN_SIZE..])
+1143            .cloned()
+1144            .map(|ch| RouteDatagramTo::Connection(ch, PathId::ZERO))
+1145    }
+1146}
+1147
+1148#[derive(Debug)]
+1149pub(crate) struct ConnectionMeta {
+1150    init_cid: ConnectionId,
+1151    /// Locally issues CIDs for each path
+1152    loc_cids: FxHashMap<PathId, PathLocalCids>,
+1153    /// Remote/local addresses the connection began with
+1154    ///
+1155    /// Only needed to support connections with zero-length CIDs, which cannot migrate, so we don't
+1156    /// bother keeping it up to date.
+1157    network_path: FourTuple,
+1158    side: Side,
+1159    /// Reset tokens provided by the peer for CIDs we're currently sending to
+1160    ///
+1161    /// Since each reset token is for a CID, it is also for a fixed remote address which is
+1162    /// also stored. This allows us to look up which reset tokens we might expect from a
+1163    /// given remote address, see [`ResetTokenTable`].
+1164    ///
+1165    /// Each path has its own active CID. We use the [`PathId`] as a unique index, allowing
+1166    /// us to retire the reset token when a path is abandoned.
+1167    // TODO(matheus23): Should be migrated to make reset tokens per 4-tuple instead of per remote addr
+1168    reset_token: FxHashMap<PathId, (SocketAddr, ResetToken)>,
+1169}
+1170
+1171/// Local connection IDs for a single path
+1172#[derive(Debug, Default)]
+1173struct PathLocalCids {
+1174    /// Number of connection IDs that have been issued in (PATH_)NEW_CONNECTION_ID frames
+1175    ///
+1176    /// Another way of saying this is that this is the next sequence number to be issued.
+1177    issued: u64,
+1178    /// Issues CIDs indexed by their sequence number.
+1179    cids: FxHashMap<u64, ConnectionId>,
+1180}
+1181
+1182/// Internal identifier for a `Connection` currently associated with an endpoint
+1183#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
+1184pub struct ConnectionHandle(pub usize);
+1185
+1186impl From<ConnectionHandle> for usize {
+1187    fn from(x: ConnectionHandle) -> Self {
+1188        x.0
+1189    }
+1190}
+1191
+1192impl Index<ConnectionHandle> for Slab<ConnectionMeta> {
+1193    type Output = ConnectionMeta;
+1194    fn index(&self, ch: ConnectionHandle) -> &ConnectionMeta {
+1195        &self[ch.0]
+1196    }
+1197}
+1198
+1199impl IndexMut<ConnectionHandle> for Slab<ConnectionMeta> {
+1200    fn index_mut(&mut self, ch: ConnectionHandle) -> &mut ConnectionMeta {
+1201        &mut self[ch.0]
+1202    }
+1203}
+1204
+1205/// Event resulting from processing a single datagram
+1206pub enum DatagramEvent {
+1207    /// The datagram is redirected to its `Connection`
+1208    ConnectionEvent(ConnectionHandle, ConnectionEvent),
+1209    /// The datagram may result in starting a new `Connection`
+1210    NewConnection(Incoming),
+1211    /// Response generated directly by the endpoint
+1212    Response(Transmit),
+1213}
+1214
+1215/// An incoming connection for which the server has not yet begun its part of the handshake.
+1216#[derive(derive_more::Debug)]
+1217pub struct Incoming {
+1218    #[debug(skip)]
+1219    received_at: Instant,
+1220    network_path: FourTuple,
+1221    ecn: Option<EcnCodepoint>,
+1222    #[debug(skip)]
+1223    packet: InitialPacket,
+1224    #[debug(skip)]
+1225    rest: Option<BytesMut>,
+1226    #[debug(skip)]
+1227    crypto: Keys,
+1228    token: IncomingToken,
+1229    incoming_idx: usize,
+1230    #[debug(skip)]
+1231    improper_drop_warner: IncomingImproperDropWarner,
+1232}
+1233
+1234impl Incoming {
+1235    /// The local IP address which was used when the peer established the connection
+1236    ///
+1237    /// This has the same behavior as [`Connection::local_ip`].
+1238    pub fn local_ip(&self) -> Option<IpAddr> {
+1239        self.network_path.local_ip
+1240    }
+1241
+1242    /// The peer's UDP address
+1243    pub fn remote_address(&self) -> SocketAddr {
+1244        self.network_path.remote
+1245    }
+1246
+1247    /// Whether the socket address that is initiating this connection has been validated
+1248    ///
+1249    /// This means that the sender of the initial packet has proved that they can receive traffic
+1250    /// sent to `self.remote_address()`.
+1251    ///
+1252    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+1253    /// The inverse is not guaranteed.
+1254    pub fn remote_address_validated(&self) -> bool {
+1255        self.token.validated
+1256    }
+1257
+1258    /// Whether it is legal to respond with a retry packet
+1259    ///
+1260    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+1261    /// The inverse is not guaranteed.
+1262    pub fn may_retry(&self) -> bool {
+1263        self.token.retry_src_cid.is_none()
+1264    }
+1265
+1266    /// The original destination connection ID sent by the client
+1267    pub fn orig_dst_cid(&self) -> ConnectionId {
+1268        self.token.orig_dst_cid
+1269    }
+1270}
+1271
+1272struct IncomingImproperDropWarner;
+1273
+1274impl IncomingImproperDropWarner {
+1275    fn dismiss(self) {
+1276        mem::forget(self);
+1277    }
+1278}
+1279
+1280impl Drop for IncomingImproperDropWarner {
+1281    fn drop(&mut self) {
+1282        warn!(
+1283            "quinn_proto::Incoming dropped without passing to Endpoint::accept/refuse/retry/ignore \
+1284               (may cause memory leak and eventual inability to accept new connections)"
+1285        );
+1286    }
+1287}
+1288
+1289/// Errors in the parameters being used to create a new connection
+1290///
+1291/// These arise before any I/O has been performed.
+1292#[derive(Debug, Error, Clone, PartialEq, Eq)]
+1293pub enum ConnectError {
+1294    /// The endpoint can no longer create new connections
+1295    ///
+1296    /// Indicates that a necessary component of the endpoint has been dropped or otherwise disabled.
+1297    #[error("endpoint stopping")]
+1298    EndpointStopping,
+1299    /// The connection could not be created because not enough of the CID space is available
+1300    ///
+1301    /// Try using longer connection IDs
+1302    #[error("CIDs exhausted")]
+1303    CidsExhausted,
+1304    /// The given server name was malformed
+1305    #[error("invalid server name: {0}")]
+1306    InvalidServerName(String),
+1307    /// The remote [`SocketAddr`] supplied was malformed
+1308    ///
+1309    /// Examples include attempting to connect to port 0, or using an inappropriate address family.
+1310    #[error("invalid remote address: {0}")]
+1311    InvalidRemoteAddress(SocketAddr),
+1312    /// No default client configuration was set up
+1313    ///
+1314    /// Use `Endpoint::connect_with` to specify a client configuration.
+1315    #[error("no default client config")]
+1316    NoDefaultClientConfig,
+1317    /// The local endpoint does not support the QUIC version specified in the client configuration
+1318    #[error("unsupported QUIC version")]
+1319    UnsupportedVersion,
+1320}
+1321
+1322/// Error type for attempting to accept an [`Incoming`]
+1323#[derive(Debug)]
+1324pub struct AcceptError {
+1325    /// Underlying error describing reason for failure
+1326    pub cause: ConnectionError,
+1327    /// Optional response to transmit back
+1328    pub response: Option<Transmit>,
+1329}
+1330
+1331/// Error for attempting to retry an [`Incoming`] which already bears a token from a previous retry
+1332#[derive(Debug, Error)]
+1333#[error("retry() with validated Incoming")]
+1334pub struct RetryError(Box<Incoming>);
+1335
+1336impl RetryError {
+1337    /// Get the [`Incoming`]
+1338    pub fn into_incoming(self) -> Incoming {
+1339        *self.0
+1340    }
+1341}
+1342
+1343/// Reset Tokens which are associated with peer socket addresses
+1344///
+1345/// The standard `HashMap` is used since both `SocketAddr` and `ResetToken` are
+1346/// peer generated and might be usable for hash collision attacks.
+1347#[derive(Default, Debug)]
+1348struct ResetTokenTable(HashMap<SocketAddr, HashMap<ResetToken, ConnectionHandle>>);
+1349
+1350impl ResetTokenTable {
+1351    fn insert(&mut self, remote: SocketAddr, token: ResetToken, ch: ConnectionHandle) -> bool {
+1352        self.0
+1353            .entry(remote)
+1354            .or_default()
+1355            .insert(token, ch)
+1356            .is_some()
+1357    }
+1358
+1359    fn remove(&mut self, remote: SocketAddr, token: ResetToken) {
+1360        use std::collections::hash_map::Entry;
+1361        match self.0.entry(remote) {
+1362            Entry::Vacant(_) => {}
+1363            Entry::Occupied(mut e) => {
+1364                e.get_mut().remove(&token);
+1365                if e.get().is_empty() {
+1366                    e.remove_entry();
+1367                }
+1368            }
+1369        }
+1370    }
+1371
+1372    fn get(&self, remote: SocketAddr, token: &[u8]) -> Option<&ConnectionHandle> {
+1373        let token = ResetToken::from(<[u8; RESET_TOKEN_SIZE]>::try_from(token).ok()?);
+1374        self.0.get(&remote)?.get(&token)
+1375    }
+1376}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/frame.rs.html b/pr/299/docs/src/iroh_quinn_proto/frame.rs.html new file mode 100644 index 000000000..1b521222d --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/frame.rs.html @@ -0,0 +1,2291 @@ +frame.rs - source

iroh_quinn_proto/
frame.rs

1use std::{
+2    fmt::{self, Write},
+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 = "arbitrary")]
+21use arbitrary::Arbitrary;
+22
+23#[derive(
+24    Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display, enum_assoc::Assoc,
+25)]
+26#[display(rename_all = "SCREAMING_SNAKE_CASE")]
+27#[allow(missing_docs)]
+28#[func(
+29    pub(crate) const fn to_u64(self) -> u64,
+30    const fn from_u64(rev: u64) -> Option<Self>,
+31)]
+32pub enum FrameType {
+33    #[assoc(to_u64 = 0x00)]
+34    Padding,
+35    #[assoc(to_u64 = 0x01)]
+36    Ping,
+37    #[assoc(to_u64 = 0x02)]
+38    Ack,
+39    #[assoc(to_u64 = 0x03)]
+40    AckEcn,
+41    #[assoc(to_u64 = 0x04)]
+42    ResetStream,
+43    #[assoc(to_u64 = 0x05)]
+44    StopSending,
+45    #[assoc(to_u64 = 0x06)]
+46    Crypto,
+47    #[assoc(to_u64 = 0x07)]
+48    NewToken,
+49    // STREAM
+50    #[assoc(to_u64 = _0.to_u64())]
+51    Stream(StreamInfo),
+52    #[assoc(to_u64 = 0x10)]
+53    MaxData,
+54    #[assoc(to_u64 = 0x11)]
+55    MaxStreamData,
+56    #[assoc(to_u64 = 0x12)]
+57    MaxStreamsBidi,
+58    #[assoc(to_u64 = 0x13)]
+59    MaxStreamsUni,
+60    #[assoc(to_u64 = 0x14)]
+61    DataBlocked,
+62    #[assoc(to_u64 = 0x15)]
+63    StreamDataBlocked,
+64    #[assoc(to_u64 = 0x16)]
+65    StreamsBlockedBidi,
+66    #[assoc(to_u64 = 0x17)]
+67    StreamsBlockedUni,
+68    #[assoc(to_u64 = 0x18)]
+69    NewConnectionId,
+70    #[assoc(to_u64 = 0x19)]
+71    RetireConnectionId,
+72    #[assoc(to_u64 = 0x1a)]
+73    PathChallenge,
+74    #[assoc(to_u64 = 0x1b)]
+75    PathResponse,
+76    #[assoc(to_u64 = 0x1c)]
+77    ConnectionClose,
+78    #[assoc(to_u64 = 0x1d)]
+79    ApplicationClose,
+80    #[assoc(to_u64 = 0x1e)]
+81    HandshakeDone,
+82    // ACK Frequency
+83    #[assoc(to_u64 = 0xaf)]
+84    AckFrequency,
+85    #[assoc(to_u64 = 0x1f)]
+86    ImmediateAck,
+87    // DATAGRAM
+88    #[assoc(to_u64 = _0.to_u64())]
+89    Datagram(DatagramInfo),
+90    // ADDRESS DISCOVERY REPORT
+91    #[assoc(to_u64 = 0x9f81a6)]
+92    ObservedIpv4Addr,
+93    #[assoc(to_u64 = 0x9f81a7)]
+94    ObservedIpv6Addr,
+95    // Multipath
+96    #[assoc(to_u64 = 0x15228c00)]
+97    PathAck,
+98    #[assoc(to_u64 = 0x15228c01)]
+99    PathAckEcn,
+100    #[assoc(to_u64 = 0x15228c05)]
+101    PathAbandon,
+102    #[assoc(to_u64 = 0x15228c07)]
+103    PathStatusBackup,
+104    #[assoc(to_u64 = 0x15228c08)]
+105    PathStatusAvailable,
+106    #[assoc(to_u64 = 0x15228c09)]
+107    PathNewConnectionId,
+108    #[assoc(to_u64 = 0x15228c0a)]
+109    PathRetireConnectionId,
+110    #[assoc(to_u64 = 0x15228c0c)]
+111    MaxPathId,
+112    #[assoc(to_u64 = 0x15228c0d)]
+113    PathsBlocked,
+114    #[assoc(to_u64 = 0x15228c0e)]
+115    PathCidsBlocked,
+116    // IROH'S NAT TRAVERSAL
+117    #[assoc(to_u64 = 0x3d7f90)]
+118    AddIpv4Address,
+119    #[assoc(to_u64 = 0x3d7f91)]
+120    AddIpv6Address,
+121    #[assoc(to_u64 = 0x3d7f92)]
+122    ReachOutAtIpv4,
+123    #[assoc(to_u64 = 0x3d7f93)]
+124    ReachOutAtIpv6,
+125    #[assoc(to_u64 = 0x3d7f94)]
+126    RemoveAddress,
+127}
+128
+129#[derive(Debug, PartialEq, Eq)]
+130pub struct InvalidFrameId(u64);
+131
+132impl TryFrom<u64> for FrameType {
+133    type Error = InvalidFrameId;
+134    fn try_from(value: u64) -> Result<Self, Self::Error> {
+135        match Self::from_u64(value) {
+136            Some(t) => Ok(t),
+137            None => {
+138                if DatagramInfo::VALUES.contains(&value) {
+139                    return Ok(Self::Datagram(DatagramInfo(value as u8)));
+140                }
+141                if StreamInfo::VALUES.contains(&value) {
+142                    return Ok(Self::Stream(StreamInfo(value as u8)));
+143                }
+144                Err(InvalidFrameId(value))
+145            }
+146        }
+147    }
+148}
+149
+150impl FrameType {
+151    /// The encoded size of this [`FrameType`].
+152    const fn size(&self) -> usize {
+153        VarInt(self.to_u64()).size()
+154    }
+155}
+156
+157impl Decodable for FrameType {
+158    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+159        Self::try_from(buf.get_var()?).map_err(|_| coding::UnexpectedEnd)
+160    }
+161}
+162
+163impl Encodable for FrameType {
+164    fn encode<B: BufMut>(&self, buf: &mut B) {
+165        buf.write_var(self.to_u64());
+166    }
+167}
+168
+169pub(crate) trait FrameStruct {
+170    /// Smallest number of bytes this type of frame is guaranteed to fit within.
+171    const SIZE_BOUND: usize;
+172}
+173
+174/// The type used to refer to [`FrameType`]s in closing and transport errors.
+175#[derive(Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display)]
+176pub enum MaybeFrame {
+177    /// Not attributed to any particular [`FrameType`].
+178    None,
+179    /// Attributed to some frame type this implementation does not recognize.
+180    #[display("UNKNOWN{:02x}", _0)]
+181    #[debug("Unknown{:02x}", _0)]
+182    Unknown(u64),
+183    /// Attributed to a specific [`FrameType`], never [`FrameType::Padding`].
+184    Known(FrameType),
+185}
+186
+187impl MaybeFrame {
+188    /// Encoded size of this [`MaybeFrame`].
+189    const fn size(&self) -> usize {
+190        match self {
+191            Self::None => VarInt(0).size(),
+192            Self::Unknown(other) => VarInt(*other).size(),
+193            Self::Known(frame_type) => frame_type.size(),
+194        }
+195    }
+196}
+197
+198impl Decodable for MaybeFrame {
+199    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+200        match FrameType::try_from(buf.get_var()?) {
+201            Ok(FrameType::Padding) => Ok(Self::None),
+202            Ok(other_frame) => Ok(Self::Known(other_frame)),
+203            Err(InvalidFrameId(other)) => Ok(Self::Unknown(other)),
+204        }
+205    }
+206}
+207
+208impl Encodable for MaybeFrame {
+209    fn encode<B: BufMut>(&self, buf: &mut B) {
+210        match self {
+211            Self::None => buf.write(0u64),
+212            Self::Unknown(frame_id) => buf.write(*frame_id),
+213            Self::Known(frame_type) => buf.write(*frame_type),
+214        }
+215    }
+216}
+217
+218#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)]
+219#[display("STREAM")]
+220pub struct StreamInfo(u8);
+221
+222impl StreamInfo {
+223    const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x08, 0x0f);
+224    fn fin(self) -> bool {
+225        self.0 & 0x01 != 0
+226    }
+227    fn len(self) -> bool {
+228        self.0 & 0x02 != 0
+229    }
+230    fn off(self) -> bool {
+231        self.0 & 0x04 != 0
+232    }
+233
+234    const fn to_u64(self) -> u64 {
+235        self.0 as u64
+236    }
+237}
+238
+239#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)]
+240#[display("DATAGRAM")]
+241pub struct DatagramInfo(u8);
+242
+243impl DatagramInfo {
+244    const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x30, 0x31);
+245
+246    fn len(self) -> bool {
+247        self.0 & 0x01 != 0
+248    }
+249
+250    const fn to_u64(self) -> u64 {
+251        self.0 as u64
+252    }
+253}
+254
+255#[derive(Debug)]
+256pub(crate) enum Frame {
+257    Padding,
+258    Ping,
+259    Ack(Ack),
+260    PathAck(PathAck),
+261    ResetStream(ResetStream),
+262    StopSending(StopSending),
+263    Crypto(Crypto),
+264    NewToken(NewToken),
+265    Stream(Stream),
+266    MaxData(VarInt),
+267    MaxStreamData { id: StreamId, offset: u64 },
+268    MaxStreams { dir: Dir, count: u64 },
+269    DataBlocked { offset: u64 },
+270    StreamDataBlocked { id: StreamId, offset: u64 },
+271    StreamsBlocked { dir: Dir, limit: u64 },
+272    NewConnectionId(NewConnectionId),
+273    RetireConnectionId(RetireConnectionId),
+274    PathChallenge(PathChallenge),
+275    PathResponse(PathResponse),
+276    Close(Close),
+277    Datagram(Datagram),
+278    AckFrequency(AckFrequency),
+279    ImmediateAck,
+280    HandshakeDone,
+281    ObservedAddr(ObservedAddr),
+282    PathAbandon(PathAbandon),
+283    PathStatusAvailable(PathStatusAvailable),
+284    PathStatusBackup(PathStatusBackup),
+285    MaxPathId(MaxPathId),
+286    PathsBlocked(PathsBlocked),
+287    PathCidsBlocked(PathCidsBlocked),
+288    AddAddress(AddAddress),
+289    ReachOut(ReachOut),
+290    RemoveAddress(RemoveAddress),
+291}
+292
+293impl fmt::Display for Frame {
+294    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+295        // Eventually all our frames will support fmt::Display and be able to be used to log
+296        // consistently. For now we fall back to fmt::Debug.
+297        match self {
+298            Self::Padding => write!(f, "PADDING"),
+299            Self::Ping => write!(f, "PING"),
+300            Self::PathChallenge(frame) => write!(f, "{frame}"),
+301            Self::PathResponse(frame) => write!(f, "{frame}"),
+302            Self::ImmediateAck => write!(f, "IMMEDIATE_ACK"),
+303            Self::HandshakeDone => write!(f, "HANDSHAKE_DONE"),
+304            _ => write!(f, "{self:?}"),
+305        }
+306    }
+307}
+308
+309impl Frame {
+310    pub(crate) fn ty(&self) -> FrameType {
+311        use Frame::*;
+312        match *self {
+313            Padding => FrameType::Padding,
+314            ResetStream(_) => FrameType::ResetStream,
+315            Close(self::Close::Connection(_)) => FrameType::ConnectionClose,
+316            Close(self::Close::Application(_)) => FrameType::ConnectionClose,
+317            MaxData(_) => FrameType::MaxData,
+318            MaxStreamData { .. } => FrameType::MaxStreamData,
+319            MaxStreams { dir: Dir::Bi, .. } => FrameType::MaxStreamsBidi,
+320            MaxStreams { dir: Dir::Uni, .. } => FrameType::MaxStreamsUni,
+321            Ping => FrameType::Ping,
+322            DataBlocked { .. } => FrameType::DataBlocked,
+323            StreamDataBlocked { .. } => FrameType::StreamDataBlocked,
+324            StreamsBlocked { dir: Dir::Bi, .. } => FrameType::StreamsBlockedBidi,
+325            StreamsBlocked { dir: Dir::Uni, .. } => FrameType::StreamsBlockedUni,
+326            StopSending { .. } => FrameType::StopSending,
+327            RetireConnectionId { .. } => FrameType::RetireConnectionId,
+328            Ack(_) => FrameType::Ack,
+329            PathAck(_) => FrameType::PathAck,
+330            Stream(ref x) => {
+331                let mut ty = *StreamInfo::VALUES.start() as u8;
+332                if x.fin {
+333                    ty |= 0x01;
+334                }
+335                if x.offset != 0 {
+336                    ty |= 0x04;
+337                }
+338                // TODO(@divma): move all this to getframetype for Stream
+339                FrameType::Stream(StreamInfo(ty))
+340            }
+341            PathChallenge(_) => FrameType::PathChallenge,
+342            PathResponse(_) => FrameType::PathResponse,
+343            NewConnectionId(cid) => cid.get_type(),
+344            Crypto(_) => FrameType::Crypto,
+345            NewToken(_) => FrameType::NewToken,
+346            Datagram(_) => FrameType::Datagram(DatagramInfo(*DatagramInfo::VALUES.start() as u8)),
+347            AckFrequency(_) => FrameType::AckFrequency,
+348            ImmediateAck => FrameType::ImmediateAck,
+349            HandshakeDone => FrameType::HandshakeDone,
+350            ObservedAddr(ref observed) => observed.get_type(),
+351            PathAbandon(_) => FrameType::PathAbandon,
+352            PathStatusAvailable(_) => FrameType::PathStatusAvailable,
+353            PathStatusBackup(_) => FrameType::PathStatusBackup,
+354            MaxPathId(_) => FrameType::MaxPathId,
+355            PathsBlocked(_) => FrameType::PathsBlocked,
+356            PathCidsBlocked(_) => FrameType::PathCidsBlocked,
+357            AddAddress(ref frame) => frame.get_type(),
+358            ReachOut(ref frame) => frame.get_type(),
+359            RemoveAddress(_) => self::RemoveAddress::TYPE,
+360        }
+361    }
+362
+363    pub(crate) fn is_ack_eliciting(&self) -> bool {
+364        !matches!(
+365            *self,
+366            Self::Ack(_) | Self::PathAck(_) | Self::Padding | Self::Close(_)
+367        )
+368    }
+369
+370    /// Returns `true` if this frame MUST be sent in 1-RTT space
+371    pub(crate) fn is_1rtt(&self) -> bool {
+372        // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-4-1:
+373        // > All frames defined in this document MUST only be sent in 1-RTT packets.
+374        // > If an endpoint receives a multipath-specific frame in a different packet type, it MUST close the
+375        // > connection with an error of type PROTOCOL_VIOLATION.
+376
+377        self.is_multipath_frame() || self.is_qad_frame()
+378    }
+379
+380    fn is_qad_frame(&self) -> bool {
+381        matches!(*self, Self::ObservedAddr(_))
+382    }
+383
+384    fn is_multipath_frame(&self) -> bool {
+385        matches!(
+386            *self,
+387            Self::PathAck(_)
+388                | Self::PathAbandon(_)
+389                | Self::PathStatusBackup(_)
+390                | Self::PathStatusAvailable(_)
+391                | Self::MaxPathId(_)
+392                | Self::PathsBlocked(_)
+393                | Self::PathCidsBlocked(_)
+394                | Self::NewConnectionId(NewConnectionId {
+395                    path_id: Some(_),
+396                    ..
+397                })
+398                | Self::RetireConnectionId(RetireConnectionId {
+399                    path_id: Some(_),
+400                    ..
+401                })
+402        )
+403    }
+404}
+405
+406#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display)]
+407#[display("PATH_CHALLENGE({_0:08x})")]
+408pub(crate) struct PathChallenge(pub(crate) u64);
+409
+410impl PathChallenge {
+411    pub(crate) const SIZE_BOUND: usize = 9;
+412}
+413impl Decodable for PathChallenge {
+414    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+415        Ok(Self(buf.get()?))
+416    }
+417}
+418impl Encodable for PathChallenge {
+419    fn encode<B: BufMut>(&self, buf: &mut B) {
+420        buf.write(FrameType::PathChallenge);
+421        buf.write(self.0);
+422    }
+423}
+424
+425#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Display)]
+426#[display("PATH_RESPONSE({_0:08x})")]
+427pub(crate) struct PathResponse(pub(crate) u64);
+428
+429impl PathResponse {
+430    pub(crate) const SIZE_BOUND: usize = 9;
+431}
+432
+433impl Decodable for PathResponse {
+434    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+435        Ok(Self(buf.get()?))
+436    }
+437}
+438impl Encodable for PathResponse {
+439    fn encode<B: BufMut>(&self, buf: &mut B) {
+440        buf.write(FrameType::PathResponse);
+441        buf.write(self.0);
+442    }
+443}
+444
+445#[derive(Debug, PartialEq, Eq)]
+446pub(crate) struct RetireConnectionId {
+447    pub(crate) path_id: Option<PathId>,
+448    pub(crate) sequence: u64,
+449}
+450
+451impl RetireConnectionId {
+452    /// Maximum size of this frame when the frame type is [`FrameType::RetireConnectionId`]
+453    pub(crate) const SIZE_BOUND: usize = {
+454        let type_len = FrameType::RetireConnectionId.size();
+455        let seq_max_len = 8usize;
+456        type_len + seq_max_len
+457    };
+458
+459    /// Maximum size of this frame when the frame type is [`FrameType::PathRetireConnectionId`]
+460    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
+461        let type_len = FrameType::PathRetireConnectionId.size();
+462        let path_id_len = VarInt::from_u32(u32::MAX).size();
+463        let seq_max_len = 8usize;
+464        type_len + path_id_len + seq_max_len
+465    };
+466
+467    /// Decode [`Self`] from the buffer, provided that the frame type has been verified (either
+468    /// [`FrameType::PathRetireConnectionId`], or [`FrameType::RetireConnectionId`])
+469    pub(crate) fn decode<R: Buf>(bytes: &mut R, read_path: bool) -> coding::Result<Self> {
+470        Ok(Self {
+471            path_id: if read_path { Some(bytes.get()?) } else { None },
+472            sequence: bytes.get_var()?,
+473        })
+474    }
+475
+476    /// Get the [`FrameType`] for this [`RetireConnectionId`]
+477    pub(crate) fn get_type(&self) -> FrameType {
+478        if self.path_id.is_some() {
+479            FrameType::PathRetireConnectionId
+480        } else {
+481            FrameType::RetireConnectionId
+482        }
+483    }
+484
+485    /// Returns the maximum encoded size on the wire
+486    ///
+487    /// `path_retire_cid` determines whether this frame is a multipath frame. This is a rough upper
+488    /// estimate, does not squeeze every last byte out.
+489    pub(crate) const fn size_bound(path_retire_cid: bool) -> usize {
+490        match path_retire_cid {
+491            true => Self::SIZE_BOUND_MULTIPATH,
+492            false => Self::SIZE_BOUND,
+493        }
+494    }
+495}
+496
+497impl Encodable for RetireConnectionId {
+498    fn encode<W: BufMut>(&self, buf: &mut W) {
+499        buf.write(self.get_type());
+500        if let Some(id) = self.path_id {
+501            buf.write(id);
+502        }
+503        buf.write_var(self.sequence);
+504    }
+505}
+506
+507#[derive(Clone, Debug)]
+508pub enum Close {
+509    Connection(ConnectionClose),
+510    Application(ApplicationClose),
+511}
+512
+513impl Close {
+514    pub(crate) fn encoder(&self, max_len: usize) -> CloseEncoder<'_> {
+515        CloseEncoder {
+516            close: self,
+517            max_len,
+518        }
+519    }
+520
+521    pub(crate) fn is_transport_layer(&self) -> bool {
+522        matches!(*self, Self::Connection(_))
+523    }
+524}
+525
+526pub(crate) struct CloseEncoder<'a> {
+527    close: &'a Close,
+528    max_len: usize,
+529}
+530
+531impl<'a> Encodable for CloseEncoder<'a> {
+532    fn encode<W: BufMut>(&self, out: &mut W) {
+533        match self.close {
+534            Close::Connection(x) => x.encode(out, self.max_len),
+535            Close::Application(x) => x.encode(out, self.max_len),
+536        }
+537    }
+538}
+539
+540impl From<TransportError> for Close {
+541    fn from(x: TransportError) -> Self {
+542        Self::Connection(x.into())
+543    }
+544}
+545impl From<ConnectionClose> for Close {
+546    fn from(x: ConnectionClose) -> Self {
+547        Self::Connection(x)
+548    }
+549}
+550impl From<ApplicationClose> for Close {
+551    fn from(x: ApplicationClose) -> Self {
+552        Self::Application(x)
+553    }
+554}
+555
+556/// Reason given by the transport for closing the connection
+557#[derive(Debug, Clone, PartialEq, Eq)]
+558pub struct ConnectionClose {
+559    /// Class of error as encoded in the specification
+560    pub error_code: TransportErrorCode,
+561    /// Type of frame that caused the close
+562    pub frame_type: MaybeFrame,
+563    /// Human-readable reason for the close
+564    pub reason: Bytes,
+565}
+566
+567impl fmt::Display for ConnectionClose {
+568    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+569        self.error_code.fmt(f)?;
+570        if !self.reason.as_ref().is_empty() {
+571            f.write_str(": ")?;
+572            f.write_str(&String::from_utf8_lossy(&self.reason))?;
+573        }
+574        Ok(())
+575    }
+576}
+577
+578impl From<TransportError> for ConnectionClose {
+579    fn from(x: TransportError) -> Self {
+580        Self {
+581            error_code: x.code,
+582            frame_type: x.frame,
+583            reason: x.reason.into(),
+584        }
+585    }
+586}
+587
+588impl FrameStruct for ConnectionClose {
+589    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+590}
+591
+592impl ConnectionClose {
+593    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
+594        out.write(FrameType::ConnectionClose); // 1 byte
+595        out.write(self.error_code); // <= 8 bytes
+596        out.write(self.frame_type); // <= 8 bytes
+597        let max_len = max_len
+598            - 3
+599            - self.frame_type.size()
+600            - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
+601        let actual_len = self.reason.len().min(max_len);
+602        out.write_var(actual_len as u64); // <= 8 bytes
+603        out.put_slice(&self.reason[0..actual_len]); // whatever's left
+604    }
+605}
+606
+607/// Reason given by an application for closing the connection
+608#[derive(Debug, Clone, PartialEq, Eq)]
+609pub struct ApplicationClose {
+610    /// Application-specific reason code
+611    pub error_code: VarInt,
+612    /// Human-readable reason for the close
+613    pub reason: Bytes,
+614}
+615
+616impl fmt::Display for ApplicationClose {
+617    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+618        if !self.reason.as_ref().is_empty() {
+619            f.write_str(&String::from_utf8_lossy(&self.reason))?;
+620            f.write_str(" (code ")?;
+621            self.error_code.fmt(f)?;
+622            f.write_str(")")?;
+623        } else {
+624            self.error_code.fmt(f)?;
+625        }
+626        Ok(())
+627    }
+628}
+629
+630impl FrameStruct for ApplicationClose {
+631    const SIZE_BOUND: usize = 1 + 8 + 8;
+632}
+633
+634impl ApplicationClose {
+635    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
+636        out.write(FrameType::ApplicationClose); // 1 byte
+637        out.write(self.error_code); // <= 8 bytes
+638        let max_len = max_len - 3 - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
+639        let actual_len = self.reason.len().min(max_len);
+640        out.write_var(actual_len as u64); // <= 8 bytes
+641        out.put_slice(&self.reason[0..actual_len]); // whatever's left
+642    }
+643}
+644
+645#[derive(Clone, Eq, PartialEq)]
+646pub struct PathAck {
+647    pub path_id: PathId,
+648    pub largest: u64,
+649    pub delay: u64,
+650    pub additional: Bytes,
+651    pub ecn: Option<EcnCounts>,
+652}
+653
+654impl fmt::Debug for PathAck {
+655    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+656        let mut ranges = "[".to_string();
+657        let mut first = true;
+658        for range in self.into_iter() {
+659            if !first {
+660                ranges.push(',');
+661            }
+662            write!(ranges, "{range:?}")?;
+663            first = false;
+664        }
+665        ranges.push(']');
+666
+667        f.debug_struct("PathAck")
+668            .field("path_id", &self.path_id)
+669            .field("largest", &self.largest)
+670            .field("delay", &self.delay)
+671            .field("ecn", &self.ecn)
+672            .field("ranges", &ranges)
+673            .finish()
+674    }
+675}
+676
+677impl<'a> IntoIterator for &'a PathAck {
+678    type Item = RangeInclusive<u64>;
+679    type IntoIter = AckIter<'a>;
+680
+681    fn into_iter(self) -> AckIter<'a> {
+682        AckIter::new(self.largest, &self.additional[..])
+683    }
+684}
+685
+686impl PathAck {
+687    pub fn into_ack(self) -> (Ack, PathId) {
+688        let ack = Ack {
+689            largest: self.largest,
+690            delay: self.delay,
+691            additional: self.additional,
+692            ecn: self.ecn,
+693        };
+694
+695        (ack, self.path_id)
+696    }
+697
+698    pub(crate) fn encoder<'a>(
+699        path_id: PathId,
+700        delay: u64,
+701        ranges: &'a ArrayRangeSet,
+702        ecn: Option<&'a EcnCounts>,
+703    ) -> PathAckEncoder<'a> {
+704        PathAckEncoder {
+705            path_id,
+706            delay,
+707            ranges,
+708            ecn,
+709        }
+710    }
+711}
+712
+713pub(crate) struct PathAckEncoder<'a> {
+714    path_id: PathId,
+715    delay: u64,
+716    ranges: &'a ArrayRangeSet,
+717    ecn: Option<&'a EcnCounts>,
+718}
+719
+720impl<'a> Encodable for PathAckEncoder<'a> {
+721    /// Encode [`Self`] into the given buffer
+722    ///
+723    /// The [`FrameType`] will be either [`FrameType::PathAckEcn`] or [`FrameType::PathAck`]
+724    /// depending on whether [`EcnCounts`] are provided.
+725    ///
+726    /// PANICS: if `ranges` is empty.
+727    fn encode<W: BufMut>(&self, buf: &mut W) {
+728        let PathAckEncoder {
+729            path_id,
+730            delay,
+731            ranges,
+732            ecn,
+733        } = self;
+734        let mut rest = ranges.iter().rev();
+735        let first = rest
+736            .next()
+737            .expect("Caller has verified ranges is non empty");
+738        let largest = first.end - 1;
+739        let first_size = first.end - first.start;
+740        let kind = match ecn.is_some() {
+741            true => FrameType::PathAckEcn,
+742            false => FrameType::PathAck,
+743        };
+744        buf.write(kind);
+745        buf.write(*path_id);
+746        buf.write_var(largest);
+747        buf.write_var(*delay);
+748        buf.write_var(ranges.len() as u64 - 1);
+749        buf.write_var(first_size - 1);
+750        let mut prev = first.start;
+751        for block in rest {
+752            let size = block.end - block.start;
+753            buf.write_var(prev - block.end - 1);
+754            buf.write_var(size - 1);
+755            prev = block.start;
+756        }
+757        if let Some(x) = ecn {
+758            x.encode(buf)
+759        }
+760    }
+761}
+762
+763#[derive(Clone, Eq, PartialEq)]
+764pub struct Ack {
+765    pub largest: u64,
+766    pub delay: u64,
+767    pub additional: Bytes,
+768    pub ecn: Option<EcnCounts>,
+769}
+770
+771impl fmt::Debug for Ack {
+772    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+773        let mut ranges = "[".to_string();
+774        let mut first = true;
+775        for range in self.iter() {
+776            if !first {
+777                ranges.push(',');
+778            }
+779            write!(ranges, "{range:?}").unwrap();
+780            first = false;
+781        }
+782        ranges.push(']');
+783
+784        f.debug_struct("Ack")
+785            .field("largest", &self.largest)
+786            .field("delay", &self.delay)
+787            .field("ecn", &self.ecn)
+788            .field("ranges", &ranges)
+789            .finish()
+790    }
+791}
+792
+793impl<'a> IntoIterator for &'a Ack {
+794    type Item = RangeInclusive<u64>;
+795    type IntoIter = AckIter<'a>;
+796
+797    fn into_iter(self) -> AckIter<'a> {
+798        AckIter::new(self.largest, &self.additional[..])
+799    }
+800}
+801
+802impl Ack {
+803    pub(crate) fn encoder<'a>(
+804        delay: u64,
+805        ranges: &'a ArrayRangeSet,
+806        ecn: Option<&'a EcnCounts>,
+807    ) -> AckEncoder<'a> {
+808        AckEncoder { delay, ranges, ecn }
+809    }
+810
+811    pub fn iter(&self) -> AckIter<'_> {
+812        self.into_iter()
+813    }
+814}
+815
+816pub(crate) struct AckEncoder<'a> {
+817    delay: u64,
+818    ranges: &'a ArrayRangeSet,
+819    ecn: Option<&'a EcnCounts>,
+820}
+821
+822impl<'a> Encodable for AckEncoder<'a> {
+823    fn encode<W: BufMut>(&self, buf: &mut W) {
+824        let AckEncoder { delay, ranges, ecn } = self;
+825        let mut rest = ranges.iter().rev();
+826        let first = rest.next().unwrap();
+827        let largest = first.end - 1;
+828        let first_size = first.end - first.start;
+829        let kind = match ecn.is_some() {
+830            true => FrameType::AckEcn,
+831            false => FrameType::Ack,
+832        };
+833        buf.write(kind);
+834        buf.write_var(largest);
+835        buf.write_var(*delay);
+836        buf.write_var(ranges.len() as u64 - 1);
+837        buf.write_var(first_size - 1);
+838        let mut prev = first.start;
+839        for block in rest {
+840            let size = block.end - block.start;
+841            buf.write_var(prev - block.end - 1);
+842            buf.write_var(size - 1);
+843            prev = block.start;
+844        }
+845        if let Some(x) = ecn {
+846            x.encode(buf)
+847        }
+848    }
+849}
+850
+851#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+852pub struct EcnCounts {
+853    pub ect0: u64,
+854    pub ect1: u64,
+855    pub ce: u64,
+856}
+857
+858impl std::ops::AddAssign<EcnCodepoint> for EcnCounts {
+859    fn add_assign(&mut self, rhs: EcnCodepoint) {
+860        match rhs {
+861            EcnCodepoint::Ect0 => {
+862                self.ect0 += 1;
+863            }
+864            EcnCodepoint::Ect1 => {
+865                self.ect1 += 1;
+866            }
+867            EcnCodepoint::Ce => {
+868                self.ce += 1;
+869            }
+870        }
+871    }
+872}
+873
+874impl EcnCounts {
+875    pub const ZERO: Self = Self {
+876        ect0: 0,
+877        ect1: 0,
+878        ce: 0,
+879    };
+880}
+881
+882impl Encodable for EcnCounts {
+883    fn encode<W: BufMut>(&self, out: &mut W) {
+884        out.write_var(self.ect0);
+885        out.write_var(self.ect1);
+886        out.write_var(self.ce);
+887    }
+888}
+889
+890#[derive(Debug, Clone)]
+891pub(crate) struct Stream {
+892    pub(crate) id: StreamId,
+893    pub(crate) offset: u64,
+894    pub(crate) fin: bool,
+895    pub(crate) data: Bytes,
+896}
+897
+898impl FrameStruct for Stream {
+899    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+900}
+901
+902/// Metadata from a stream frame
+903#[derive(Debug, Clone)]
+904pub(crate) struct StreamMeta {
+905    pub(crate) id: StreamId,
+906    pub(crate) offsets: Range<u64>,
+907    pub(crate) fin: bool,
+908}
+909
+910// This manual implementation exists because `Default` is not implemented for `StreamId`
+911impl Default for StreamMeta {
+912    fn default() -> Self {
+913        Self {
+914            id: StreamId(0),
+915            offsets: 0..0,
+916            fin: false,
+917        }
+918    }
+919}
+920
+921impl StreamMeta {
+922    pub(crate) fn encoder(&self, encode_length: bool) -> StreamMetaEncoder<'_> {
+923        StreamMetaEncoder {
+924            meta: self,
+925            encode_length,
+926        }
+927    }
+928}
+929
+930pub(crate) struct StreamMetaEncoder<'a> {
+931    meta: &'a StreamMeta,
+932    encode_length: bool,
+933}
+934
+935impl<'a> Encodable for StreamMetaEncoder<'a> {
+936    fn encode<W: BufMut>(&self, out: &mut W) {
+937        let StreamMetaEncoder {
+938            meta,
+939            encode_length: length,
+940        } = self;
+941        let mut ty = *StreamInfo::VALUES.start();
+942        if meta.offsets.start != 0 {
+943            ty |= 0x04;
+944        }
+945        if *length {
+946            ty |= 0x02;
+947        }
+948        if meta.fin {
+949            ty |= 0x01;
+950        }
+951        out.write_var(ty); // 1 byte
+952        out.write(meta.id); // <=8 bytes
+953        if meta.offsets.start != 0 {
+954            out.write_var(meta.offsets.start); // <=8 bytes
+955        }
+956        if *length {
+957            out.write_var(meta.offsets.end - meta.offsets.start); // <=8 bytes
+958        }
+959    }
+960}
+961
+962/// A vector of [`StreamMeta`] with optimization for the single element case
+963pub(crate) type StreamMetaVec = TinyVec<[StreamMeta; 1]>;
+964
+965#[derive(Debug, Clone)]
+966pub(crate) struct Crypto {
+967    pub(crate) offset: u64,
+968    pub(crate) data: Bytes,
+969}
+970
+971impl Crypto {
+972    pub(crate) const SIZE_BOUND: usize = 17;
+973}
+974
+975impl Encodable for Crypto {
+976    fn encode<W: BufMut>(&self, out: &mut W) {
+977        out.write(FrameType::Crypto);
+978        out.write_var(self.offset);
+979        out.write_var(self.data.len() as u64);
+980        out.put_slice(&self.data);
+981    }
+982}
+983
+984#[derive(Debug, Clone)]
+985pub(crate) struct NewToken {
+986    pub(crate) token: Bytes,
+987}
+988
+989impl Encodable for NewToken {
+990    fn encode<W: BufMut>(&self, out: &mut W) {
+991        out.write(FrameType::NewToken);
+992        out.write_var(self.token.len() as u64);
+993        out.put_slice(&self.token);
+994    }
+995}
+996
+997impl NewToken {
+998    pub(crate) fn size(&self) -> usize {
+999        1 + VarInt::from_u64(self.token.len() as u64).unwrap().size() + self.token.len()
+1000    }
+1001}
+1002
+1003#[derive(Debug, Clone)]
+1004pub(crate) struct MaxPathId(pub(crate) PathId);
+1005
+1006impl MaxPathId {
+1007    pub(crate) const SIZE_BOUND: usize =
+1008        FrameType::MaxPathId.size() + VarInt(u32::MAX as u64).size();
+1009}
+1010
+1011impl Decodable for MaxPathId {
+1012    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+1013        Ok(Self(buf.get()?))
+1014    }
+1015}
+1016
+1017impl Encodable for MaxPathId {
+1018    fn encode<B: BufMut>(&self, buf: &mut B) {
+1019        buf.write(FrameType::MaxPathId);
+1020        buf.write(self.0);
+1021    }
+1022}
+1023
+1024#[derive(Debug, Clone, PartialEq, Eq)]
+1025pub(crate) struct PathsBlocked(pub(crate) PathId);
+1026
+1027impl PathsBlocked {
+1028    pub(crate) const SIZE_BOUND: usize =
+1029        FrameType::PathsBlocked.size() + VarInt(u32::MAX as u64).size();
+1030}
+1031
+1032impl Encodable for PathsBlocked {
+1033    fn encode<B: BufMut>(&self, buf: &mut B) {
+1034        buf.write(FrameType::PathsBlocked);
+1035        buf.write(self.0);
+1036    }
+1037}
+1038
+1039impl Decodable for PathsBlocked {
+1040    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
+1041    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+1042        Ok(Self(buf.get()?))
+1043    }
+1044}
+1045
+1046#[derive(Debug, Clone, PartialEq, Eq)]
+1047pub(crate) struct PathCidsBlocked {
+1048    pub(crate) path_id: PathId,
+1049    pub(crate) next_seq: VarInt,
+1050}
+1051
+1052impl PathCidsBlocked {
+1053    pub(crate) const SIZE_BOUND: usize =
+1054        FrameType::PathCidsBlocked.size() + VarInt(u32::MAX as u64).size() + VarInt::MAX.size();
+1055}
+1056
+1057impl Decodable for PathCidsBlocked {
+1058    fn decode<R: Buf>(buf: &mut R) -> coding::Result<Self> {
+1059        Ok(Self {
+1060            path_id: buf.get()?,
+1061            next_seq: buf.get()?,
+1062        })
+1063    }
+1064}
+1065
+1066impl Encodable for PathCidsBlocked {
+1067    fn encode<W: BufMut>(&self, buf: &mut W) {
+1068        buf.write(FrameType::PathCidsBlocked);
+1069        buf.write(self.path_id);
+1070        buf.write(self.next_seq);
+1071    }
+1072}
+1073
+1074pub(crate) struct Iter {
+1075    bytes: Bytes,
+1076    last_ty: MaybeFrame,
+1077}
+1078
+1079impl Iter {
+1080    pub(crate) fn new(payload: Bytes) -> Result<Self, TransportError> {
+1081        if payload.is_empty() {
+1082            // "An endpoint MUST treat receipt of a packet containing no frames as a
+1083            // connection error of type PROTOCOL_VIOLATION."
+1084            // https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types
+1085            return Err(TransportError::PROTOCOL_VIOLATION(
+1086                "packet payload is empty",
+1087            ));
+1088        }
+1089
+1090        Ok(Self {
+1091            bytes: payload,
+1092            last_ty: MaybeFrame::None,
+1093        })
+1094    }
+1095
+1096    fn take_len(&mut self) -> Result<Bytes, UnexpectedEnd> {
+1097        let len = self.bytes.get_var()?;
+1098        if len > self.bytes.remaining() as u64 {
+1099            return Err(UnexpectedEnd);
+1100        }
+1101        Ok(self.bytes.split_to(len as usize))
+1102    }
+1103
+1104    #[track_caller]
+1105    fn try_next(&mut self) -> Result<Frame, IterErr> {
+1106        self.last_ty = self.bytes.get()?;
+1107
+1108        let ty = match self.last_ty {
+1109            MaybeFrame::None => FrameType::Padding,
+1110            MaybeFrame::Unknown(_other) => return Err(IterErr::InvalidFrameId),
+1111            MaybeFrame::Known(frame_type) => frame_type,
+1112        };
+1113        Ok(match ty {
+1114            FrameType::Padding => Frame::Padding,
+1115            FrameType::ResetStream => Frame::ResetStream(ResetStream {
+1116                id: self.bytes.get()?,
+1117                error_code: self.bytes.get()?,
+1118                final_offset: self.bytes.get()?,
+1119            }),
+1120            FrameType::ConnectionClose => Frame::Close(Close::Connection(ConnectionClose {
+1121                error_code: self.bytes.get()?,
+1122                frame_type: self.bytes.get()?,
+1123                reason: self.take_len()?,
+1124            })),
+1125            FrameType::ApplicationClose => Frame::Close(Close::Application(ApplicationClose {
+1126                error_code: self.bytes.get()?,
+1127                reason: self.take_len()?,
+1128            })),
+1129            FrameType::MaxData => Frame::MaxData(self.bytes.get()?),
+1130            FrameType::MaxStreamData => Frame::MaxStreamData {
+1131                id: self.bytes.get()?,
+1132                offset: self.bytes.get_var()?,
+1133            },
+1134            FrameType::MaxStreamsBidi => Frame::MaxStreams {
+1135                dir: Dir::Bi,
+1136                count: self.bytes.get_var()?,
+1137            },
+1138            FrameType::MaxStreamsUni => Frame::MaxStreams {
+1139                dir: Dir::Uni,
+1140                count: self.bytes.get_var()?,
+1141            },
+1142            FrameType::Ping => Frame::Ping,
+1143            FrameType::DataBlocked => Frame::DataBlocked {
+1144                offset: self.bytes.get_var()?,
+1145            },
+1146            FrameType::StreamDataBlocked => Frame::StreamDataBlocked {
+1147                id: self.bytes.get()?,
+1148                offset: self.bytes.get_var()?,
+1149            },
+1150            FrameType::StreamsBlockedBidi => Frame::StreamsBlocked {
+1151                dir: Dir::Bi,
+1152                limit: self.bytes.get_var()?,
+1153            },
+1154            FrameType::StreamsBlockedUni => Frame::StreamsBlocked {
+1155                dir: Dir::Uni,
+1156                limit: self.bytes.get_var()?,
+1157            },
+1158            FrameType::StopSending => Frame::StopSending(StopSending {
+1159                id: self.bytes.get()?,
+1160                error_code: self.bytes.get()?,
+1161            }),
+1162            FrameType::RetireConnectionId | FrameType::PathRetireConnectionId => {
+1163                Frame::RetireConnectionId(RetireConnectionId::decode(
+1164                    &mut self.bytes,
+1165                    ty == FrameType::PathRetireConnectionId,
+1166                )?)
+1167            }
+1168            FrameType::Ack | FrameType::AckEcn => {
+1169                let largest = self.bytes.get_var()?;
+1170                let delay = self.bytes.get_var()?;
+1171                let extra_blocks = self.bytes.get_var()? as usize;
+1172                let n = scan_ack_blocks(&self.bytes, largest, extra_blocks)?;
+1173                Frame::Ack(Ack {
+1174                    delay,
+1175                    largest,
+1176                    additional: self.bytes.split_to(n),
+1177                    ecn: if ty != FrameType::AckEcn && ty != FrameType::PathAckEcn {
+1178                        None
+1179                    } else {
+1180                        Some(EcnCounts {
+1181                            ect0: self.bytes.get_var()?,
+1182                            ect1: self.bytes.get_var()?,
+1183                            ce: self.bytes.get_var()?,
+1184                        })
+1185                    },
+1186                })
+1187            }
+1188            FrameType::PathAck | FrameType::PathAckEcn => {
+1189                let path_id = self.bytes.get()?;
+1190                let largest = self.bytes.get_var()?;
+1191                let delay = self.bytes.get_var()?;
+1192                let extra_blocks = self.bytes.get_var()? as usize;
+1193                let n = scan_ack_blocks(&self.bytes, largest, extra_blocks)?;
+1194                Frame::PathAck(PathAck {
+1195                    path_id,
+1196                    delay,
+1197                    largest,
+1198                    additional: self.bytes.split_to(n),
+1199                    ecn: if ty != FrameType::AckEcn && ty != FrameType::PathAckEcn {
+1200                        None
+1201                    } else {
+1202                        Some(EcnCounts {
+1203                            ect0: self.bytes.get_var()?,
+1204                            ect1: self.bytes.get_var()?,
+1205                            ce: self.bytes.get_var()?,
+1206                        })
+1207                    },
+1208                })
+1209            }
+1210            FrameType::PathChallenge => Frame::PathChallenge(self.bytes.get()?),
+1211            FrameType::PathResponse => Frame::PathResponse(self.bytes.get()?),
+1212            FrameType::NewConnectionId | FrameType::PathNewConnectionId => {
+1213                let read_path = ty == FrameType::PathNewConnectionId;
+1214                Frame::NewConnectionId(NewConnectionId::read(&mut self.bytes, read_path)?)
+1215            }
+1216            FrameType::Crypto => Frame::Crypto(Crypto {
+1217                offset: self.bytes.get_var()?,
+1218                data: self.take_len()?,
+1219            }),
+1220            FrameType::NewToken => Frame::NewToken(NewToken {
+1221                token: self.take_len()?,
+1222            }),
+1223            FrameType::HandshakeDone => Frame::HandshakeDone,
+1224            FrameType::AckFrequency => Frame::AckFrequency(AckFrequency {
+1225                sequence: self.bytes.get()?,
+1226                ack_eliciting_threshold: self.bytes.get()?,
+1227                request_max_ack_delay: self.bytes.get()?,
+1228                reordering_threshold: self.bytes.get()?,
+1229            }),
+1230            FrameType::ImmediateAck => Frame::ImmediateAck,
+1231            FrameType::ObservedIpv4Addr | FrameType::ObservedIpv6Addr => {
+1232                let is_ipv6 = ty == FrameType::ObservedIpv6Addr;
+1233                let observed = ObservedAddr::read(&mut self.bytes, is_ipv6)?;
+1234                Frame::ObservedAddr(observed)
+1235            }
+1236            FrameType::PathAbandon => Frame::PathAbandon(PathAbandon::decode(&mut self.bytes)?),
+1237            FrameType::PathStatusAvailable => {
+1238                Frame::PathStatusAvailable(PathStatusAvailable::decode(&mut self.bytes)?)
+1239            }
+1240            FrameType::PathStatusBackup => {
+1241                Frame::PathStatusBackup(PathStatusBackup::decode(&mut self.bytes)?)
+1242            }
+1243            FrameType::MaxPathId => Frame::MaxPathId(MaxPathId::decode(&mut self.bytes)?),
+1244            FrameType::PathsBlocked => Frame::PathsBlocked(PathsBlocked::decode(&mut self.bytes)?),
+1245            FrameType::PathCidsBlocked => {
+1246                Frame::PathCidsBlocked(PathCidsBlocked::decode(&mut self.bytes)?)
+1247            }
+1248            FrameType::AddIpv4Address | FrameType::AddIpv6Address => {
+1249                let is_ipv6 = ty == FrameType::AddIpv6Address;
+1250                let add_address = AddAddress::read(&mut self.bytes, is_ipv6)?;
+1251                Frame::AddAddress(add_address)
+1252            }
+1253            FrameType::ReachOutAtIpv4 | FrameType::ReachOutAtIpv6 => {
+1254                let is_ipv6 = ty == FrameType::ReachOutAtIpv6;
+1255                let reach_out = ReachOut::read(&mut self.bytes, is_ipv6)?;
+1256                Frame::ReachOut(reach_out)
+1257            }
+1258            FrameType::RemoveAddress => Frame::RemoveAddress(RemoveAddress::read(&mut self.bytes)?),
+1259            FrameType::Stream(s) => Frame::Stream(Stream {
+1260                id: self.bytes.get()?,
+1261                offset: if s.off() { self.bytes.get_var()? } else { 0 },
+1262                fin: s.fin(),
+1263                data: if s.len() {
+1264                    self.take_len()?
+1265                } else {
+1266                    self.take_remaining()
+1267                },
+1268            }),
+1269            FrameType::Datagram(d) => Frame::Datagram(Datagram {
+1270                data: if d.len() {
+1271                    self.take_len()?
+1272                } else {
+1273                    self.take_remaining()
+1274                },
+1275            }),
+1276        })
+1277    }
+1278
+1279    fn take_remaining(&mut self) -> Bytes {
+1280        mem::take(&mut self.bytes)
+1281    }
+1282}
+1283
+1284impl Iterator for Iter {
+1285    type Item = Result<Frame, InvalidFrame>;
+1286    fn next(&mut self) -> Option<Self::Item> {
+1287        if !self.bytes.has_remaining() {
+1288            return None;
+1289        }
+1290        match self.try_next() {
+1291            Ok(x) => Some(Ok(x)),
+1292            Err(e) => {
+1293                // Corrupt frame, skip it and everything that follows
+1294                self.bytes.clear();
+1295                Some(Err(InvalidFrame {
+1296                    ty: self.last_ty,
+1297                    reason: e.reason(),
+1298                }))
+1299            }
+1300        }
+1301    }
+1302}
+1303
+1304#[derive(Debug)]
+1305pub(crate) struct InvalidFrame {
+1306    pub(crate) ty: MaybeFrame,
+1307    pub(crate) reason: &'static str,
+1308}
+1309
+1310impl From<InvalidFrame> for TransportError {
+1311    fn from(err: InvalidFrame) -> Self {
+1312        let mut te = Self::FRAME_ENCODING_ERROR(err.reason);
+1313        te.frame = err.ty;
+1314        te
+1315    }
+1316}
+1317
+1318/// Validate exactly `n` ACK ranges in `buf` and return the number of bytes they cover
+1319fn scan_ack_blocks(mut buf: &[u8], largest: u64, n: usize) -> Result<usize, IterErr> {
+1320    let total_len = buf.remaining();
+1321    let first_block = buf.get_var()?;
+1322    let mut smallest = largest.checked_sub(first_block).ok_or(IterErr::Malformed)?;
+1323    for _ in 0..n {
+1324        let gap = buf.get_var()?;
+1325        smallest = smallest.checked_sub(gap + 2).ok_or(IterErr::Malformed)?;
+1326        let block = buf.get_var()?;
+1327        smallest = smallest.checked_sub(block).ok_or(IterErr::Malformed)?;
+1328    }
+1329    Ok(total_len - buf.remaining())
+1330}
+1331
+1332#[derive(Debug)]
+1333enum IterErr {
+1334    UnexpectedEnd,
+1335    InvalidFrameId,
+1336    Malformed,
+1337}
+1338
+1339impl IterErr {
+1340    fn reason(&self) -> &'static str {
+1341        use IterErr::*;
+1342        match *self {
+1343            UnexpectedEnd => "unexpected end",
+1344            InvalidFrameId => "invalid frame ID",
+1345            Malformed => "malformed",
+1346        }
+1347    }
+1348}
+1349
+1350impl From<UnexpectedEnd> for IterErr {
+1351    fn from(_: UnexpectedEnd) -> Self {
+1352        Self::UnexpectedEnd
+1353    }
+1354}
+1355
+1356#[derive(Debug, Clone)]
+1357pub struct AckIter<'a> {
+1358    largest: u64,
+1359    data: &'a [u8],
+1360}
+1361
+1362impl<'a> AckIter<'a> {
+1363    fn new(largest: u64, data: &'a [u8]) -> Self {
+1364        Self { largest, data }
+1365    }
+1366}
+1367
+1368impl Iterator for AckIter<'_> {
+1369    type Item = RangeInclusive<u64>;
+1370    fn next(&mut self) -> Option<RangeInclusive<u64>> {
+1371        if !self.data.has_remaining() {
+1372            return None;
+1373        }
+1374        let block = self.data.get_var().unwrap();
+1375        let largest = self.largest;
+1376        if let Ok(gap) = self.data.get_var() {
+1377            self.largest -= block + gap + 2;
+1378        }
+1379        Some(largest - block..=largest)
+1380    }
+1381}
+1382
+1383#[allow(unreachable_pub)] // fuzzing only
+1384#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
+1385#[derive(Debug, Copy, Clone)]
+1386pub struct ResetStream {
+1387    pub(crate) id: StreamId,
+1388    pub(crate) error_code: VarInt,
+1389    pub(crate) final_offset: VarInt,
+1390}
+1391
+1392impl FrameStruct for ResetStream {
+1393    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+1394}
+1395
+1396impl Encodable for ResetStream {
+1397    fn encode<W: BufMut>(&self, out: &mut W) {
+1398        out.write(FrameType::ResetStream); // 1 byte
+1399        out.write(self.id); // <= 8 bytes
+1400        out.write(self.error_code); // <= 8 bytes
+1401        out.write(self.final_offset); // <= 8 bytes
+1402    }
+1403}
+1404
+1405#[derive(Debug, Copy, Clone)]
+1406pub(crate) struct StopSending {
+1407    pub(crate) id: StreamId,
+1408    pub(crate) error_code: VarInt,
+1409}
+1410
+1411impl FrameStruct for StopSending {
+1412    const SIZE_BOUND: usize = 1 + 8 + 8;
+1413}
+1414
+1415impl Encodable for StopSending {
+1416    fn encode<W: BufMut>(&self, out: &mut W) {
+1417        out.write(FrameType::StopSending); // 1 byte
+1418        out.write(self.id); // <= 8 bytes
+1419        out.write(self.error_code) // <= 8 bytes
+1420    }
+1421}
+1422
+1423#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+1424pub(crate) struct NewConnectionId {
+1425    pub(crate) path_id: Option<PathId>,
+1426    pub(crate) sequence: u64,
+1427    pub(crate) retire_prior_to: u64,
+1428    pub(crate) id: ConnectionId,
+1429    pub(crate) reset_token: ResetToken,
+1430}
+1431
+1432impl NewConnectionId {
+1433    /// Maximum size of this frame when the frame type is [`FrameType::NewConnectionId`],
+1434    pub(crate) const SIZE_BOUND: usize = {
+1435        let type_len = FrameType::NewConnectionId.size();
+1436        let seq_max_len = 8usize;
+1437        let retire_prior_to_max_len = 8usize;
+1438        let cid_len_len = 1;
+1439        let cid_len = 160;
+1440        let reset_token_len = 16;
+1441        type_len + seq_max_len + retire_prior_to_max_len + cid_len_len + cid_len + reset_token_len
+1442    };
+1443
+1444    /// Maximum size of this frame when the frame type is [`FrameType::PathNewConnectionId`],
+1445    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
+1446        let type_len = FrameType::PathNewConnectionId.size();
+1447        let path_id_len = VarInt::from_u32(u32::MAX).size();
+1448        let seq_max_len = 8usize;
+1449        let retire_prior_to_max_len = 8usize;
+1450        let cid_len_len = 1;
+1451        let cid_len = 160;
+1452        let reset_token_len = 16;
+1453        type_len
+1454            + path_id_len
+1455            + seq_max_len
+1456            + retire_prior_to_max_len
+1457            + cid_len_len
+1458            + cid_len
+1459            + reset_token_len
+1460    };
+1461
+1462    pub(crate) fn get_type(&self) -> FrameType {
+1463        if self.path_id.is_some() {
+1464            FrameType::PathNewConnectionId
+1465        } else {
+1466            FrameType::NewConnectionId
+1467        }
+1468    }
+1469
+1470    /// Returns the maximum encoded size on the wire.
+1471    ///
+1472    /// This is a rough upper estimate, does not squeeze every last byte out.
+1473    pub(crate) const fn size_bound(path_new_cid: bool, cid_len: usize) -> usize {
+1474        let upper_bound = match path_new_cid {
+1475            true => Self::SIZE_BOUND_MULTIPATH,
+1476            false => Self::SIZE_BOUND,
+1477        };
+1478        // instead of using the maximum cid len, use the provided one
+1479        upper_bound - 160 + cid_len
+1480    }
+1481
+1482    fn read<R: Buf>(bytes: &mut R, read_path: bool) -> Result<Self, IterErr> {
+1483        let path_id = if read_path { Some(bytes.get()?) } else { None };
+1484        let sequence = bytes.get_var()?;
+1485        let retire_prior_to = bytes.get_var()?;
+1486        if retire_prior_to > sequence {
+1487            return Err(IterErr::Malformed);
+1488        }
+1489        let length = bytes.get::<u8>()? as usize;
+1490        if length > MAX_CID_SIZE || length == 0 {
+1491            return Err(IterErr::Malformed);
+1492        }
+1493        if length > bytes.remaining() {
+1494            return Err(IterErr::UnexpectedEnd);
+1495        }
+1496        let mut stage = [0; MAX_CID_SIZE];
+1497        bytes.copy_to_slice(&mut stage[0..length]);
+1498        let id = ConnectionId::new(&stage[..length]);
+1499        if bytes.remaining() < 16 {
+1500            return Err(IterErr::UnexpectedEnd);
+1501        }
+1502        let mut reset_token = [0; RESET_TOKEN_SIZE];
+1503        bytes.copy_to_slice(&mut reset_token);
+1504        Ok(Self {
+1505            path_id,
+1506            sequence,
+1507            retire_prior_to,
+1508            id,
+1509            reset_token: reset_token.into(),
+1510        })
+1511    }
+1512}
+1513
+1514impl Encodable for NewConnectionId {
+1515    fn encode<W: BufMut>(&self, out: &mut W) {
+1516        out.write(self.get_type());
+1517        if let Some(id) = self.path_id {
+1518            out.write(id);
+1519        }
+1520        out.write_var(self.sequence);
+1521        out.write_var(self.retire_prior_to);
+1522        out.write(self.id.len() as u8);
+1523        out.put_slice(&self.id);
+1524        out.put_slice(&self.reset_token);
+1525    }
+1526}
+1527
+1528impl FrameStruct for NewConnectionId {
+1529    const SIZE_BOUND: usize = 1 + 8 + 8 + 1 + MAX_CID_SIZE + RESET_TOKEN_SIZE;
+1530}
+1531
+1532/// An unreliable datagram
+1533#[derive(Debug, Clone)]
+1534pub struct Datagram {
+1535    /// Payload
+1536    pub data: Bytes,
+1537}
+1538
+1539impl FrameStruct for Datagram {
+1540    const SIZE_BOUND: usize = 1 + 8;
+1541}
+1542
+1543impl Datagram {
+1544    pub(crate) fn size(&self, length: bool) -> usize {
+1545        1 + if length {
+1546            VarInt::from_u64(self.data.len() as u64).unwrap().size()
+1547        } else {
+1548            0
+1549        } + self.data.len()
+1550    }
+1551}
+1552
+1553impl Encodable for Datagram {
+1554    fn encode<B: BufMut>(&self, out: &mut B) {
+1555        // A datagram is encoded only after this is verified.
+1556        const ENCODE_LEN: bool = true;
+1557        out.write(FrameType::Datagram(DatagramInfo(
+1558            *DatagramInfo::VALUES.start() as u8 | u8::from(ENCODE_LEN),
+1559        ))); // 1 byte
+1560        // Safe to unwrap because we check length sanity before queueing datagrams
+1561        out.write(VarInt::from_u64(self.data.len() as u64).unwrap()); // <= 8 bytes
+1562        out.put_slice(&self.data);
+1563    }
+1564}
+1565
+1566#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+1567pub(crate) struct AckFrequency {
+1568    pub(crate) sequence: VarInt,
+1569    pub(crate) ack_eliciting_threshold: VarInt,
+1570    pub(crate) request_max_ack_delay: VarInt,
+1571    pub(crate) reordering_threshold: VarInt,
+1572}
+1573
+1574impl Encodable for AckFrequency {
+1575    fn encode<W: BufMut>(&self, buf: &mut W) {
+1576        buf.write(FrameType::AckFrequency);
+1577        buf.write(self.sequence);
+1578        buf.write(self.ack_eliciting_threshold);
+1579        buf.write(self.request_max_ack_delay);
+1580        buf.write(self.reordering_threshold);
+1581    }
+1582}
+1583
+1584/* Address Discovery https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/ */
+1585
+1586/// Conjunction of the information contained in the address discovery frames
+1587/// ([`FrameType::ObservedIpv4Addr`], [`FrameType::ObservedIpv6Addr`]).
+1588#[derive(Debug, PartialEq, Eq, Clone)]
+1589pub(crate) struct ObservedAddr {
+1590    /// Monotonically increasing integer within the same connection.
+1591    pub(crate) seq_no: VarInt,
+1592    /// Reported observed address.
+1593    pub(crate) ip: IpAddr,
+1594    /// Reported observed port.
+1595    pub(crate) port: u16,
+1596}
+1597
+1598impl ObservedAddr {
+1599    pub(crate) fn new<N: Into<VarInt>>(remote: std::net::SocketAddr, seq_no: N) -> Self {
+1600        Self {
+1601            ip: remote.ip(),
+1602            port: remote.port(),
+1603            seq_no: seq_no.into(),
+1604        }
+1605    }
+1606
+1607    /// Get the [`FrameType`] for this frame.
+1608    pub(crate) fn get_type(&self) -> FrameType {
+1609        if self.ip.is_ipv6() {
+1610            FrameType::ObservedIpv6Addr
+1611        } else {
+1612            FrameType::ObservedIpv4Addr
+1613        }
+1614    }
+1615
+1616    /// Compute the number of bytes needed to encode the frame.
+1617    pub(crate) fn size(&self) -> usize {
+1618        let type_size = self.get_type().size();
+1619        let req_id_bytes = self.seq_no.size();
+1620        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+1621        let port_bytes = 2;
+1622        type_size + req_id_bytes + ip_bytes + port_bytes
+1623    }
+1624
+1625    /// Reads the frame contents from the buffer.
+1626    ///
+1627    /// Should only be called when the frame type has been identified as
+1628    /// [`FrameType::ObservedIpv4Addr`] or [`FrameType::ObservedIpv6Addr`].
+1629    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+1630        let seq_no = bytes.get()?;
+1631        let ip = if is_ipv6 {
+1632            IpAddr::V6(bytes.get()?)
+1633        } else {
+1634            IpAddr::V4(bytes.get()?)
+1635        };
+1636        let port = bytes.get()?;
+1637        Ok(Self { seq_no, ip, port })
+1638    }
+1639
+1640    /// Gives the [`SocketAddr`] reported in the frame.
+1641    pub(crate) fn socket_addr(&self) -> SocketAddr {
+1642        (self.ip, self.port).into()
+1643    }
+1644}
+1645
+1646impl Encodable for ObservedAddr {
+1647    fn encode<W: BufMut>(&self, buf: &mut W) {
+1648        buf.write(self.get_type());
+1649        buf.write(self.seq_no);
+1650        match self.ip {
+1651            IpAddr::V4(ipv4_addr) => {
+1652                buf.write(ipv4_addr);
+1653            }
+1654            IpAddr::V6(ipv6_addr) => {
+1655                buf.write(ipv6_addr);
+1656            }
+1657        }
+1658        buf.write::<u16>(self.port);
+1659    }
+1660}
+1661
+1662/* Multipath <https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/> */
+1663
+1664#[derive(Debug, PartialEq, Eq)]
+1665pub(crate) struct PathAbandon {
+1666    pub(crate) path_id: PathId,
+1667    pub(crate) error_code: TransportErrorCode,
+1668}
+1669
+1670impl PathAbandon {
+1671    pub(crate) const SIZE_BOUND: usize = FrameType::PathAbandon.size() + 8 + 8;
+1672}
+1673
+1674impl Encodable for PathAbandon {
+1675    fn encode<W: BufMut>(&self, buf: &mut W) {
+1676        buf.write(FrameType::PathAbandon);
+1677        buf.write(self.path_id);
+1678        buf.write(self.error_code);
+1679    }
+1680}
+1681
+1682impl Decodable for PathAbandon {
+1683    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+1684        Ok(Self {
+1685            path_id: bytes.get()?,
+1686            error_code: bytes.get()?,
+1687        })
+1688    }
+1689}
+1690
+1691#[derive(Debug, PartialEq, Eq)]
+1692pub(crate) struct PathStatusAvailable {
+1693    pub(crate) path_id: PathId,
+1694    pub(crate) status_seq_no: VarInt,
+1695}
+1696
+1697impl PathStatusAvailable {
+1698    const TYPE: FrameType = FrameType::PathStatusAvailable;
+1699    pub(crate) const SIZE_BOUND: usize = FrameType::PathStatusAvailable.size() + 8 + 8;
+1700}
+1701
+1702impl Encodable for PathStatusAvailable {
+1703    fn encode<W: BufMut>(&self, buf: &mut W) {
+1704        buf.write(Self::TYPE);
+1705        buf.write(self.path_id);
+1706        buf.write(self.status_seq_no);
+1707    }
+1708}
+1709
+1710impl Decodable for PathStatusAvailable {
+1711    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+1712        Ok(Self {
+1713            path_id: bytes.get()?,
+1714            status_seq_no: bytes.get()?,
+1715        })
+1716    }
+1717}
+1718
+1719#[derive(Debug, PartialEq, Eq)]
+1720pub(crate) struct PathStatusBackup {
+1721    pub(crate) path_id: PathId,
+1722    pub(crate) status_seq_no: VarInt,
+1723}
+1724
+1725impl PathStatusBackup {
+1726    const TYPE: FrameType = FrameType::PathStatusBackup;
+1727}
+1728
+1729impl Encodable for PathStatusBackup {
+1730    fn encode<W: BufMut>(&self, buf: &mut W) {
+1731        buf.write(Self::TYPE);
+1732        buf.write(self.path_id);
+1733        buf.write(self.status_seq_no);
+1734    }
+1735}
+1736
+1737impl Decodable for PathStatusBackup {
+1738    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+1739        Ok(Self {
+1740            path_id: bytes.get()?,
+1741            status_seq_no: bytes.get()?,
+1742        })
+1743    }
+1744}
+1745
+1746/* Nat traversal frames */
+1747
+1748/// Conjunction of the information contained in the add address frames
+1749/// ([`FrameType::AddIpv4Address`], [`FrameType::AddIpv6Address`]).
+1750#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
+1751// TODO(@divma): remove
+1752#[allow(dead_code)]
+1753pub(crate) struct AddAddress {
+1754    /// Monotonically increasing integer within the same connection
+1755    // TODO(@divma): both assumed, the draft has no mention of this but it's standard
+1756    pub(crate) seq_no: VarInt,
+1757    /// Address to include in the known set
+1758    pub(crate) ip: IpAddr,
+1759    /// Port to use with this address
+1760    pub(crate) port: u16,
+1761}
+1762
+1763// TODO(@divma): remove
+1764#[allow(dead_code)]
+1765impl AddAddress {
+1766    /// Smallest number of bytes this type of frame is guaranteed to fit within.
+1767    pub(crate) const SIZE_BOUND: usize = Self {
+1768        ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
+1769        port: u16::MAX,
+1770        seq_no: VarInt::MAX,
+1771    }
+1772    .size();
+1773
+1774    pub(crate) const fn new((ip, port): (IpAddr, u16), seq_no: VarInt) -> Self {
+1775        Self { ip, port, seq_no }
+1776    }
+1777
+1778    /// Get the [`FrameType`] for this frame.
+1779    pub(crate) const fn get_type(&self) -> FrameType {
+1780        if self.ip.is_ipv6() {
+1781            FrameType::AddIpv6Address
+1782        } else {
+1783            FrameType::AddIpv4Address
+1784        }
+1785    }
+1786
+1787    /// Compute the number of bytes needed to encode the frame
+1788    pub(crate) const fn size(&self) -> usize {
+1789        let type_size = self.get_type().size();
+1790        let seq_no_bytes = self.seq_no.size();
+1791        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+1792        let port_bytes = 2;
+1793        type_size + seq_no_bytes + ip_bytes + port_bytes
+1794    }
+1795
+1796    /// Read the frame contents from the buffer
+1797    ///
+1798    /// Should only be called when the frame type has been identified as
+1799    /// [`FrameType::AddIpv4Address`] or [`FrameType::AddIpv6Address`].
+1800    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+1801        let seq_no = bytes.get()?;
+1802        let ip = if is_ipv6 {
+1803            IpAddr::V6(bytes.get()?)
+1804        } else {
+1805            IpAddr::V4(bytes.get()?)
+1806        };
+1807        let port = bytes.get()?;
+1808        Ok(Self { seq_no, ip, port })
+1809    }
+1810
+1811    /// Give the [`SocketAddr`] encoded in the frame
+1812    pub(crate) fn socket_addr(&self) -> SocketAddr {
+1813        self.ip_port().into()
+1814    }
+1815
+1816    pub(crate) fn ip_port(&self) -> (IpAddr, u16) {
+1817        (self.ip, self.port)
+1818    }
+1819}
+1820
+1821impl Encodable for AddAddress {
+1822    fn encode<W: BufMut>(&self, buf: &mut W) {
+1823        buf.write(self.get_type());
+1824        buf.write(self.seq_no);
+1825        match self.ip {
+1826            IpAddr::V4(ipv4_addr) => {
+1827                buf.write(ipv4_addr);
+1828            }
+1829            IpAddr::V6(ipv6_addr) => {
+1830                buf.write(ipv6_addr);
+1831            }
+1832        }
+1833        buf.write::<u16>(self.port);
+1834    }
+1835}
+1836
+1837/// Conjunction of the information contained in the reach out frames
+1838/// ([`FrameType::ReachOutAtIpv4`], [`FrameType::ReachOutAtIpv6`])
+1839#[derive(Debug, PartialEq, Eq, Clone)]
+1840// TODO(@divma): remove
+1841#[allow(dead_code)]
+1842pub(crate) struct ReachOut {
+1843    /// The sequence number of the NAT Traversal attempts
+1844    pub(crate) round: VarInt,
+1845    /// Address to use
+1846    pub(crate) ip: IpAddr,
+1847    /// Port to use with this address
+1848    pub(crate) port: u16,
+1849}
+1850
+1851// TODO(@divma): remove
+1852#[allow(dead_code)]
+1853impl ReachOut {
+1854    /// Smallest number of bytes this type of frame is guaranteed to fit within
+1855    pub(crate) const SIZE_BOUND: usize = Self {
+1856        round: VarInt::MAX,
+1857        ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
+1858        port: u16::MAX,
+1859    }
+1860    .size();
+1861
+1862    pub(crate) const fn new(round: VarInt, (ip, port): (IpAddr, u16)) -> Self {
+1863        Self { round, ip, port }
+1864    }
+1865
+1866    /// Get the [`FrameType`] for this frame
+1867    pub(crate) const fn get_type(&self) -> FrameType {
+1868        if self.ip.is_ipv6() {
+1869            FrameType::ReachOutAtIpv6
+1870        } else {
+1871            FrameType::ReachOutAtIpv4
+1872        }
+1873    }
+1874
+1875    /// Compute the number of bytes needed to encode the frame
+1876    pub(crate) const fn size(&self) -> usize {
+1877        let type_size = self.get_type().size();
+1878        let round_bytes = self.round.size();
+1879        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+1880        let port_bytes = 2;
+1881        type_size + round_bytes + ip_bytes + port_bytes
+1882    }
+1883
+1884    /// Read the frame contents from the buffer
+1885    ///
+1886    /// Should only be called when the frame type has been identified as
+1887    /// [`FrameType::ReachOutAtIpv4`] or [`FrameType::ReachOutAtIpv6`].
+1888    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+1889        let round = bytes.get()?;
+1890        let ip = if is_ipv6 {
+1891            IpAddr::V6(bytes.get()?)
+1892        } else {
+1893            IpAddr::V4(bytes.get()?)
+1894        };
+1895        let port = bytes.get()?;
+1896        Ok(Self { round, ip, port })
+1897    }
+1898
+1899    /// Give the [`SocketAddr`] encoded in the frame
+1900    pub(crate) fn socket_addr(&self) -> SocketAddr {
+1901        (self.ip, self.port).into()
+1902    }
+1903}
+1904
+1905impl Encodable for ReachOut {
+1906    fn encode<W: BufMut>(&self, buf: &mut W) {
+1907        buf.write(self.get_type());
+1908        buf.write(self.round);
+1909        match self.ip {
+1910            IpAddr::V4(ipv4_addr) => {
+1911                buf.write(ipv4_addr);
+1912            }
+1913            IpAddr::V6(ipv6_addr) => {
+1914                buf.write(ipv6_addr);
+1915            }
+1916        }
+1917        buf.write::<u16>(self.port);
+1918    }
+1919}
+1920
+1921/// Frame signaling an address is no longer being advertised
+1922#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
+1923// TODO(@divma): remove
+1924#[allow(dead_code)]
+1925pub(crate) struct RemoveAddress {
+1926    /// The sequence number of the address advertisement to be removed
+1927    pub(crate) seq_no: VarInt,
+1928}
+1929
+1930// TODO(@divma): remove
+1931#[allow(dead_code)]
+1932impl RemoveAddress {
+1933    /// [`FrameType`] of this frame
+1934    pub(crate) const TYPE: FrameType = FrameType::RemoveAddress;
+1935
+1936    /// Smallest number of bytes this type of frame is guaranteed to fit within
+1937    pub(crate) const SIZE_BOUND: usize = Self::new(VarInt::MAX).size();
+1938
+1939    pub(crate) const fn new(seq_no: VarInt) -> Self {
+1940        Self { seq_no }
+1941    }
+1942
+1943    /// Compute the number of bytes needed to encode the frame
+1944    pub(crate) const fn size(&self) -> usize {
+1945        let type_size = Self::TYPE.size();
+1946        let seq_no_bytes = self.seq_no.size();
+1947        type_size + seq_no_bytes
+1948    }
+1949
+1950    /// Read the frame contents from the buffer
+1951    ///
+1952    /// Should only be called when the frame type has been identified as
+1953    /// [`FrameType::RemoveAddress`].
+1954    pub(crate) fn read<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+1955        Ok(Self {
+1956            seq_no: bytes.get()?,
+1957        })
+1958    }
+1959}
+1960
+1961impl Encodable for RemoveAddress {
+1962    fn encode<W: BufMut>(&self, buf: &mut W) {
+1963        buf.write(Self::TYPE);
+1964        buf.write(self.seq_no);
+1965    }
+1966}
+1967
+1968#[cfg(test)]
+1969mod test {
+1970    use super::*;
+1971    use crate::coding::Encodable;
+1972    use assert_matches::assert_matches;
+1973
+1974    #[test]
+1975    fn frame_type() {
+1976        assert_eq!(
+1977            FrameType::try_from(FrameType::Padding.to_u64()),
+1978            Ok(FrameType::Padding),
+1979        );
+1980
+1981        assert_eq!(
+1982            FrameType::try_from(FrameType::Datagram(DatagramInfo(0x30)).to_u64()),
+1983            Ok(FrameType::Datagram(DatagramInfo(0x30))),
+1984        );
+1985
+1986        assert_eq!(
+1987            FrameType::try_from(FrameType::Stream(StreamInfo(0x08)).to_u64()),
+1988            Ok(FrameType::Stream(StreamInfo(0x08))),
+1989        );
+1990    }
+1991
+1992    #[track_caller]
+1993    fn frames(buf: Vec<u8>) -> Vec<Frame> {
+1994        Iter::new(Bytes::from(buf))
+1995            .unwrap()
+1996            .collect::<Result<Vec<_>, _>>()
+1997            .unwrap()
+1998    }
+1999
+2000    #[test]
+2001    fn ack_coding() {
+2002        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
+2003        let mut ranges = ArrayRangeSet::new();
+2004        for &packet in PACKETS {
+2005            ranges.insert(packet..packet + 1);
+2006        }
+2007        let mut buf = Vec::new();
+2008        const ECN: EcnCounts = EcnCounts {
+2009            ect0: 42,
+2010            ect1: 24,
+2011            ce: 12,
+2012        };
+2013        Ack::encoder(42, &ranges, Some(&ECN)).encode(&mut buf);
+2014        let frames = frames(buf);
+2015        assert_eq!(frames.len(), 1);
+2016        match frames[0] {
+2017            Frame::Ack(ref ack) => {
+2018                let mut packets = ack.iter().flatten().collect::<Vec<_>>();
+2019                packets.sort_unstable();
+2020                assert_eq!(&packets[..], PACKETS);
+2021                assert_eq!(ack.ecn, Some(ECN));
+2022            }
+2023            ref x => panic!("incorrect frame {x:?}"),
+2024        }
+2025    }
+2026
+2027    #[test]
+2028    #[allow(clippy::range_plus_one)]
+2029    fn path_ack_coding() {
+2030        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
+2031        let mut ranges = ArrayRangeSet::new();
+2032        for &packet in PACKETS {
+2033            ranges.insert(packet..packet + 1);
+2034        }
+2035        let mut buf = Vec::new();
+2036        const ECN: EcnCounts = EcnCounts {
+2037            ect0: 42,
+2038            ect1: 24,
+2039            ce: 12,
+2040        };
+2041        const PATH_ID: PathId = PathId::MAX;
+2042        PathAck::encoder(PATH_ID, 42, &ranges, Some(&ECN)).encode(&mut buf);
+2043        let frames = frames(buf);
+2044        assert_eq!(frames.len(), 1);
+2045        match frames[0] {
+2046            Frame::PathAck(ref ack) => {
+2047                assert_eq!(ack.path_id, PATH_ID);
+2048                let mut packets = ack.into_iter().flatten().collect::<Vec<_>>();
+2049                packets.sort_unstable();
+2050                assert_eq!(&packets[..], PACKETS);
+2051                assert_eq!(ack.ecn, Some(ECN));
+2052            }
+2053            ref x => panic!("incorrect frame {x:?}"),
+2054        }
+2055    }
+2056
+2057    #[test]
+2058    fn ack_frequency_coding() {
+2059        let mut buf = Vec::new();
+2060        let original = AckFrequency {
+2061            sequence: VarInt(42),
+2062            ack_eliciting_threshold: VarInt(20),
+2063            request_max_ack_delay: VarInt(50_000),
+2064            reordering_threshold: VarInt(1),
+2065        };
+2066        original.encode(&mut buf);
+2067        let frames = frames(buf);
+2068        assert_eq!(frames.len(), 1);
+2069        match &frames[0] {
+2070            Frame::AckFrequency(decoded) => assert_eq!(decoded, &original),
+2071            x => panic!("incorrect frame {x:?}"),
+2072        }
+2073    }
+2074
+2075    #[test]
+2076    fn immediate_ack_coding() {
+2077        let mut buf = Vec::new();
+2078        FrameType::ImmediateAck.encode(&mut buf);
+2079        let frames = frames(buf);
+2080        assert_eq!(frames.len(), 1);
+2081        assert_matches!(&frames[0], Frame::ImmediateAck);
+2082    }
+2083
+2084    /// Test that encoding and decoding [`ObservedAddr`] produces the same result.
+2085    #[test]
+2086    fn test_observed_addr_roundrip() {
+2087        let observed_addr = ObservedAddr {
+2088            seq_no: VarInt(42),
+2089            ip: std::net::Ipv4Addr::LOCALHOST.into(),
+2090            port: 4242,
+2091        };
+2092        let mut buf = Vec::with_capacity(observed_addr.size());
+2093        observed_addr.encode(&mut buf);
+2094
+2095        assert_eq!(
+2096            observed_addr.size(),
+2097            buf.len(),
+2098            "expected written bytes and actual size differ"
+2099        );
+2100
+2101        let mut decoded = frames(buf);
+2102        assert_eq!(decoded.len(), 1);
+2103        match decoded.pop().expect("non empty") {
+2104            Frame::ObservedAddr(decoded) => assert_eq!(decoded, observed_addr),
+2105            x => panic!("incorrect frame {x:?}"),
+2106        }
+2107    }
+2108
+2109    #[test]
+2110    fn test_path_abandon_roundtrip() {
+2111        let abandon = PathAbandon {
+2112            path_id: PathId(42),
+2113            error_code: TransportErrorCode::NO_ERROR,
+2114        };
+2115        let mut buf = Vec::new();
+2116        abandon.encode(&mut buf);
+2117
+2118        let mut decoded = frames(buf);
+2119        assert_eq!(decoded.len(), 1);
+2120        match decoded.pop().expect("non empty") {
+2121            Frame::PathAbandon(decoded) => assert_eq!(decoded, abandon),
+2122            x => panic!("incorrect frame {x:?}"),
+2123        }
+2124    }
+2125
+2126    #[test]
+2127    fn test_path_status_available_roundtrip() {
+2128        let path_status_available = PathStatusAvailable {
+2129            path_id: PathId(42),
+2130            status_seq_no: VarInt(73),
+2131        };
+2132        let mut buf = Vec::new();
+2133        path_status_available.encode(&mut buf);
+2134
+2135        let mut decoded = frames(buf);
+2136        assert_eq!(decoded.len(), 1);
+2137        match decoded.pop().expect("non empty") {
+2138            Frame::PathStatusAvailable(decoded) => assert_eq!(decoded, path_status_available),
+2139            x => panic!("incorrect frame {x:?}"),
+2140        }
+2141    }
+2142
+2143    #[test]
+2144    fn test_path_status_backup_roundtrip() {
+2145        let path_status_backup = PathStatusBackup {
+2146            path_id: PathId(42),
+2147            status_seq_no: VarInt(73),
+2148        };
+2149        let mut buf = Vec::new();
+2150        path_status_backup.encode(&mut buf);
+2151
+2152        let mut decoded = frames(buf);
+2153        assert_eq!(decoded.len(), 1);
+2154        match decoded.pop().expect("non empty") {
+2155            Frame::PathStatusBackup(decoded) => assert_eq!(decoded, path_status_backup),
+2156            x => panic!("incorrect frame {x:?}"),
+2157        }
+2158    }
+2159
+2160    #[test]
+2161    fn test_path_new_connection_id_roundtrip() {
+2162        let cid = NewConnectionId {
+2163            path_id: Some(PathId(22)),
+2164            sequence: 31,
+2165            retire_prior_to: 13,
+2166            id: ConnectionId::new(&[0xAB; 8]),
+2167            reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]),
+2168        };
+2169        let mut buf = Vec::new();
+2170        cid.encode(&mut buf);
+2171
+2172        let mut decoded = frames(buf);
+2173        assert_eq!(decoded.len(), 1);
+2174        match decoded.pop().expect("non empty") {
+2175            Frame::NewConnectionId(decoded) => assert_eq!(decoded, cid),
+2176            x => panic!("incorrect frame {x:?}"),
+2177        }
+2178    }
+2179
+2180    #[test]
+2181    fn test_path_retire_connection_id_roundtrip() {
+2182        let retire_cid = RetireConnectionId {
+2183            path_id: Some(PathId(22)),
+2184            sequence: 31,
+2185        };
+2186        let mut buf = Vec::new();
+2187        retire_cid.encode(&mut buf);
+2188
+2189        let mut decoded = frames(buf);
+2190        assert_eq!(decoded.len(), 1);
+2191        match decoded.pop().expect("non empty") {
+2192            Frame::RetireConnectionId(decoded) => assert_eq!(decoded, retire_cid),
+2193            x => panic!("incorrect frame {x:?}"),
+2194        }
+2195    }
+2196
+2197    #[test]
+2198    fn test_paths_blocked_path_cids_blocked_roundtrip() {
+2199        let mut buf = Vec::new();
+2200
+2201        let frame0 = PathsBlocked(PathId(22));
+2202        frame0.encode(&mut buf);
+2203        let frame1 = PathCidsBlocked {
+2204            path_id: PathId(23),
+2205            next_seq: VarInt(32),
+2206        };
+2207        frame1.encode(&mut buf);
+2208
+2209        let mut decoded = frames(buf);
+2210        assert_eq!(decoded.len(), 2);
+2211        match decoded.pop().expect("non empty") {
+2212            Frame::PathCidsBlocked(decoded) => assert_eq!(decoded, frame1),
+2213            x => panic!("incorrect frame {x:?}"),
+2214        }
+2215        match decoded.pop().expect("non empty") {
+2216            Frame::PathsBlocked(decoded) => assert_eq!(decoded, frame0),
+2217            x => panic!("incorrect frame {x:?}"),
+2218        }
+2219    }
+2220
+2221    /// Test that encoding and decoding [`AddAddress`] produces the same result
+2222    #[test]
+2223    fn test_add_address_roundrip() {
+2224        let add_address = AddAddress {
+2225            seq_no: VarInt(42),
+2226            ip: std::net::Ipv4Addr::LOCALHOST.into(),
+2227            port: 4242,
+2228        };
+2229        let mut buf = Vec::with_capacity(add_address.size());
+2230        add_address.encode(&mut buf);
+2231
+2232        assert_eq!(
+2233            add_address.size(),
+2234            buf.len(),
+2235            "expected written bytes and actual size differ"
+2236        );
+2237
+2238        let mut decoded = frames(buf);
+2239        assert_eq!(decoded.len(), 1);
+2240        match decoded.pop().expect("non empty") {
+2241            Frame::AddAddress(decoded) => assert_eq!(decoded, add_address),
+2242            x => panic!("incorrect frame {x:?}"),
+2243        }
+2244    }
+2245
+2246    /// Test that encoding and decoding [`AddAddress`] produces the same result
+2247    #[test]
+2248    fn test_reach_out_roundrip() {
+2249        let reach_out = ReachOut {
+2250            round: VarInt(42),
+2251            ip: std::net::Ipv6Addr::LOCALHOST.into(),
+2252            port: 4242,
+2253        };
+2254        let mut buf = Vec::with_capacity(reach_out.size());
+2255        reach_out.encode(&mut buf);
+2256
+2257        assert_eq!(
+2258            reach_out.size(),
+2259            buf.len(),
+2260            "expected written bytes and actual size differ"
+2261        );
+2262
+2263        let mut decoded = frames(buf);
+2264        assert_eq!(decoded.len(), 1);
+2265        match decoded.pop().expect("non empty") {
+2266            Frame::ReachOut(decoded) => assert_eq!(decoded, reach_out),
+2267            x => panic!("incorrect frame {x:?}"),
+2268        }
+2269    }
+2270
+2271    /// Test that encoding and decoding [`RemoveAddress`] produces the same result
+2272    #[test]
+2273    fn test_remove_address_roundrip() {
+2274        let remove_addr = RemoveAddress::new(VarInt(10));
+2275        let mut buf = Vec::with_capacity(remove_addr.size());
+2276        remove_addr.encode(&mut buf);
+2277
+2278        assert_eq!(
+2279            remove_addr.size(),
+2280            buf.len(),
+2281            "expected written bytes and actual size differ"
+2282        );
+2283
+2284        let mut decoded = frames(buf);
+2285        assert_eq!(decoded.len(), 1);
+2286        match decoded.pop().expect("non empty") {
+2287            Frame::RemoveAddress(decoded) => assert_eq!(decoded, remove_addr),
+2288            x => panic!("incorrect frame {x:?}"),
+2289        }
+2290    }
+2291}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/iroh_hp.rs.html b/pr/299/docs/src/iroh_quinn_proto/iroh_hp.rs.html new file mode 100644 index 000000000..6d6713d5c --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/iroh_hp.rs.html @@ -0,0 +1,513 @@ +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    pub(crate) addresses_to_probe: Vec<(VarInt, IpPort)>,
+52    /// [`PathId`]s of the cancelled round.
+53    pub(crate) prev_round_path_ids: Vec<PathId>,
+54}
+55
+56/// Event emitted when the client receives ADD_ADDRESS or REMOVE_ADDRESS frames.
+57#[derive(Debug, Clone)]
+58pub enum Event {
+59    /// An ADD_ADDRESS frame was received.
+60    AddressAdded(SocketAddr),
+61    /// A REMOVE_ADDRESS frame was received.
+62    AddressRemoved(SocketAddr),
+63}
+64
+65/// State kept for Iroh's nat traversal
+66#[derive(Debug, Default)]
+67pub(crate) enum State {
+68    #[default]
+69    NotNegotiated,
+70    ClientSide(ClientState),
+71    ServerSide(ServerState),
+72}
+73
+74#[derive(Debug)]
+75pub(crate) struct ClientState {
+76    /// Max number of remote addresses we allow
+77    ///
+78    /// This is set by the local endpoint.
+79    max_remote_addresses: usize,
+80    /// Max number of local addresses allowed
+81    ///
+82    /// This is set by the remote endpoint.
+83    max_local_addresses: usize,
+84    /// Candidate addresses the remote server reports as potentially reachable, to use for nat
+85    /// traversal attempts.
+86    ///
+87    /// These are indexed by their advertised Id. For each address, whether the address should be
+88    /// reported in nat traversal continuations is kept.
+89    remote_addresses: FxHashMap<VarInt, (IpPort, bool)>,
+90    /// Candidate addresses the local client reports as potentially reachable, to use for nat
+91    /// traversal attempts.
+92    local_addresses: FxHashSet<IpPort>,
+93    /// Current nat holepunching round.
+94    round: VarInt,
+95    /// [`PathId`]s used to probe remotes assigned to this round.
+96    round_path_ids: Vec<PathId>,
+97}
+98
+99impl ClientState {
+100    fn new(max_remote_addresses: usize, max_local_addresses: usize) -> Self {
+101        Self {
+102            max_remote_addresses,
+103            max_local_addresses,
+104            remote_addresses: Default::default(),
+105            local_addresses: Default::default(),
+106            round: Default::default(),
+107            round_path_ids: Default::default(),
+108        }
+109    }
+110
+111    fn add_local_address(&mut self, address: IpPort) -> Result<(), Error> {
+112        if self.local_addresses.len() < self.max_local_addresses {
+113            self.local_addresses.insert(address);
+114            Ok(())
+115        } else if self.local_addresses.contains(&address) {
+116            // at capacity, but the address is known, no issues here
+117            Ok(())
+118        } else {
+119            // at capacity and the address is new
+120            Err(Error::TooManyAddresses)
+121        }
+122    }
+123
+124    fn remove_local_address(&mut self, address: &IpPort) {
+125        self.local_addresses.remove(address);
+126    }
+127
+128    /// Initiates a new nat traversal round.
+129    ///
+130    /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT`
+131    /// frames, and initiating probing of the known remote addresses. When a new round is
+132    /// initiated, the previous one is cancelled, and paths that have not been opened should be
+133    /// closed.
+134    pub(crate) fn initiate_nat_traversal_round(&mut self) -> Result<NatTraversalRound, Error> {
+135        if self.local_addresses.is_empty() {
+136            return Err(Error::NotEnoughAddresses);
+137        }
+138
+139        let prev_round_path_ids = std::mem::take(&mut self.round_path_ids);
+140        self.round = self.round.saturating_add(1u8);
+141        let mut addresses_to_probe = Vec::with_capacity(self.remote_addresses.len());
+142        for (id, (address, report_in_continuation)) in self.remote_addresses.iter_mut() {
+143            addresses_to_probe.push((*id, *address));
+144            *report_in_continuation = false;
+145        }
+146
+147        Ok(NatTraversalRound {
+148            new_round: self.round,
+149            reach_out_at: self.local_addresses.iter().copied().collect(),
+150            addresses_to_probe,
+151            prev_round_path_ids,
+152        })
+153    }
+154
+155    /// Mark a remote address to be reported back in a nat traversal continuation if the error is
+156    /// considered spurious from a nat traversal point of view.
+157    ///
+158    /// Ids not present are silently ignored.
+159    pub(crate) fn report_in_continuation(&mut self, id: VarInt, e: crate::PathError) {
+160        match e {
+161            crate::PathError::MaxPathIdReached | crate::PathError::RemoteCidsExhausted => {
+162                if let Some((_address, report_in_continuation)) = self.remote_addresses.get_mut(&id)
+163                {
+164                    *report_in_continuation = true;
+165                }
+166            }
+167            _ => {}
+168        }
+169    }
+170
+171    /// Returns an address that needs to be probed, if any.
+172    ///
+173    /// The address will not be returned twice unless marked as such again with
+174    /// [`Self::report_in_continuation`].
+175    pub(crate) fn continue_nat_traversal_round(&mut self) -> Option<(VarInt, IpPort)> {
+176        // this being random depends on iteration not returning always on the same order
+177        let (id, (address, report_in_continuation)) = self
+178            .remote_addresses
+179            .iter_mut()
+180            .find(|(_id, (_addr, report))| *report)?;
+181        *report_in_continuation = false;
+182        Some((*id, *address))
+183    }
+184
+185    /// Add a [`PathId`] as part of the current attempts to create paths based on the server's
+186    /// advertised addresses.
+187    pub(crate) fn set_round_path_ids(&mut self, path_ids: Vec<PathId>) {
+188        self.round_path_ids = path_ids;
+189    }
+190
+191    /// Add a [`PathId`] as part of the current attempts to create paths based on the server's
+192    /// advertised addresses.
+193    pub(crate) fn add_round_path_id(&mut self, path_id: PathId) {
+194        self.round_path_ids.push(path_id);
+195    }
+196
+197    /// Adds an address to the remote set
+198    ///
+199    /// On success returns the address if it was new to the set. It will error when the set has no
+200    /// capacity for the address.
+201    pub(crate) fn add_remote_address(
+202        &mut self,
+203        add_addr: AddAddress,
+204    ) -> Result<Option<SocketAddr>, Error> {
+205        let AddAddress { seq_no, ip, port } = add_addr;
+206        let address = (ip, port);
+207        let allow_new = self.remote_addresses.len() < self.max_remote_addresses;
+208        match self.remote_addresses.entry(seq_no) {
+209            Entry::Occupied(mut occupied_entry) => {
+210                let is_update = occupied_entry.get().0 != address;
+211                if is_update {
+212                    occupied_entry.insert((address, false));
+213                }
+214                // The value might be different. This should not happen, but we assume that the new
+215                // address is more recent than the previous, and thus worth updating
+216                Ok(is_update.then_some(address.into()))
+217            }
+218            Entry::Vacant(vacant_entry) if allow_new => {
+219                vacant_entry.insert((address, false));
+220                Ok(Some(address.into()))
+221            }
+222            _ => Err(Error::TooManyAddresses),
+223        }
+224    }
+225
+226    /// Removes an address from the remote set
+227    ///
+228    /// Returns whether the address was present.
+229    pub(crate) fn remove_remote_address(
+230        &mut self,
+231        remove_addr: RemoveAddress,
+232    ) -> Option<SocketAddr> {
+233        self.remote_addresses
+234            .remove(&remove_addr.seq_no)
+235            .map(|(address, _report_in_continuation)| address.into())
+236    }
+237
+238    /// Checks that a received remote address is valid
+239    ///
+240    /// An address is valid as long as it does not change the value of a known address id.
+241    pub(crate) fn check_remote_address(&self, add_addr: &AddAddress) -> bool {
+242        match self.remote_addresses.get(&add_addr.seq_no) {
+243            None => true,
+244            Some((existing, _)) => existing == &add_addr.ip_port(),
+245        }
+246    }
+247
+248    pub(crate) fn get_remote_nat_traversal_addresses(&self) -> Vec<SocketAddr> {
+249        self.remote_addresses
+250            .values()
+251            .map(|(address, _report_in_continuation)| (*address).into())
+252            .collect()
+253    }
+254}
+255
+256#[derive(Debug)]
+257pub(crate) struct ServerState {
+258    /// Max number of remote addresses we allow.
+259    ///
+260    /// This is set by the local endpoint.
+261    max_remote_addresses: usize,
+262    /// Max number of local addresses allowed.
+263    ///
+264    /// This is set by the remote endpoint.
+265    max_local_addresses: usize,
+266    /// Candidate addresses the server reports as potentially reachable, to use for nat
+267    /// traversal attempts.
+268    local_addresses: FxHashMap<IpPort, VarInt>,
+269    /// The next id to use for local addresses sent to the client.
+270    next_local_addr_id: VarInt,
+271    /// Current nat holepunching round
+272    ///
+273    /// Servers keep track of the client's most recent round and cancel probing related to previous
+274    /// rounds.
+275    round: VarInt,
+276    /// Addresses to which PATH_CHALLENGES need to be sent.
+277    pending_probes: FxHashSet<IpPort>,
+278    /// Sent PATH_CHALLENGES for this round.
+279    ///
+280    /// This is used to validate the remotes assigned to each token.
+281    active_probes: IntMap<u64, IpPort>,
+282}
+283
+284impl ServerState {
+285    fn new(max_remote_addresses: usize, max_local_addresses: usize) -> Self {
+286        Self {
+287            max_remote_addresses,
+288            max_local_addresses,
+289            local_addresses: Default::default(),
+290            next_local_addr_id: Default::default(),
+291            round: Default::default(),
+292            pending_probes: Default::default(),
+293            active_probes: Default::default(),
+294        }
+295    }
+296
+297    fn add_local_address(&mut self, address: IpPort) -> Result<Option<AddAddress>, Error> {
+298        let allow_new = self.local_addresses.len() < self.max_local_addresses;
+299        match self.local_addresses.entry(address) {
+300            Entry::Occupied(_) => Ok(None),
+301            Entry::Vacant(vacant_entry) if allow_new => {
+302                let id = self.next_local_addr_id;
+303                self.next_local_addr_id = self.next_local_addr_id.saturating_add(1u8);
+304                vacant_entry.insert(id);
+305                Ok(Some(AddAddress::new(address, id)))
+306            }
+307            _ => Err(Error::TooManyAddresses),
+308        }
+309    }
+310
+311    fn remove_local_address(&mut self, address: &IpPort) -> Option<RemoveAddress> {
+312        self.local_addresses.remove(address).map(RemoveAddress::new)
+313    }
+314
+315    /// Handles a received [`ReachOut`].
+316    ///
+317    /// It returns the token that should be sent in response to this frame as a challenge, and
+318    /// whether this starts a new nat traversal round.
+319    ///
+320    /// If this frame was ignored, it returns `None`.
+321    pub(crate) fn handle_reach_out(&mut self, reach_out: ReachOut) -> Result<(), Error> {
+322        let ReachOut { round, ip, port } = reach_out;
+323
+324        if round < self.round {
+325            trace!(current_round=%self.round, "ignoring REACH_OUT for previous round");
+326            return Ok(());
+327        }
+328
+329        if round > self.round {
+330            self.round = round;
+331            self.pending_probes.clear();
+332            // TODO(@divma): This log is here because I'm not sure if dropping the challenges
+333            // without further interaction with the connection is going to cause issues.
+334            for (token, remote) in self.active_probes.drain() {
+335                let remote: SocketAddr = remote.into();
+336                trace!(token=format!("{:08x}", token), %remote, "dropping nat traversal challenge pending response");
+337            }
+338        } else if self.pending_probes.len() >= self.max_remote_addresses {
+339            return Err(Error::TooManyAddresses);
+340        }
+341        self.pending_probes.insert((ip, port));
+342        Ok(())
+343    }
+344
+345    pub(crate) fn next_probe(&mut self) -> Option<ServerProbing<'_>> {
+346        self.pending_probes
+347            .iter()
+348            .next()
+349            .copied()
+350            .map(|remote| ServerProbing {
+351                remote,
+352                pending_probes: &mut self.pending_probes,
+353                active_probes: &mut self.active_probes,
+354            })
+355    }
+356}
+357
+358pub(crate) struct ServerProbing<'a> {
+359    remote: IpPort,
+360    pending_probes: &'a mut FxHashSet<IpPort>,
+361    active_probes: &'a mut IntMap<u64, IpPort>,
+362}
+363
+364impl<'a> ServerProbing<'a> {
+365    pub(crate) fn finish(self, token: u64) {
+366        self.pending_probes.remove(&self.remote);
+367        self.active_probes.insert(token, self.remote);
+368    }
+369
+370    pub(crate) fn remote(&self) -> SocketAddr {
+371        self.remote.into()
+372    }
+373}
+374
+375impl State {
+376    pub(crate) fn new(max_remote_addresses: u8, max_local_addresses: u8, side: Side) -> Self {
+377        match side {
+378            Side::Client => Self::ClientSide(ClientState::new(
+379                max_remote_addresses.into(),
+380                max_local_addresses.into(),
+381            )),
+382            Side::Server => Self::ServerSide(ServerState::new(
+383                max_remote_addresses.into(),
+384                max_local_addresses.into(),
+385            )),
+386        }
+387    }
+388
+389    pub(crate) fn client_side(&self) -> Result<&ClientState, Error> {
+390        match self {
+391            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+392            Self::ClientSide(client_side) => Ok(client_side),
+393            Self::ServerSide(_) => Err(Error::WrongConnectionSide),
+394        }
+395    }
+396
+397    pub(crate) fn client_side_mut(&mut self) -> Result<&mut ClientState, Error> {
+398        match self {
+399            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+400            Self::ClientSide(client_side) => Ok(client_side),
+401            Self::ServerSide(_) => Err(Error::WrongConnectionSide),
+402        }
+403    }
+404
+405    pub(crate) fn server_side_mut(&mut self) -> Result<&mut ServerState, Error> {
+406        match self {
+407            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+408            Self::ClientSide(_) => Err(Error::WrongConnectionSide),
+409            Self::ServerSide(server_side) => Ok(server_side),
+410        }
+411    }
+412
+413    /// Adds a local address to use for nat traversal.
+414    ///
+415    /// When this endpoint is the server within the connection, these addresses will be sent to the
+416    /// client in add address frames. For clients, these addresses will be sent in reach out frames
+417    /// when nat traversal attempts are initiated.
+418    ///
+419    /// If a frame should be sent, it is returned.
+420    pub(crate) fn add_local_address(
+421        &mut self,
+422        address: SocketAddr,
+423    ) -> Result<Option<AddAddress>, Error> {
+424        match self {
+425            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+426            Self::ClientSide(client_state) => {
+427                client_state.add_local_address((address.ip(), address.port()))?;
+428                Ok(None)
+429            }
+430            Self::ServerSide(server_state) => {
+431                server_state.add_local_address((address.ip(), address.port()))
+432            }
+433        }
+434    }
+435
+436    /// Removes a local address from the advertised set for nat traversal.
+437    ///
+438    /// When this endpoint is the server, removed addresses must be reported with remove address
+439    /// frames. Clients will simply stop reporting these addresses in reach out frames.
+440    ///
+441    /// If a frame should be sent, it is returned.
+442    pub(crate) fn remove_local_address(
+443        &mut self,
+444        address: SocketAddr,
+445    ) -> Result<Option<RemoveAddress>, Error> {
+446        let address = &(address.ip(), address.port());
+447        match self {
+448            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+449            Self::ClientSide(client_state) => {
+450                client_state.remove_local_address(address);
+451                Ok(None)
+452            }
+453            Self::ServerSide(server_state) => Ok(server_state.remove_local_address(address)),
+454        }
+455    }
+456
+457    pub(crate) fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, Error> {
+458        match self {
+459            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+460            Self::ClientSide(client_state) => Ok(client_state
+461                .local_addresses
+462                .iter()
+463                .copied()
+464                .map(Into::into)
+465                .collect()),
+466            Self::ServerSide(server_state) => Ok(server_state
+467                .local_addresses
+468                .keys()
+469                .copied()
+470                .map(Into::into)
+471                .collect()),
+472        }
+473    }
+474}
+475
+476#[cfg(test)]
+477mod tests {
+478    use super::*;
+479
+480    #[test]
+481    fn test_basic_server_state() {
+482        let mut state = ServerState::new(2, 2);
+483
+484        state
+485            .handle_reach_out(ReachOut {
+486                round: 1u32.into(),
+487                ip: std::net::Ipv4Addr::LOCALHOST.into(),
+488                port: 1,
+489            })
+490            .unwrap();
+491
+492        state
+493            .handle_reach_out(ReachOut {
+494                round: 1u32.into(),
+495                ip: "1.1.1.1".parse().unwrap(), //std::net::Ipv4Addr::LOCALHOST.into(),
+496                port: 2,
+497            })
+498            .unwrap();
+499
+500        dbg!(&state);
+501        assert_eq!(state.pending_probes.len(), 2);
+502        assert_eq!(state.active_probes.len(), 0);
+503
+504        let probe = state.next_probe().unwrap();
+505        probe.finish(1);
+506        let probe = state.next_probe().unwrap();
+507        probe.finish(2);
+508
+509        assert!(state.next_probe().is_none());
+510        assert_eq!(state.pending_probes.len(), 0);
+511        assert_eq!(state.active_probes.len(), 2);
+512    }
+513}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/lib.rs.html b/pr/299/docs/src/iroh_quinn_proto/lib.rs.html new file mode 100644 index 000000000..9adce6ac7 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/lib.rs.html @@ -0,0 +1,417 @@ +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, PathError, PathEvent, PathId,
+48    PathStats, PathStatus, ReadError, ReadableError, RecvStream, RttEstimator, SendDatagramError,
+49    SendStream, SetPathStatusError, ShouldTransmit, StreamEvent, Streams, UdpStats, WriteError,
+50    Written,
+51};
+52
+53#[cfg(feature = "rustls")]
+54pub use rustls;
+55
+56mod config;
+57#[cfg(doc)]
+58pub use config::DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED;
+59pub use config::{
+60    AckFrequencyConfig, ClientConfig, ConfigError, EndpointConfig, IdleTimeout, MtuDiscoveryConfig,
+61    ServerConfig, StdSystemTime, TimeSource, TransportConfig, ValidationTokenConfig,
+62};
+63#[cfg(feature = "qlog")]
+64pub use config::{QlogConfig, QlogFactory, QlogFileFactory};
+65
+66pub mod crypto;
+67
+68mod frame;
+69pub use crate::frame::{ApplicationClose, ConnectionClose, Datagram, FrameType};
+70use crate::{
+71    coding::{Decodable, Encodable},
+72    frame::Frame,
+73};
+74
+75mod endpoint;
+76pub use crate::endpoint::{
+77    AcceptError, ConnectError, ConnectionHandle, DatagramEvent, Endpoint, Incoming, RetryError,
+78};
+79
+80mod packet;
+81pub use packet::{
+82    ConnectionIdParser, FixedLengthConnectionIdParser, LongType, PacketDecodeError, PartialDecode,
+83    ProtectedHeader, ProtectedInitialHeader,
+84};
+85
+86mod shared;
+87pub use crate::shared::{ConnectionEvent, ConnectionId, EcnCodepoint, EndpointEvent};
+88
+89mod transport_error;
+90pub use crate::transport_error::{Code as TransportErrorCode, Error as TransportError};
+91
+92pub mod congestion;
+93
+94mod cid_generator;
+95pub use crate::cid_generator::{
+96    ConnectionIdGenerator, HashedConnectionIdGenerator, InvalidCid, RandomConnectionIdGenerator,
+97};
+98
+99mod token;
+100use token::ResetToken;
+101pub use token::{NoneTokenLog, NoneTokenStore, TokenLog, TokenReuseError, TokenStore};
+102
+103mod address_discovery;
+104
+105mod token_memory_cache;
+106pub use token_memory_cache::TokenMemoryCache;
+107
+108pub mod iroh_hp;
+109
+110#[cfg(feature = "arbitrary")]
+111use arbitrary::Arbitrary;
+112
+113// Deal with time
+114#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
+115pub(crate) use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
+116#[cfg(all(target_family = "wasm", target_os = "unknown"))]
+117pub(crate) use web_time::{Duration, Instant, SystemTime, UNIX_EPOCH};
+118
+119#[cfg(feature = "bench")]
+120pub mod bench_exports {
+121    //! Exports for benchmarks
+122    pub use crate::connection::send_buffer::send_buffer_benches;
+123}
+124
+125#[cfg(fuzzing)]
+126pub mod fuzzing {
+127    pub use crate::connection::{Retransmits, State as ConnectionState, StreamsState};
+128    pub use crate::frame::ResetStream;
+129    pub use crate::packet::PartialDecode;
+130    pub use crate::transport_parameters::TransportParameters;
+131    pub use bytes::{BufMut, BytesMut};
+132
+133    #[cfg(feature = "arbitrary")]
+134    use arbitrary::{Arbitrary, Result, Unstructured};
+135
+136    #[cfg(feature = "arbitrary")]
+137    impl<'arbitrary> Arbitrary<'arbitrary> for TransportParameters {
+138        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
+139            Ok(Self {
+140                initial_max_streams_bidi: u.arbitrary()?,
+141                initial_max_streams_uni: u.arbitrary()?,
+142                ack_delay_exponent: u.arbitrary()?,
+143                max_udp_payload_size: u.arbitrary()?,
+144                ..Self::default()
+145            })
+146        }
+147    }
+148
+149    #[derive(Debug)]
+150    pub struct PacketParams {
+151        pub local_cid_len: usize,
+152        pub buf: BytesMut,
+153        pub grease_quic_bit: bool,
+154    }
+155
+156    #[cfg(feature = "arbitrary")]
+157    impl<'arbitrary> Arbitrary<'arbitrary> for PacketParams {
+158        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
+159            let local_cid_len: usize = u.int_in_range(0..=crate::MAX_CID_SIZE)?;
+160            let bytes: Vec<u8> = Vec::arbitrary(u)?;
+161            let mut buf = BytesMut::new();
+162            buf.put_slice(&bytes[..]);
+163            Ok(Self {
+164                local_cid_len,
+165                buf,
+166                grease_quic_bit: bool::arbitrary(u)?,
+167            })
+168        }
+169    }
+170}
+171
+172/// The QUIC protocol version implemented.
+173pub const DEFAULT_SUPPORTED_VERSIONS: &[u32] = &[
+174    0x00000001,
+175    0xff00_001d,
+176    0xff00_001e,
+177    0xff00_001f,
+178    0xff00_0020,
+179    0xff00_0021,
+180    0xff00_0022,
+181];
+182
+183/// Whether an endpoint was the initiator of a connection
+184#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
+185#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+186pub enum Side {
+187    /// The initiator of a connection
+188    Client = 0,
+189    /// The acceptor of a connection
+190    Server = 1,
+191}
+192
+193impl Side {
+194    #[inline]
+195    /// Shorthand for `self == Side::Client`
+196    pub fn is_client(self) -> bool {
+197        self == Self::Client
+198    }
+199
+200    #[inline]
+201    /// Shorthand for `self == Side::Server`
+202    pub fn is_server(self) -> bool {
+203        self == Self::Server
+204    }
+205}
+206
+207impl ops::Not for Side {
+208    type Output = Self;
+209    fn not(self) -> Self {
+210        match self {
+211            Self::Client => Self::Server,
+212            Self::Server => Self::Client,
+213        }
+214    }
+215}
+216
+217/// Whether a stream communicates data in both directions or only from the initiator
+218#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
+219#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+220pub enum Dir {
+221    /// Data flows in both directions
+222    Bi = 0,
+223    /// Data flows only from the stream's initiator
+224    Uni = 1,
+225}
+226
+227impl Dir {
+228    fn iter() -> impl Iterator<Item = Self> {
+229        [Self::Bi, Self::Uni].iter().cloned()
+230    }
+231}
+232
+233impl fmt::Display for Dir {
+234    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+235        use Dir::*;
+236        f.pad(match *self {
+237            Bi => "bidirectional",
+238            Uni => "unidirectional",
+239        })
+240    }
+241}
+242
+243/// Identifier for a stream within a particular connection
+244#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
+245#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+246pub struct StreamId(u64);
+247
+248impl fmt::Display for StreamId {
+249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+250        let initiator = match self.initiator() {
+251            Side::Client => "client",
+252            Side::Server => "server",
+253        };
+254        let dir = match self.dir() {
+255            Dir::Uni => "uni",
+256            Dir::Bi => "bi",
+257        };
+258        write!(
+259            f,
+260            "{} {}directional stream {}",
+261            initiator,
+262            dir,
+263            self.index()
+264        )
+265    }
+266}
+267
+268impl StreamId {
+269    /// Create a new StreamId
+270    pub fn new(initiator: Side, dir: Dir, index: u64) -> Self {
+271        Self((index << 2) | ((dir as u64) << 1) | initiator as u64)
+272    }
+273    /// Which side of a connection initiated the stream
+274    pub fn initiator(self) -> Side {
+275        if self.0 & 0x1 == 0 {
+276            Side::Client
+277        } else {
+278            Side::Server
+279        }
+280    }
+281    /// Which directions data flows in
+282    pub fn dir(self) -> Dir {
+283        if self.0 & 0x2 == 0 { Dir::Bi } else { Dir::Uni }
+284    }
+285    /// Distinguishes streams of the same initiator and directionality
+286    pub fn index(self) -> u64 {
+287        self.0 >> 2
+288    }
+289}
+290
+291impl From<StreamId> for VarInt {
+292    fn from(x: StreamId) -> Self {
+293        unsafe { Self::from_u64_unchecked(x.0) }
+294    }
+295}
+296
+297impl From<VarInt> for StreamId {
+298    fn from(v: VarInt) -> Self {
+299        Self(v.0)
+300    }
+301}
+302
+303impl From<StreamId> for u64 {
+304    fn from(x: StreamId) -> Self {
+305        x.0
+306    }
+307}
+308
+309impl Decodable for StreamId {
+310    fn decode<B: bytes::Buf>(buf: &mut B) -> coding::Result<Self> {
+311        VarInt::decode(buf).map(|x| Self(x.into_inner()))
+312    }
+313}
+314impl Encodable for StreamId {
+315    fn encode<B: bytes::BufMut>(&self, buf: &mut B) {
+316        VarInt::from_u64(self.0).unwrap().encode(buf);
+317    }
+318}
+319
+320/// An outgoing packet
+321#[derive(Debug)]
+322#[must_use]
+323pub struct Transmit {
+324    /// The socket this datagram should be sent to
+325    pub destination: SocketAddr,
+326    /// Explicit congestion notification bits to set on the packet
+327    pub ecn: Option<EcnCodepoint>,
+328    /// Amount of data written to the caller-supplied buffer
+329    pub size: usize,
+330    /// The segment size if this transmission contains multiple datagrams.
+331    /// This is `None` if the transmit only contains a single datagram
+332    pub segment_size: Option<usize>,
+333    /// Optional source IP address for the datagram
+334    pub src_ip: Option<IpAddr>,
+335}
+336
+337//
+338// Useful internal constants
+339//
+340
+341/// The maximum number of CIDs we bother to issue per path
+342const LOC_CID_COUNT: u64 = 12;
+343const RESET_TOKEN_SIZE: usize = 16;
+344const MAX_CID_SIZE: usize = 20;
+345const MIN_INITIAL_SIZE: u16 = 1200;
+346/// <https://www.rfc-editor.org/rfc/rfc9000.html#name-datagram-size>
+347const INITIAL_MTU: u16 = 1200;
+348const MAX_UDP_PAYLOAD: u16 = 65527;
+349const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
+350/// Maximum number of streams that can be uniquely identified by a stream ID
+351const MAX_STREAM_COUNT: u64 = 1 << 60;
+352
+353/// Identifies a network path by the combination of remote and local addresses
+354///
+355/// Including the local ensures good behavior when the host has multiple IP addresses on the same
+356/// subnet and zero-length connection IDs are in use or when multipath is enabled and multiple
+357/// paths exist with the same remote, but different local IP interfaces.
+358#[derive(Hash, Eq, PartialEq, Copy, Clone)]
+359pub struct FourTuple {
+360    /// The remote side of this tuple
+361    pub remote: SocketAddr,
+362    /// The local side of this tuple.
+363    ///
+364    /// The socket is irrelevant for our intents and purposes:
+365    /// When we send, we can only specify the `src_ip`, not the source port.
+366    /// So even if we track the port, we won't be able to make use of it.
+367    pub local_ip: Option<IpAddr>,
+368}
+369
+370impl FourTuple {
+371    /// Returns whether we think the other address probably represents the same path
+372    /// as ours.
+373    ///
+374    /// If we have a local IP set, then we're exact and only match if the 4-tuples are
+375    /// exactly equal.
+376    /// If we don't have a local IP set, then we only check the remote addresses for equality.
+377    pub fn is_probably_same_path(&self, other: &Self) -> bool {
+378        self.remote == other.remote && (self.local_ip.is_none() || self.local_ip == other.local_ip)
+379    }
+380
+381    /// Updates this tuple's local address iff
+382    /// - it was unset before,
+383    /// - the other tuple has the same remote, and
+384    /// - the other tuple has a local address set.
+385    ///
+386    /// Returns whether this and the other remote are now fully equal.
+387    pub fn update_local_if_same_remote(&mut self, other: &Self) -> bool {
+388        if self.remote != other.remote {
+389            return false;
+390        }
+391        if self.local_ip.is_some() && self.local_ip != other.local_ip {
+392            return false;
+393        }
+394        self.local_ip = other.local_ip;
+395        true
+396    }
+397}
+398
+399impl fmt::Display for FourTuple {
+400    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+401        f.write_str("(")?;
+402        if let Some(local_ip) = &self.local_ip {
+403            local_ip.fmt(f)?;
+404            f.write_str(", ")?;
+405        } else {
+406            f.write_str("<unknown>, ")?;
+407        }
+408        self.remote.fmt(f)?;
+409        f.write_str(")")
+410    }
+411}
+412
+413impl fmt::Debug for FourTuple {
+414    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+415        fmt::Display::fmt(&self, f)
+416    }
+417}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/packet.rs.html b/pr/299/docs/src/iroh_quinn_proto/packet.rs.html new file mode 100644 index 000000000..297af0dfa --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/packet.rs.html @@ -0,0 +1,1053 @@ +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    /// Whether the payload of this packet contains QUIC frames
+468    pub(crate) fn has_frames(&self) -> bool {
+469        use Header::*;
+470        match *self {
+471            Initial(_) => true,
+472            Long { .. } => true,
+473            Retry { .. } => false,
+474            Short { .. } => true,
+475            VersionNegotiate { .. } => false,
+476        }
+477    }
+478
+479    #[cfg(feature = "qlog")]
+480    pub(crate) fn src_cid(&self) -> Option<ConnectionId> {
+481        match self {
+482            Self::Initial(initial_header) => Some(initial_header.src_cid),
+483            Self::Long { src_cid, .. } => Some(*src_cid),
+484            Self::Retry { src_cid, .. } => Some(*src_cid),
+485            Self::Short { .. } => None,
+486            Self::VersionNegotiate { src_cid, .. } => Some(*src_cid),
+487        }
+488    }
+489}
+490
+491pub(crate) struct PartialEncode {
+492    pub(crate) start: usize,
+493    pub(crate) header_len: usize,
+494    // Packet number length, payload length needed
+495    pn: Option<(usize, bool)>,
+496}
+497
+498impl PartialEncode {
+499    pub(crate) fn finish(
+500        self,
+501        buf: &mut [u8],
+502        header_crypto: &dyn crypto::HeaderKey,
+503        crypto: Option<(u64, PathId, &dyn crypto::PacketKey)>,
+504    ) {
+505        let Self { header_len, pn, .. } = self;
+506        let (pn_len, write_len) = match pn {
+507            Some((pn_len, write_len)) => (pn_len, write_len),
+508            None => return,
+509        };
+510
+511        let pn_pos = header_len - pn_len;
+512        if write_len {
+513            let len = buf.len() - header_len + pn_len;
+514            assert!(len < 2usize.pow(14)); // Fits in reserved space
+515            let mut slice = &mut buf[pn_pos - 2..pn_pos];
+516            slice.put_u16(len as u16 | (0b01 << 14));
+517        }
+518
+519        if let Some((packet_number, path_id, crypto)) = crypto {
+520            crypto.encrypt(path_id, packet_number, buf, header_len);
+521        }
+522
+523        debug_assert!(
+524            pn_pos + 4 + header_crypto.sample_size() <= buf.len(),
+525            "packet must be padded to at least {} bytes for header protection sampling",
+526            pn_pos + 4 + header_crypto.sample_size()
+527        );
+528        header_crypto.encrypt(pn_pos, buf);
+529    }
+530}
+531
+532/// Plain packet header
+533#[derive(Clone, Debug)]
+534pub enum ProtectedHeader {
+535    /// An Initial packet header
+536    Initial(ProtectedInitialHeader),
+537    /// A Long packet header, as used during the handshake
+538    Long {
+539        /// Type of the Long header packet
+540        ty: LongType,
+541        /// Destination Connection ID
+542        dst_cid: ConnectionId,
+543        /// Source Connection ID
+544        src_cid: ConnectionId,
+545        /// Length of the packet payload
+546        len: u64,
+547        /// QUIC version
+548        version: u32,
+549    },
+550    /// A Retry packet header
+551    Retry {
+552        /// Destination Connection ID
+553        dst_cid: ConnectionId,
+554        /// Source Connection ID
+555        src_cid: ConnectionId,
+556        /// QUIC version
+557        version: u32,
+558    },
+559    /// A short packet header, as used during the data phase
+560    Short {
+561        /// Spin bit
+562        spin: bool,
+563        /// Destination Connection ID
+564        dst_cid: ConnectionId,
+565    },
+566    /// A Version Negotiation packet header
+567    VersionNegotiate {
+568        /// Random value
+569        random: u8,
+570        /// Destination Connection ID
+571        dst_cid: ConnectionId,
+572        /// Source Connection ID
+573        src_cid: ConnectionId,
+574    },
+575}
+576
+577impl ProtectedHeader {
+578    fn as_initial(&self) -> Option<&ProtectedInitialHeader> {
+579        match self {
+580            Self::Initial(x) => Some(x),
+581            _ => None,
+582        }
+583    }
+584
+585    /// The destination Connection ID of the packet
+586    pub fn dst_cid(&self) -> ConnectionId {
+587        use ProtectedHeader::*;
+588        match self {
+589            Initial(header) => header.dst_cid,
+590            &Long { dst_cid, .. } => dst_cid,
+591            &Retry { dst_cid, .. } => dst_cid,
+592            &Short { dst_cid, .. } => dst_cid,
+593            &VersionNegotiate { dst_cid, .. } => dst_cid,
+594        }
+595    }
+596
+597    fn payload_len(&self) -> Option<u64> {
+598        use ProtectedHeader::*;
+599        match self {
+600            Initial(ProtectedInitialHeader { len, .. }) | Long { len, .. } => Some(*len),
+601            _ => None,
+602        }
+603    }
+604
+605    /// Decode a plain header from given buffer, with given [`ConnectionIdParser`].
+606    pub fn decode(
+607        buf: &mut io::Cursor<BytesMut>,
+608        cid_parser: &(impl ConnectionIdParser + ?Sized),
+609        supported_versions: &[u32],
+610        grease_quic_bit: bool,
+611    ) -> Result<Self, PacketDecodeError> {
+612        let first = buf.get::<u8>()?;
+613        if !grease_quic_bit && first & FIXED_BIT == 0 {
+614            return Err(PacketDecodeError::InvalidHeader("fixed bit unset"));
+615        }
+616        if first & LONG_HEADER_FORM == 0 {
+617            let spin = first & SPIN_BIT != 0;
+618
+619            Ok(Self::Short {
+620                spin,
+621                dst_cid: cid_parser.parse(buf)?,
+622            })
+623        } else {
+624            let version = buf.get::<u32>()?;
+625
+626            let dst_cid = ConnectionId::decode_long(buf)
+627                .ok_or(PacketDecodeError::InvalidHeader("malformed cid"))?;
+628            let src_cid = ConnectionId::decode_long(buf)
+629                .ok_or(PacketDecodeError::InvalidHeader("malformed cid"))?;
+630
+631            // TODO: Support long CIDs for compatibility with future QUIC versions
+632            if version == 0 {
+633                let random = first & !LONG_HEADER_FORM;
+634                return Ok(Self::VersionNegotiate {
+635                    random,
+636                    dst_cid,
+637                    src_cid,
+638                });
+639            }
+640
+641            if !supported_versions.contains(&version) {
+642                return Err(PacketDecodeError::UnsupportedVersion {
+643                    src_cid,
+644                    dst_cid,
+645                    version,
+646                });
+647            }
+648
+649            match LongHeaderType::from_byte(first)? {
+650                LongHeaderType::Initial => {
+651                    let token_len = buf.get_var()? as usize;
+652                    let token_start = buf.position() as usize;
+653                    if token_len > buf.remaining() {
+654                        return Err(PacketDecodeError::InvalidHeader("token out of bounds"));
+655                    }
+656                    buf.advance(token_len);
+657
+658                    let len = buf.get_var()?;
+659                    Ok(Self::Initial(ProtectedInitialHeader {
+660                        dst_cid,
+661                        src_cid,
+662                        token_pos: token_start..token_start + token_len,
+663                        len,
+664                        version,
+665                    }))
+666                }
+667                LongHeaderType::Retry => Ok(Self::Retry {
+668                    dst_cid,
+669                    src_cid,
+670                    version,
+671                }),
+672                LongHeaderType::Standard(ty) => Ok(Self::Long {
+673                    ty,
+674                    dst_cid,
+675                    src_cid,
+676                    len: buf.get_var()?,
+677                    version,
+678                }),
+679            }
+680        }
+681    }
+682}
+683
+684/// Header of an Initial packet, before decryption
+685#[derive(Clone, Debug)]
+686pub struct ProtectedInitialHeader {
+687    /// Destination Connection ID
+688    pub dst_cid: ConnectionId,
+689    /// Source Connection ID
+690    pub src_cid: ConnectionId,
+691    /// The position of a token in the packet buffer
+692    pub token_pos: Range<usize>,
+693    /// Length of the packet payload
+694    pub len: u64,
+695    /// QUIC version
+696    pub version: u32,
+697}
+698
+699#[derive(Clone, Debug)]
+700pub(crate) struct InitialHeader {
+701    pub(crate) dst_cid: ConnectionId,
+702    pub(crate) src_cid: ConnectionId,
+703    pub(crate) token: Bytes,
+704    pub(crate) number: PacketNumber,
+705    pub(crate) version: u32,
+706}
+707
+708// An encoded packet number
+709#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+710pub(crate) enum PacketNumber {
+711    U8(u8),
+712    U16(u16),
+713    U24(u32),
+714    U32(u32),
+715}
+716
+717impl PacketNumber {
+718    pub(crate) fn new(n: u64, largest_acked: u64) -> Self {
+719        let range = (n - largest_acked) * 2;
+720        if range < 1 << 8 {
+721            Self::U8(n as u8)
+722        } else if range < 1 << 16 {
+723            Self::U16(n as u16)
+724        } else if range < 1 << 24 {
+725            Self::U24(n as u32)
+726        } else if range < 1 << 32 {
+727            Self::U32(n as u32)
+728        } else {
+729            panic!("packet number too large to encode")
+730        }
+731    }
+732
+733    pub(crate) fn len(self) -> usize {
+734        use PacketNumber::*;
+735        match self {
+736            U8(_) => 1,
+737            U16(_) => 2,
+738            U24(_) => 3,
+739            U32(_) => 4,
+740        }
+741    }
+742
+743    pub(crate) fn encode<W: BufMut>(self, w: &mut W) {
+744        use PacketNumber::*;
+745        match self {
+746            U8(x) => w.write(x),
+747            U16(x) => w.write(x),
+748            U24(x) => w.put_uint(u64::from(x), 3),
+749            U32(x) => w.write(x),
+750        }
+751    }
+752
+753    pub(crate) fn decode<R: Buf>(len: usize, r: &mut R) -> Result<Self, PacketDecodeError> {
+754        use PacketNumber::*;
+755        let pn = match len {
+756            1 => U8(r.get()?),
+757            2 => U16(r.get()?),
+758            3 => U24(r.get_uint(3) as u32),
+759            4 => U32(r.get()?),
+760            _ => unreachable!(),
+761        };
+762        Ok(pn)
+763    }
+764
+765    pub(crate) fn decode_len(tag: u8) -> usize {
+766        1 + (tag & 0x03) as usize
+767    }
+768
+769    fn tag(self) -> u8 {
+770        use PacketNumber::*;
+771        match self {
+772            U8(_) => 0b00,
+773            U16(_) => 0b01,
+774            U24(_) => 0b10,
+775            U32(_) => 0b11,
+776        }
+777    }
+778
+779    pub(crate) fn expand(self, expected: u64) -> u64 {
+780        // From Appendix A
+781        use PacketNumber::*;
+782        let truncated = match self {
+783            U8(x) => u64::from(x),
+784            U16(x) => u64::from(x),
+785            U24(x) => u64::from(x),
+786            U32(x) => u64::from(x),
+787        };
+788        let nbits = self.len() * 8;
+789        let win = 1 << nbits;
+790        let hwin = win / 2;
+791        let mask = win - 1;
+792        // The incoming packet number should be greater than expected - hwin and less than or equal
+793        // to expected + hwin
+794        //
+795        // This means we can't just strip the trailing bits from expected and add the truncated
+796        // because that might yield a value outside the window.
+797        //
+798        // The following code calculates a candidate value and makes sure it's within the packet
+799        // number window.
+800        let candidate = (expected & !mask) | truncated;
+801        if expected.checked_sub(hwin).is_some_and(|x| candidate <= x) {
+802            candidate + win
+803        } else if candidate > expected + hwin && candidate > win {
+804            candidate - win
+805        } else {
+806            candidate
+807        }
+808    }
+809}
+810
+811/// A [`ConnectionIdParser`] implementation that assumes the connection ID is of fixed length
+812pub struct FixedLengthConnectionIdParser {
+813    expected_len: usize,
+814}
+815
+816impl FixedLengthConnectionIdParser {
+817    /// Create a new instance of `FixedLengthConnectionIdParser`
+818    pub fn new(expected_len: usize) -> Self {
+819        Self { expected_len }
+820    }
+821}
+822
+823impl ConnectionIdParser for FixedLengthConnectionIdParser {
+824    fn parse(&self, buffer: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError> {
+825        (buffer.remaining() >= self.expected_len)
+826            .then(|| ConnectionId::from_buf(buffer, self.expected_len))
+827            .ok_or(PacketDecodeError::InvalidHeader("packet too small"))
+828    }
+829}
+830
+831/// Parse connection id in short header packet
+832pub trait ConnectionIdParser {
+833    /// Parse a connection id from given buffer
+834    fn parse(&self, buf: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError>;
+835}
+836
+837/// Long packet type including non-uniform cases
+838#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+839pub(crate) enum LongHeaderType {
+840    Initial,
+841    Retry,
+842    Standard(LongType),
+843}
+844
+845impl LongHeaderType {
+846    fn from_byte(b: u8) -> Result<Self, PacketDecodeError> {
+847        use {LongHeaderType::*, LongType::*};
+848        debug_assert!(b & LONG_HEADER_FORM != 0, "not a long packet");
+849        Ok(match (b & 0x30) >> 4 {
+850            0x0 => Initial,
+851            0x1 => Standard(ZeroRtt),
+852            0x2 => Standard(Handshake),
+853            0x3 => Retry,
+854            _ => unreachable!(),
+855        })
+856    }
+857}
+858
+859impl From<LongHeaderType> for u8 {
+860    fn from(ty: LongHeaderType) -> Self {
+861        use {LongHeaderType::*, LongType::*};
+862        match ty {
+863            Initial => LONG_HEADER_FORM | FIXED_BIT,
+864            Standard(ZeroRtt) => LONG_HEADER_FORM | FIXED_BIT | (0x1 << 4),
+865            Standard(Handshake) => LONG_HEADER_FORM | FIXED_BIT | (0x2 << 4),
+866            Retry => LONG_HEADER_FORM | FIXED_BIT | (0x3 << 4),
+867        }
+868    }
+869}
+870
+871/// Long packet types with uniform header structure
+872#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+873pub enum LongType {
+874    /// Handshake packet
+875    Handshake,
+876    /// 0-RTT packet
+877    ZeroRtt,
+878}
+879
+880/// Packet decode error
+881#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+882pub enum PacketDecodeError {
+883    /// Packet uses a QUIC version that is not supported
+884    #[error("unsupported version {version:x}")]
+885    UnsupportedVersion {
+886        /// Source Connection ID
+887        src_cid: ConnectionId,
+888        /// Destination Connection ID
+889        dst_cid: ConnectionId,
+890        /// The version that was unsupported
+891        version: u32,
+892    },
+893    /// The packet header is invalid
+894    #[error("invalid header: {0}")]
+895    InvalidHeader(&'static str),
+896}
+897
+898impl From<coding::UnexpectedEnd> for PacketDecodeError {
+899    fn from(_: coding::UnexpectedEnd) -> Self {
+900        Self::InvalidHeader("unexpected end of packet")
+901    }
+902}
+903
+904pub(crate) const LONG_HEADER_FORM: u8 = 0x80;
+905pub(crate) const FIXED_BIT: u8 = 0x40;
+906pub(crate) const SPIN_BIT: u8 = 0x20;
+907const SHORT_RESERVED_BITS: u8 = 0x18;
+908const LONG_RESERVED_BITS: u8 = 0x0c;
+909const KEY_PHASE_BIT: u8 = 0x04;
+910
+911/// Packet number space identifiers
+912#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+913pub enum SpaceId {
+914    /// Unprotected packets, used to bootstrap the handshake
+915    Initial = 0,
+916    Handshake = 1,
+917    /// Application data space, used for 0-RTT and post-handshake/1-RTT packets
+918    Data = 2,
+919}
+920
+921impl SpaceId {
+922    pub fn iter() -> impl Iterator<Item = Self> {
+923        [Self::Initial, Self::Handshake, Self::Data].iter().cloned()
+924    }
+925
+926    /// Returns the next higher packet space.
+927    ///
+928    /// Keeps returning [`SpaceId::Data`] as the highest space.
+929    pub fn next(&self) -> Self {
+930        match self {
+931            Self::Initial => Self::Handshake,
+932            Self::Handshake => Self::Data,
+933            Self::Data => Self::Data,
+934        }
+935    }
+936}
+937
+938#[cfg(test)]
+939mod tests {
+940    use super::*;
+941    use hex_literal::hex;
+942    use std::io;
+943
+944    fn check_pn(typed: PacketNumber, encoded: &[u8]) {
+945        let mut buf = Vec::new();
+946        typed.encode(&mut buf);
+947        assert_eq!(&buf[..], encoded);
+948        let decoded = PacketNumber::decode(typed.len(), &mut io::Cursor::new(&buf)).unwrap();
+949        assert_eq!(typed, decoded);
+950    }
+951
+952    #[test]
+953    fn roundtrip_packet_numbers() {
+954        check_pn(PacketNumber::U8(0x7f), &hex!("7f"));
+955        check_pn(PacketNumber::U16(0x80), &hex!("0080"));
+956        check_pn(PacketNumber::U16(0x3fff), &hex!("3fff"));
+957        check_pn(PacketNumber::U32(0x0000_4000), &hex!("0000 4000"));
+958        check_pn(PacketNumber::U32(0xffff_ffff), &hex!("ffff ffff"));
+959    }
+960
+961    #[test]
+962    fn pn_encode() {
+963        check_pn(PacketNumber::new(0x10, 0), &hex!("10"));
+964        check_pn(PacketNumber::new(0x100, 0), &hex!("0100"));
+965        check_pn(PacketNumber::new(0x10000, 0), &hex!("010000"));
+966    }
+967
+968    #[test]
+969    fn pn_expand_roundtrip() {
+970        for expected in 0..1024 {
+971            for actual in expected..1024 {
+972                assert_eq!(actual, PacketNumber::new(actual, expected).expand(expected));
+973            }
+974        }
+975    }
+976
+977    #[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+978    #[test]
+979    fn header_encoding() {
+980        use crate::Side;
+981        use crate::crypto::rustls::{initial_keys, initial_suite_from_provider};
+982        #[cfg(all(feature = "rustls-aws-lc-rs", not(feature = "rustls-ring")))]
+983        use rustls::crypto::aws_lc_rs::default_provider;
+984        #[cfg(feature = "rustls-ring")]
+985        use rustls::crypto::ring::default_provider;
+986        use rustls::quic::Version;
+987
+988        let dcid = ConnectionId::new(&hex!("06b858ec6f80452b"));
+989        let provider = default_provider();
+990
+991        let suite = initial_suite_from_provider(&std::sync::Arc::new(provider)).unwrap();
+992        let client = initial_keys(Version::V1, dcid, Side::Client, &suite);
+993        let mut buf = Vec::new();
+994        let header = Header::Initial(InitialHeader {
+995            number: PacketNumber::U8(0),
+996            src_cid: ConnectionId::new(&[]),
+997            dst_cid: dcid,
+998            token: Bytes::new(),
+999            version: crate::DEFAULT_SUPPORTED_VERSIONS[0],
+1000        });
+1001        let encode = header.encode(&mut buf);
+1002        let header_len = buf.len();
+1003        buf.resize(header_len + 16 + client.packet.local.tag_len(), 0);
+1004        encode.finish(
+1005            &mut buf,
+1006            &*client.header.local,
+1007            Some((0, PathId::ZERO, &*client.packet.local)),
+1008        );
+1009
+1010        for byte in &buf {
+1011            print!("{byte:02x}");
+1012        }
+1013        println!();
+1014        assert_eq!(
+1015            buf[..],
+1016            hex!(
+1017                "c8000000010806b858ec6f80452b00004021be
+1018                 3ef50807b84191a196f760a6dad1e9d1c430c48952cba0148250c21c0a6a70e1"
+1019            )[..]
+1020        );
+1021
+1022        let server = initial_keys(Version::V1, dcid, Side::Server, &suite);
+1023        let supported_versions = crate::DEFAULT_SUPPORTED_VERSIONS.to_vec();
+1024        let decode = PartialDecode::new(
+1025            buf.as_slice().into(),
+1026            &FixedLengthConnectionIdParser::new(0),
+1027            &supported_versions,
+1028            false,
+1029        )
+1030        .unwrap()
+1031        .0;
+1032        let mut packet = decode.finish(Some(&*server.header.remote)).unwrap();
+1033        assert_eq!(
+1034            packet.header_data[..],
+1035            hex!("c0000000010806b858ec6f80452b0000402100")[..]
+1036        );
+1037        server
+1038            .packet
+1039            .remote
+1040            .decrypt(PathId::ZERO, 0, &packet.header_data, &mut packet.payload)
+1041            .unwrap();
+1042        assert_eq!(packet.payload[..], [0; 16]);
+1043        match packet.header {
+1044            Header::Initial(InitialHeader {
+1045                number: PacketNumber::U8(0),
+1046                ..
+1047            }) => {}
+1048            _ => {
+1049                panic!("unexpected header {:?}", packet.header);
+1050            }
+1051        }
+1052    }
+1053}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/range_set/array_range_set.rs.html b/pr/299/docs/src/iroh_quinn_proto/range_set/array_range_set.rs.html new file mode 100644 index 000000000..4afd5976c --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/range_set/array_range_set.rs.html @@ -0,0 +1,212 @@ +array_range_set.rs - source

iroh_quinn_proto/range_set/
array_range_set.rs

1use std::ops::Range;
+2
+3use tinyvec::TinyVec;
+4
+5/// A set of u64 values optimized for long runs and random insert/delete/contains
+6///
+7/// `ArrayRangeSet` uses an array representation, where each array entry represents
+8/// a range.
+9///
+10/// The array-based RangeSet provides 2 benefits:
+11/// - There exists an inline representation, which avoids the need of heap
+12///   allocating ACK ranges for SentFrames for small ranges.
+13/// - Iterating over ranges should usually be faster since there is only
+14///   a single cache-friendly contiguous range.
+15///
+16/// `ArrayRangeSet` is especially useful for tracking ACK ranges where the amount
+17/// of ranges is usually very low (since ACK numbers are in consecutive fashion
+18/// unless reordering or packet loss occur).
+19#[derive(Debug, Default)]
+20pub(crate) struct ArrayRangeSet(TinyVec<[Range<u64>; ARRAY_RANGE_SET_INLINE_CAPACITY]>);
+21
+22/// The capacity of elements directly stored in [`ArrayRangeSet`]
+23///
+24/// An inline capacity of 2 is chosen to keep `SentFrame` below 128 bytes.
+25const ARRAY_RANGE_SET_INLINE_CAPACITY: usize = 2;
+26
+27impl Clone for ArrayRangeSet {
+28    fn clone(&self) -> Self {
+29        // tinyvec keeps the heap representation after clones.
+30        // We rather prefer the inline representation for clones if possible,
+31        // since clones (e.g. for storage in `SentFrames`) are rarely mutated
+32        if self.0.is_inline() || self.0.len() > ARRAY_RANGE_SET_INLINE_CAPACITY {
+33            return Self(self.0.clone());
+34        }
+35
+36        let mut vec = TinyVec::new();
+37        vec.extend_from_slice(self.0.as_slice());
+38        Self(vec)
+39    }
+40}
+41
+42impl ArrayRangeSet {
+43    pub(crate) fn new() -> Self {
+44        Default::default()
+45    }
+46
+47    pub(crate) fn iter(&self) -> impl DoubleEndedIterator<Item = Range<u64>> + '_ {
+48        self.0.iter().cloned()
+49    }
+50
+51    pub(crate) fn elts(&self) -> impl Iterator<Item = u64> + '_ {
+52        self.iter().flatten()
+53    }
+54
+55    pub(crate) fn len(&self) -> usize {
+56        self.0.len()
+57    }
+58
+59    // TODO(@divma): consider removing
+60    #[allow(unused)]
+61    pub(crate) fn contains(&self, x: u64) -> bool {
+62        for range in self.0.iter() {
+63            if range.start > x {
+64                // We only get here if there was no prior range that contained x
+65                return false;
+66            } else if range.contains(&x) {
+67                return true;
+68            }
+69        }
+70        false
+71    }
+72
+73    pub(crate) fn iter_range(&self, range: Range<u64>) -> impl Iterator<Item = Range<u64>> + '_ {
+74        self.iter().filter_map(move |r| {
+75            if r.end > range.start && r.start < range.end {
+76                Some(r.start.max(range.start)..r.end.min(range.end))
+77            } else {
+78                None
+79            }
+80        })
+81    }
+82
+83    pub(crate) fn insert_one(&mut self, x: u64) -> bool {
+84        self.insert(x..x + 1)
+85    }
+86
+87    pub(crate) fn insert(&mut self, x: Range<u64>) -> bool {
+88        let mut result = false;
+89
+90        if x.is_empty() {
+91            // Don't try to deal with ranges where x.end <= x.start
+92            return false;
+93        }
+94
+95        let mut idx = 0;
+96        while idx != self.0.len() {
+97            let range = &mut self.0[idx];
+98
+99            if range.start > x.end {
+100                // The range is fully before this range and therefore not extensible.
+101                // Add a new range to the left
+102                self.0.insert(idx, x);
+103                return true;
+104            } else if range.start > x.start {
+105                // The new range starts before this range but overlaps.
+106                // Extend the current range to the left
+107                // Note that we don't have to merge a potential left range, since
+108                // this case would have been captured by merging the right range
+109                // in the previous loop iteration
+110                result = true;
+111                range.start = x.start;
+112            }
+113
+114            // At this point we have handled all parts of the new range which
+115            // are in front of the current range. Now we handle everything from
+116            // the start of the current range
+117
+118            if x.end <= range.end {
+119                // Fully contained
+120                return result;
+121            } else if x.start <= range.end {
+122                // Extend the current range to the end of the new range.
+123                // Since it's not contained it must be bigger
+124                range.end = x.end;
+125
+126                // Merge all follow-up ranges which overlap
+127                while idx != self.0.len() - 1 {
+128                    let curr = self.0[idx].clone();
+129                    let next = self.0[idx + 1].clone();
+130                    if curr.end >= next.start {
+131                        self.0[idx].end = next.end.max(curr.end);
+132                        self.0.remove(idx + 1);
+133                    } else {
+134                        break;
+135                    }
+136                }
+137
+138                return true;
+139            }
+140
+141            idx += 1;
+142        }
+143
+144        // Insert a range at the end
+145        self.0.push(x);
+146        true
+147    }
+148
+149    pub(crate) fn remove(&mut self, x: Range<u64>) -> bool {
+150        let mut result = false;
+151
+152        if x.is_empty() {
+153            // Don't try to deal with ranges where x.end <= x.start
+154            return false;
+155        }
+156
+157        let mut idx = 0;
+158        while idx != self.0.len() && x.start != x.end {
+159            let range = self.0[idx].clone();
+160
+161            if x.end <= range.start {
+162                // The range is fully before this range
+163                return result;
+164            } else if x.start >= range.end {
+165                // The range is fully after this range
+166                idx += 1;
+167                continue;
+168            }
+169
+170            // The range overlaps with this range
+171            result = true;
+172
+173            let left = range.start..x.start;
+174            let right = x.end..range.end;
+175            if left.is_empty() && right.is_empty() {
+176                self.0.remove(idx);
+177            } else if left.is_empty() {
+178                self.0[idx] = right;
+179                idx += 1;
+180            } else if right.is_empty() {
+181                self.0[idx] = left;
+182                idx += 1;
+183            } else {
+184                self.0[idx] = right;
+185                self.0.insert(idx, left);
+186                idx += 2;
+187            }
+188        }
+189
+190        result
+191    }
+192
+193    pub(crate) fn is_empty(&self) -> bool {
+194        self.0.is_empty()
+195    }
+196
+197    pub(crate) fn pop_min(&mut self) -> Option<Range<u64>> {
+198        if !self.0.is_empty() {
+199            Some(self.0.remove(0))
+200        } else {
+201            None
+202        }
+203    }
+204
+205    pub(crate) fn min(&self) -> Option<u64> {
+206        self.iter().next().map(|x| x.start)
+207    }
+208
+209    pub(crate) fn max(&self) -> Option<u64> {
+210        self.iter().next_back().map(|x| x.end - 1)
+211    }
+212}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/range_set/mod.rs.html b/pr/299/docs/src/iroh_quinn_proto/range_set/mod.rs.html new file mode 100644 index 000000000..d8326bb7f --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/shared.rs.html b/pr/299/docs/src/iroh_quinn_proto/shared.rs.html new file mode 100644 index 000000000..91679e137 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/shared.rs.html @@ -0,0 +1,199 @@ +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)]
+81pub struct ConnectionId {
+82    /// length of CID
+83    len: u8,
+84    /// CID in byte array
+85    bytes: [u8; MAX_CID_SIZE],
+86}
+87
+88impl ConnectionId {
+89    /// Construct cid from byte array
+90    pub fn new(bytes: &[u8]) -> Self {
+91        debug_assert!(bytes.len() <= MAX_CID_SIZE);
+92        let mut res = Self {
+93            len: bytes.len() as u8,
+94            bytes: [0; MAX_CID_SIZE],
+95        };
+96        res.bytes[..bytes.len()].copy_from_slice(bytes);
+97        res
+98    }
+99
+100    /// Constructs cid by reading `len` bytes from a `Buf`
+101    ///
+102    /// Callers need to assure that `buf.remaining() >= len`
+103    pub fn from_buf(buf: &mut (impl Buf + ?Sized), len: usize) -> Self {
+104        debug_assert!(len <= MAX_CID_SIZE);
+105        let mut res = Self {
+106            len: len as u8,
+107            bytes: [0; MAX_CID_SIZE],
+108        };
+109        buf.copy_to_slice(&mut res[..len]);
+110        res
+111    }
+112
+113    pub(crate) fn len(&self) -> usize {
+114        self.len as usize
+115    }
+116
+117    /// Decode from long header format
+118    pub(crate) fn decode_long(buf: &mut impl Buf) -> Option<Self> {
+119        let len = buf.get::<u8>().ok()? as usize;
+120        match len > MAX_CID_SIZE || buf.remaining() < len {
+121            false => Some(Self::from_buf(buf, len)),
+122            true => None,
+123        }
+124    }
+125
+126    /// Encode in long header format
+127    pub(crate) fn encode_long(&self, buf: &mut impl BufMut) {
+128        buf.put_u8(self.len() as u8);
+129        buf.put_slice(self);
+130    }
+131}
+132
+133impl ::std::ops::Deref for ConnectionId {
+134    type Target = [u8];
+135    fn deref(&self) -> &[u8] {
+136        &self.bytes[0..self.len as usize]
+137    }
+138}
+139
+140impl ::std::ops::DerefMut for ConnectionId {
+141    fn deref_mut(&mut self) -> &mut [u8] {
+142        &mut self.bytes[0..self.len as usize]
+143    }
+144}
+145
+146impl fmt::Debug for ConnectionId {
+147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+148        self.bytes[0..self.len as usize].fmt(f)
+149    }
+150}
+151
+152impl fmt::Display for ConnectionId {
+153    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+154        for byte in self.iter() {
+155            write!(f, "{byte:02x}")?;
+156        }
+157        Ok(())
+158    }
+159}
+160
+161/// Explicit congestion notification codepoint
+162#[repr(u8)]
+163#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+164pub enum EcnCodepoint {
+165    /// The ECT(0) codepoint, indicating that an endpoint is ECN-capable
+166    Ect0 = 0b10,
+167    /// The ECT(1) codepoint, indicating that an endpoint is ECN-capable
+168    Ect1 = 0b01,
+169    /// The CE codepoint, signalling that congestion was experienced
+170    Ce = 0b11,
+171}
+172
+173impl EcnCodepoint {
+174    /// Create new object from the given bits
+175    pub fn from_bits(x: u8) -> Option<Self> {
+176        use EcnCodepoint::*;
+177        Some(match x & 0b11 {
+178            0b10 => Ect0,
+179            0b01 => Ect1,
+180            0b11 => Ce,
+181            _ => {
+182                return None;
+183            }
+184        })
+185    }
+186
+187    /// Returns whether the codepoint is a CE, signalling that congestion was experienced
+188    pub fn is_ce(self) -> bool {
+189        matches!(self, Self::Ce)
+190    }
+191}
+192
+193#[derive(Debug, Copy, Clone)]
+194pub(crate) struct IssuedCid {
+195    pub(crate) path_id: PathId,
+196    pub(crate) sequence: u64,
+197    pub(crate) id: ConnectionId,
+198    pub(crate) reset_token: ResetToken,
+199}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/token.rs.html b/pr/299/docs/src/iroh_quinn_proto/token.rs.html new file mode 100644 index 000000000..ca26f97ef --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/token_memory_cache.rs.html b/pr/299/docs/src/iroh_quinn_proto/token_memory_cache.rs.html new file mode 100644 index 000000000..45007f185 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_proto/transport_error.rs.html b/pr/299/docs/src/iroh_quinn_proto/transport_error.rs.html new file mode 100644 index 000000000..068d92426 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/transport_error.rs.html @@ -0,0 +1,165 @@ +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)]
+66pub struct Code(u64);
+67
+68impl Code {
+69    /// Create QUIC error code from TLS alert code
+70    pub fn crypto(code: u8) -> Self {
+71        Self(0x100 | u64::from(code))
+72    }
+73}
+74
+75impl Decodable for Code {
+76    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+77        Ok(Self(buf.get_var()?))
+78    }
+79}
+80
+81impl Encodable for Code {
+82    fn encode<B: BufMut>(&self, buf: &mut B) {
+83        buf.write_var(self.0)
+84    }
+85}
+86
+87impl From<Code> for u64 {
+88    fn from(x: Code) -> Self {
+89        x.0
+90    }
+91}
+92
+93impl From<VarInt> for Code {
+94    fn from(value: VarInt) -> Self {
+95        Self(value.0)
+96    }
+97}
+98
+99impl From<Code> for VarInt {
+100    fn from(value: Code) -> Self {
+101        Self(value.0)
+102    }
+103}
+104
+105macro_rules! errors {
+106    {$($name:ident($val:expr) $desc:expr;)*} => {
+107        #[allow(non_snake_case, unused)]
+108        impl Error {
+109            $(
+110            pub(crate) fn $name<T>(reason: T) -> Self where T: Into<String> {
+111                Self::new(Code::$name, reason.into())
+112            }
+113            )*
+114        }
+115
+116        impl Code {
+117            $(#[doc = $desc] pub const $name: Self = Code($val);)*
+118        }
+119
+120        impl fmt::Debug for Code {
+121            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+122                match self.0 {
+123                    $($val => f.write_str(stringify!($name)),)*
+124                    x if (0x100..0x200).contains(&x) => write!(f, "Code::crypto({:02x})", self.0 as u8),
+125                    _ => write!(f, "Code({:x})", self.0),
+126                }
+127            }
+128        }
+129
+130        impl fmt::Display for Code {
+131            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+132                match self.0 {
+133                    $($val => f.write_str($desc),)*
+134                    // We're trying to be abstract over the crypto protocol, so human-readable descriptions here is tricky.
+135                    _ if self.0 >= 0x100 && self.0 < 0x200 => write!(f, "the cryptographic handshake failed: error {}", self.0 & 0xFF),
+136                    _ => f.write_str("unknown error"),
+137                }
+138            }
+139        }
+140    }
+141}
+142
+143errors! {
+144    NO_ERROR(0x0) "the connection is being closed abruptly in the absence of any error";
+145    INTERNAL_ERROR(0x1) "the endpoint encountered an internal error and cannot continue with the connection";
+146    CONNECTION_REFUSED(0x2) "the server refused to accept a new connection";
+147    FLOW_CONTROL_ERROR(0x3) "received more data than permitted in advertised data limits";
+148    STREAM_LIMIT_ERROR(0x4) "received a frame for a stream identifier that exceeded advertised the stream limit for the corresponding stream type";
+149    STREAM_STATE_ERROR(0x5) "received a frame for a stream that was not in a state that permitted that frame";
+150    FINAL_SIZE_ERROR(0x6) "received a STREAM frame or a RESET_STREAM frame containing a different final size to the one already established";
+151    FRAME_ENCODING_ERROR(0x7) "received a frame that was badly formatted";
+152    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";
+153    CONNECTION_ID_LIMIT_ERROR(0x9) "the number of connection IDs provided by the peer exceeds the advertised active_connection_id_limit";
+154    PROTOCOL_VIOLATION(0xA) "detected an error with protocol compliance that was not covered by more specific error codes";
+155    INVALID_TOKEN(0xB) "received an invalid Retry Token in a client Initial";
+156    APPLICATION_ERROR(0xC) "the application or application protocol caused the connection to be closed during the handshake";
+157    CRYPTO_BUFFER_EXCEEDED(0xD) "received more data in CRYPTO frames than can be buffered";
+158    KEY_UPDATE_ERROR(0xE) "key update error";
+159    AEAD_LIMIT_REACHED(0xF) "the endpoint has reached the confidentiality or integrity limit for the AEAD algorithm";
+160    NO_VIABLE_PATH(0x10) "no viable network path exists";
+161    APPLICATION_ABANDON_PATH(0x004150504142414e) "Path abandoned at the application's request";
+162    PATH_RESOURCE_LIMIT_REACHED(0x0052534c494d4954) "Path abandoned due to resource limitations in the transport";
+163    PATH_UNSTABLE_OR_POOR(0x00554e5f494e5446) "Path abandoned due to unstable interfaces";
+164    NO_CID_AVAILABLE_FOR_PATH(0x004e4f5f4349445f) "Path abandoned due to no available connection IDs for the path";
+165}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/transport_parameters.rs.html b/pr/299/docs/src/iroh_quinn_proto/transport_parameters.rs.html new file mode 100644 index 000000000..ec1947c1f --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/transport_parameters.rs.html @@ -0,0 +1,978 @@ +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    LOC_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) LOC_CID_COUNT used locally
+231    pub(crate) fn issue_cids_limit(&self) -> u64 {
+232        self.active_connection_id_limit.0.min(LOC_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        for idx in self
+331            .write_order
+332            .as_ref()
+333            .unwrap_or(&std::array::from_fn(|i| i as u8))
+334        {
+335            let id = TransportParameterId::SUPPORTED[*idx as usize];
+336            match id {
+337                TransportParameterId::ReservedTransportParameter => {
+338                    if let Some(param) = self.grease_transport_parameter {
+339                        param.write(w);
+340                    }
+341                }
+342                TransportParameterId::StatelessResetToken => {
+343                    if let Some(ref x) = self.stateless_reset_token {
+344                        w.write_var(id as u64);
+345                        w.write_var(16);
+346                        w.put_slice(x);
+347                    }
+348                }
+349                TransportParameterId::DisableActiveMigration => {
+350                    if self.disable_active_migration {
+351                        w.write_var(id as u64);
+352                        w.write_var(0);
+353                    }
+354                }
+355                TransportParameterId::MaxDatagramFrameSize => {
+356                    if let Some(x) = self.max_datagram_frame_size {
+357                        w.write_var(id as u64);
+358                        w.write_var(x.size() as u64);
+359                        w.write(x);
+360                    }
+361                }
+362                TransportParameterId::PreferredAddress => {
+363                    if let Some(ref x) = self.preferred_address {
+364                        w.write_var(id as u64);
+365                        w.write_var(x.wire_size() as u64);
+366                        x.write(w);
+367                    }
+368                }
+369                TransportParameterId::OriginalDestinationConnectionId => {
+370                    if let Some(ref cid) = self.original_dst_cid {
+371                        w.write_var(id as u64);
+372                        w.write_var(cid.len() as u64);
+373                        w.put_slice(cid);
+374                    }
+375                }
+376                TransportParameterId::InitialSourceConnectionId => {
+377                    if let Some(ref cid) = self.initial_src_cid {
+378                        w.write_var(id as u64);
+379                        w.write_var(cid.len() as u64);
+380                        w.put_slice(cid);
+381                    }
+382                }
+383                TransportParameterId::RetrySourceConnectionId => {
+384                    if let Some(ref cid) = self.retry_src_cid {
+385                        w.write_var(id as u64);
+386                        w.write_var(cid.len() as u64);
+387                        w.put_slice(cid);
+388                    }
+389                }
+390                TransportParameterId::GreaseQuicBit => {
+391                    if self.grease_quic_bit {
+392                        w.write_var(id as u64);
+393                        w.write_var(0);
+394                    }
+395                }
+396                TransportParameterId::MinAckDelayDraft07 => {
+397                    if let Some(x) = self.min_ack_delay {
+398                        w.write_var(id as u64);
+399                        w.write_var(x.size() as u64);
+400                        w.write(x);
+401                    }
+402                }
+403                TransportParameterId::ObservedAddr => {
+404                    if let Some(varint_role) = self.address_discovery_role.as_transport_parameter()
+405                    {
+406                        w.write_var(id as u64);
+407                        w.write_var(varint_role.size() as u64);
+408                        w.write(varint_role);
+409                    }
+410                }
+411                TransportParameterId::InitialMaxPathId => {
+412                    if let Some(val) = self.initial_max_path_id {
+413                        w.write_var(id as u64);
+414                        w.write_var(val.size() as u64);
+415                        w.write(val);
+416                    }
+417                }
+418                TransportParameterId::IrohNatTraversal => {
+419                    if let Some(val) = self.max_remote_nat_traversal_addresses {
+420                        w.write_var(id as u64);
+421                        w.write(VarInt(1));
+422                        w.write(val.get());
+423                    }
+424                }
+425                id => {
+426                    macro_rules! write_params {
+427                        {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
+428                            match id {
+429                                $(TransportParameterId::$id => {
+430                                    if self.$name.0 != $default {
+431                                        w.write_var(id as u64);
+432                                        w.write(VarInt::try_from(self.$name.size()).unwrap());
+433                                        w.write(self.$name);
+434                                    }
+435                                })*,
+436                                _ => {
+437                                    unimplemented!("Missing implementation of write for transport parameter with code {id:?}");
+438                                }
+439                            }
+440                        }
+441                    }
+442                    apply_params!(write_params);
+443                }
+444            }
+445        }
+446    }
+447
+448    /// Decode `TransportParameters` from buffer
+449    pub fn read<R: Buf>(side: Side, r: &mut R) -> Result<Self, Error> {
+450        // Initialize to protocol-specified defaults
+451        let mut params = Self::default();
+452
+453        // State to check for duplicate transport parameters.
+454        macro_rules! param_state {
+455            {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {{
+456                struct ParamState {
+457                    $($name: bool,)*
+458                }
+459
+460                ParamState {
+461                    $($name: false,)*
+462                }
+463            }}
+464        }
+465        let mut got = apply_params!(param_state);
+466
+467        while r.has_remaining() {
+468            let id = r.get_var()?;
+469            let len = r.get_var()?;
+470            if (r.remaining() as u64) < len {
+471                return Err(Error::Malformed);
+472            }
+473            let len = len as usize;
+474            let Ok(id) = TransportParameterId::try_from(id) else {
+475                // unknown transport parameters are ignored
+476                r.advance(len);
+477                continue;
+478            };
+479
+480            match id {
+481                TransportParameterId::OriginalDestinationConnectionId => {
+482                    decode_cid(len, &mut params.original_dst_cid, r)?
+483                }
+484                TransportParameterId::StatelessResetToken => {
+485                    if len != 16 || params.stateless_reset_token.is_some() {
+486                        return Err(Error::Malformed);
+487                    }
+488                    let mut tok = [0; RESET_TOKEN_SIZE];
+489                    r.copy_to_slice(&mut tok);
+490                    params.stateless_reset_token = Some(tok.into());
+491                }
+492                TransportParameterId::DisableActiveMigration => {
+493                    if len != 0 || params.disable_active_migration {
+494                        return Err(Error::Malformed);
+495                    }
+496                    params.disable_active_migration = true;
+497                }
+498                TransportParameterId::PreferredAddress => {
+499                    if params.preferred_address.is_some() {
+500                        return Err(Error::Malformed);
+501                    }
+502                    params.preferred_address = Some(PreferredAddress::read(&mut r.take(len))?);
+503                }
+504                TransportParameterId::InitialSourceConnectionId => {
+505                    decode_cid(len, &mut params.initial_src_cid, r)?
+506                }
+507                TransportParameterId::RetrySourceConnectionId => {
+508                    decode_cid(len, &mut params.retry_src_cid, r)?
+509                }
+510                TransportParameterId::MaxDatagramFrameSize => {
+511                    if len > 8 || params.max_datagram_frame_size.is_some() {
+512                        return Err(Error::Malformed);
+513                    }
+514                    params.max_datagram_frame_size = Some(r.get().unwrap());
+515                }
+516                TransportParameterId::GreaseQuicBit => match len {
+517                    0 => params.grease_quic_bit = true,
+518                    _ => return Err(Error::Malformed),
+519                },
+520                TransportParameterId::MinAckDelayDraft07 => {
+521                    params.min_ack_delay = Some(r.get().unwrap())
+522                }
+523                TransportParameterId::ObservedAddr => {
+524                    if !params.address_discovery_role.is_disabled() {
+525                        // duplicate parameter
+526                        return Err(Error::Malformed);
+527                    }
+528                    let value: VarInt = r.get()?;
+529                    if len != value.size() {
+530                        return Err(Error::Malformed);
+531                    }
+532                    params.address_discovery_role = value.try_into()?;
+533                    tracing::debug!(
+534                        role = ?params.address_discovery_role,
+535                        "address discovery enabled for peer"
+536                    );
+537                }
+538                TransportParameterId::InitialMaxPathId => {
+539                    if params.initial_max_path_id.is_some() {
+540                        return Err(Error::Malformed);
+541                    }
+542
+543                    let value: PathId = r.get()?;
+544                    if len != value.size() {
+545                        return Err(Error::Malformed);
+546                    }
+547
+548                    params.initial_max_path_id = Some(value);
+549                }
+550                TransportParameterId::IrohNatTraversal => {
+551                    if params.max_remote_nat_traversal_addresses.is_some() {
+552                        return Err(Error::Malformed);
+553                    }
+554                    if len != 1 {
+555                        return Err(Error::Malformed);
+556                    }
+557
+558                    let value: u8 = r.get()?;
+559                    let value = NonZeroU8::new(value).ok_or(Error::IllegalValue)?;
+560
+561                    params.max_remote_nat_traversal_addresses = Some(value);
+562                }
+563                _ => {
+564                    macro_rules! parse {
+565                        {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
+566                            match id {
+567                                $(TransportParameterId::$id => {
+568                                    let value = r.get::<VarInt>()?;
+569                                    if len != value.size() || got.$name { return Err(Error::Malformed); }
+570                                    params.$name = value.into();
+571                                    got.$name = true;
+572                                })*
+573                                _ => r.advance(len),
+574                            }
+575                        }
+576                    }
+577                    apply_params!(parse);
+578                }
+579            }
+580        }
+581
+582        // Semantic validation
+583
+584        // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.26.1
+585        if params.ack_delay_exponent.0 > 20
+586            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.28.1
+587            || params.max_ack_delay.0 >= 1 << 14
+588            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-6.2.1
+589            || params.active_connection_id_limit.0 < 2
+590            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.10.1
+591            || params.max_udp_payload_size.0 < 1200
+592            // https://www.rfc-editor.org/rfc/rfc9000.html#section-4.6-2
+593            || params.initial_max_streams_bidi.0 > MAX_STREAM_COUNT
+594            || params.initial_max_streams_uni.0 > MAX_STREAM_COUNT
+595            // https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-08.html#section-3-4
+596            || params.min_ack_delay.is_some_and(|min_ack_delay| {
+597                // min_ack_delay uses microseconds, whereas max_ack_delay uses milliseconds
+598                min_ack_delay.0 > params.max_ack_delay.0 * 1_000
+599            })
+600            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-8
+601            || (side.is_server()
+602                && (params.original_dst_cid.is_some()
+603                    || params.preferred_address.is_some()
+604                    || params.retry_src_cid.is_some()
+605                    || params.stateless_reset_token.is_some()))
+606            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.38.1
+607            || params
+608                .preferred_address.is_some_and(|x| x.connection_id.is_empty())
+609        {
+610            return Err(Error::IllegalValue);
+611        }
+612
+613        Ok(params)
+614    }
+615}
+616
+617/// A reserved transport parameter.
+618///
+619/// It has an identifier of the form 31 * N + 27 for the integer value of N.
+620/// Such identifiers are reserved to exercise the requirement that unknown transport parameters be ignored.
+621/// The reserved transport parameter has no semantics and can carry arbitrary values.
+622/// It may be included in transport parameters sent to the peer, and should be ignored when received.
+623///
+624/// See spec: <https://www.rfc-editor.org/rfc/rfc9000.html#section-18.1>
+625#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+626pub(crate) struct ReservedTransportParameter {
+627    /// The reserved identifier of the transport parameter
+628    id: VarInt,
+629
+630    /// Buffer to store the parameter payload
+631    payload: [u8; Self::MAX_PAYLOAD_LEN],
+632
+633    /// The number of bytes to include in the wire format from the `payload` buffer
+634    payload_len: usize,
+635}
+636
+637impl ReservedTransportParameter {
+638    /// Generates a transport parameter with a random payload and a reserved ID.
+639    ///
+640    /// The implementation is inspired by quic-go and quiche:
+641    /// 1. <https://github.com/quic-go/quic-go/blob/3e0a67b2476e1819752f04d75968de042b197b56/internal/wire/transport_parameters.go#L338-L344>
+642    /// 2. <https://github.com/google/quiche/blob/cb1090b20c40e2f0815107857324e99acf6ec567/quiche/quic/core/crypto/transport_parameters.cc#L843-L860>
+643    fn random(rng: &mut impl RngCore) -> Self {
+644        let id = Self::generate_reserved_id(rng);
+645
+646        let payload_len = rng.random_range(0..Self::MAX_PAYLOAD_LEN);
+647
+648        let payload = {
+649            let mut slice = [0u8; Self::MAX_PAYLOAD_LEN];
+650            rng.fill_bytes(&mut slice[..payload_len]);
+651            slice
+652        };
+653
+654        Self {
+655            id,
+656            payload,
+657            payload_len,
+658        }
+659    }
+660
+661    fn write(&self, w: &mut impl BufMut) {
+662        w.write_var(self.id.0);
+663        w.write_var(self.payload_len as u64);
+664        w.put_slice(&self.payload[..self.payload_len]);
+665    }
+666
+667    /// Generates a random reserved identifier of the form `31 * N + 27`, as required by RFC 9000.
+668    /// Reserved transport parameter identifiers are used to test compliance with the requirement
+669    /// that unknown transport parameters must be ignored by peers.
+670    /// See: <https://www.rfc-editor.org/rfc/rfc9000.html#section-18.1> and <https://www.rfc-editor.org/rfc/rfc9000.html#section-22.3>
+671    fn generate_reserved_id(rng: &mut impl RngCore) -> VarInt {
+672        let id = {
+673            let rand = rng.random_range(0u64..(1 << 62) - 27);
+674            let n = rand / 31;
+675            31 * n + 27
+676        };
+677        debug_assert!(
+678            id % 31 == 27,
+679            "generated id does not have the form of 31 * N + 27"
+680        );
+681        VarInt::from_u64(id).expect(
+682            "generated id does fit into range of allowed transport parameter IDs: [0; 2^62)",
+683        )
+684    }
+685
+686    /// The maximum length of the payload to include as the parameter payload.
+687    /// This value is not a specification-imposed limit but is chosen to match
+688    /// the limit used by other implementations of QUIC, e.g., quic-go and quiche.
+689    const MAX_PAYLOAD_LEN: usize = 16;
+690}
+691
+692#[repr(u64)]
+693#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+694pub(crate) enum TransportParameterId {
+695    // https://www.rfc-editor.org/rfc/rfc9000.html#iana-tp-table
+696    OriginalDestinationConnectionId = 0x00,
+697    MaxIdleTimeout = 0x01,
+698    StatelessResetToken = 0x02,
+699    MaxUdpPayloadSize = 0x03,
+700    InitialMaxData = 0x04,
+701    InitialMaxStreamDataBidiLocal = 0x05,
+702    InitialMaxStreamDataBidiRemote = 0x06,
+703    InitialMaxStreamDataUni = 0x07,
+704    InitialMaxStreamsBidi = 0x08,
+705    InitialMaxStreamsUni = 0x09,
+706    AckDelayExponent = 0x0A,
+707    MaxAckDelay = 0x0B,
+708    DisableActiveMigration = 0x0C,
+709    PreferredAddress = 0x0D,
+710    ActiveConnectionIdLimit = 0x0E,
+711    InitialSourceConnectionId = 0x0F,
+712    RetrySourceConnectionId = 0x10,
+713
+714    // Smallest possible ID of reserved transport parameter https://datatracker.ietf.org/doc/html/rfc9000#section-22.3
+715    ReservedTransportParameter = 0x1B,
+716
+717    // https://www.rfc-editor.org/rfc/rfc9221.html#section-3
+718    MaxDatagramFrameSize = 0x20,
+719
+720    // https://www.rfc-editor.org/rfc/rfc9287.html#section-3
+721    GreaseQuicBit = 0x2AB2,
+722
+723    // https://datatracker.ietf.org/doc/html/draft-ietf-quic-ack-frequency#section-10.1
+724    MinAckDelayDraft07 = 0xFF04DE1B,
+725
+726    // <https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/>
+727    ObservedAddr = 0x9f81a176,
+728
+729    // https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath
+730    InitialMaxPathId = 0x0f739bbc1b666d0c,
+731
+732    // inspired by https://www.ietf.org/archive/id/draft-seemann-quic-nat-traversal-02.html,
+733    // simplified to iroh's needs
+734    IrohNatTraversal = 0x3d7f91120401,
+735}
+736
+737impl TransportParameterId {
+738    /// Array with all supported transport parameter IDs
+739    const SUPPORTED: [Self; 24] = [
+740        Self::MaxIdleTimeout,
+741        Self::MaxUdpPayloadSize,
+742        Self::InitialMaxData,
+743        Self::InitialMaxStreamDataBidiLocal,
+744        Self::InitialMaxStreamDataBidiRemote,
+745        Self::InitialMaxStreamDataUni,
+746        Self::InitialMaxStreamsBidi,
+747        Self::InitialMaxStreamsUni,
+748        Self::AckDelayExponent,
+749        Self::MaxAckDelay,
+750        Self::ActiveConnectionIdLimit,
+751        Self::ReservedTransportParameter,
+752        Self::StatelessResetToken,
+753        Self::DisableActiveMigration,
+754        Self::MaxDatagramFrameSize,
+755        Self::PreferredAddress,
+756        Self::OriginalDestinationConnectionId,
+757        Self::InitialSourceConnectionId,
+758        Self::RetrySourceConnectionId,
+759        Self::GreaseQuicBit,
+760        Self::MinAckDelayDraft07,
+761        Self::ObservedAddr,
+762        Self::InitialMaxPathId,
+763        Self::IrohNatTraversal,
+764    ];
+765}
+766
+767impl std::cmp::PartialEq<u64> for TransportParameterId {
+768    fn eq(&self, other: &u64) -> bool {
+769        *other == (*self as u64)
+770    }
+771}
+772
+773impl TryFrom<u64> for TransportParameterId {
+774    type Error = ();
+775
+776    fn try_from(value: u64) -> Result<Self, Self::Error> {
+777        let param = match value {
+778            id if Self::MaxIdleTimeout == id => Self::MaxIdleTimeout,
+779            id if Self::MaxUdpPayloadSize == id => Self::MaxUdpPayloadSize,
+780            id if Self::InitialMaxData == id => Self::InitialMaxData,
+781            id if Self::InitialMaxStreamDataBidiLocal == id => Self::InitialMaxStreamDataBidiLocal,
+782            id if Self::InitialMaxStreamDataBidiRemote == id => {
+783                Self::InitialMaxStreamDataBidiRemote
+784            }
+785            id if Self::InitialMaxStreamDataUni == id => Self::InitialMaxStreamDataUni,
+786            id if Self::InitialMaxStreamsBidi == id => Self::InitialMaxStreamsBidi,
+787            id if Self::InitialMaxStreamsUni == id => Self::InitialMaxStreamsUni,
+788            id if Self::AckDelayExponent == id => Self::AckDelayExponent,
+789            id if Self::MaxAckDelay == id => Self::MaxAckDelay,
+790            id if Self::ActiveConnectionIdLimit == id => Self::ActiveConnectionIdLimit,
+791            id if Self::ReservedTransportParameter == id => Self::ReservedTransportParameter,
+792            id if Self::StatelessResetToken == id => Self::StatelessResetToken,
+793            id if Self::DisableActiveMigration == id => Self::DisableActiveMigration,
+794            id if Self::MaxDatagramFrameSize == id => Self::MaxDatagramFrameSize,
+795            id if Self::PreferredAddress == id => Self::PreferredAddress,
+796            id if Self::OriginalDestinationConnectionId == id => {
+797                Self::OriginalDestinationConnectionId
+798            }
+799            id if Self::InitialSourceConnectionId == id => Self::InitialSourceConnectionId,
+800            id if Self::RetrySourceConnectionId == id => Self::RetrySourceConnectionId,
+801            id if Self::GreaseQuicBit == id => Self::GreaseQuicBit,
+802            id if Self::MinAckDelayDraft07 == id => Self::MinAckDelayDraft07,
+803            id if Self::ObservedAddr == id => Self::ObservedAddr,
+804            id if Self::InitialMaxPathId == id => Self::InitialMaxPathId,
+805            id if Self::IrohNatTraversal == id => Self::IrohNatTraversal,
+806            _ => return Err(()),
+807        };
+808        Ok(param)
+809    }
+810}
+811
+812fn decode_cid(len: usize, value: &mut Option<ConnectionId>, r: &mut impl Buf) -> Result<(), Error> {
+813    if len > MAX_CID_SIZE || value.is_some() || r.remaining() < len {
+814        return Err(Error::Malformed);
+815    }
+816
+817    *value = Some(ConnectionId::from_buf(r, len));
+818    Ok(())
+819}
+820
+821#[cfg(test)]
+822mod test {
+823
+824    use super::*;
+825
+826    #[test]
+827    fn coding() {
+828        let mut buf = Vec::new();
+829        let params = TransportParameters {
+830            initial_src_cid: Some(ConnectionId::new(&[])),
+831            original_dst_cid: Some(ConnectionId::new(&[])),
+832            initial_max_streams_bidi: 16u32.into(),
+833            initial_max_streams_uni: 16u32.into(),
+834            ack_delay_exponent: 2u32.into(),
+835            max_udp_payload_size: 1200u32.into(),
+836            preferred_address: Some(PreferredAddress {
+837                address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
+838                address_v6: Some(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 24, 0, 0)),
+839                connection_id: ConnectionId::new(&[0x42]),
+840                stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
+841            }),
+842            grease_quic_bit: true,
+843            min_ack_delay: Some(2_000u32.into()),
+844            address_discovery_role: address_discovery::Role::SendOnly,
+845            initial_max_path_id: Some(PathId::MAX),
+846            max_remote_nat_traversal_addresses: Some(5u8.try_into().unwrap()),
+847            ..TransportParameters::default()
+848        };
+849        params.write(&mut buf);
+850        assert_eq!(
+851            TransportParameters::read(Side::Client, &mut buf.as_slice()).unwrap(),
+852            params
+853        );
+854    }
+855
+856    #[test]
+857    fn reserved_transport_parameter_generate_reserved_id() {
+858        let mut rngs = [
+859            StepRng(0),
+860            StepRng(1),
+861            StepRng(27),
+862            StepRng(31),
+863            StepRng(u32::MAX as u64),
+864            StepRng(u32::MAX as u64 - 1),
+865            StepRng(u32::MAX as u64 + 1),
+866            StepRng(u32::MAX as u64 - 27),
+867            StepRng(u32::MAX as u64 + 27),
+868            StepRng(u32::MAX as u64 - 31),
+869            StepRng(u32::MAX as u64 + 31),
+870            StepRng(u64::MAX),
+871            StepRng(u64::MAX - 1),
+872            StepRng(u64::MAX - 27),
+873            StepRng(u64::MAX - 31),
+874            StepRng(1 << 62),
+875            StepRng((1 << 62) - 1),
+876            StepRng((1 << 62) + 1),
+877            StepRng((1 << 62) - 27),
+878            StepRng((1 << 62) + 27),
+879            StepRng((1 << 62) - 31),
+880            StepRng((1 << 62) + 31),
+881        ];
+882        for rng in &mut rngs {
+883            let id = ReservedTransportParameter::generate_reserved_id(rng);
+884            assert!(id.0 % 31 == 27)
+885        }
+886    }
+887
+888    struct StepRng(u64);
+889
+890    impl RngCore for StepRng {
+891        #[inline]
+892        fn next_u32(&mut self) -> u32 {
+893            self.next_u64() as u32
+894        }
+895
+896        #[inline]
+897        fn next_u64(&mut self) -> u64 {
+898            let res = self.0;
+899            self.0 = self.0.wrapping_add(1);
+900            res
+901        }
+902
+903        #[inline]
+904        fn fill_bytes(&mut self, dst: &mut [u8]) {
+905            let mut left = dst;
+906            while left.len() >= 8 {
+907                let (l, r) = left.split_at_mut(8);
+908                left = r;
+909                l.copy_from_slice(&self.next_u64().to_le_bytes());
+910            }
+911            let n = left.len();
+912            if n > 0 {
+913                left.copy_from_slice(&self.next_u32().to_le_bytes()[..n]);
+914            }
+915        }
+916    }
+917
+918    #[test]
+919    fn reserved_transport_parameter_ignored_when_read() {
+920        let mut buf = Vec::new();
+921        let reserved_parameter = ReservedTransportParameter::random(&mut rand::rng());
+922        assert!(reserved_parameter.payload_len < ReservedTransportParameter::MAX_PAYLOAD_LEN);
+923        assert!(reserved_parameter.id.0 % 31 == 27);
+924
+925        reserved_parameter.write(&mut buf);
+926        assert!(!buf.is_empty());
+927        let read_params = TransportParameters::read(Side::Server, &mut buf.as_slice()).unwrap();
+928        assert_eq!(read_params, TransportParameters::default());
+929    }
+930
+931    #[test]
+932    fn read_semantic_validation() {
+933        #[allow(clippy::type_complexity)]
+934        let illegal_params_builders: Vec<Box<dyn FnMut(&mut TransportParameters)>> = vec![
+935            Box::new(|t| {
+936                // This min_ack_delay is bigger than max_ack_delay!
+937                let min_ack_delay = t.max_ack_delay.0 * 1_000 + 1;
+938                t.min_ack_delay = Some(VarInt::from_u64(min_ack_delay).unwrap())
+939            }),
+940            Box::new(|t| {
+941                // Preferred address can only be sent by senders (and we are reading the transport
+942                // params as a client)
+943                t.preferred_address = Some(PreferredAddress {
+944                    address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
+945                    address_v6: None,
+946                    connection_id: ConnectionId::new(&[]),
+947                    stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
+948                })
+949            }),
+950        ];
+951
+952        for mut builder in illegal_params_builders {
+953            let mut buf = Vec::new();
+954            let mut params = TransportParameters::default();
+955            builder(&mut params);
+956            params.write(&mut buf);
+957
+958            assert_eq!(
+959                TransportParameters::read(Side::Server, &mut buf.as_slice()),
+960                Err(Error::IllegalValue)
+961            );
+962        }
+963    }
+964
+965    #[test]
+966    fn resumption_params_validation() {
+967        let high_limit = TransportParameters {
+968            initial_max_streams_uni: 32u32.into(),
+969            ..TransportParameters::default()
+970        };
+971        let low_limit = TransportParameters {
+972            initial_max_streams_uni: 16u32.into(),
+973            ..TransportParameters::default()
+974        };
+975        high_limit.validate_resumption_from(&low_limit).unwrap();
+976        low_limit.validate_resumption_from(&high_limit).unwrap_err();
+977    }
+978}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_proto/varint.rs.html b/pr/299/docs/src/iroh_quinn_proto/varint.rs.html new file mode 100644 index 000000000..db607d6aa --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_proto/varint.rs.html @@ -0,0 +1,218 @@ +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#[cfg(feature = "arbitrary")]
+9use arbitrary::Arbitrary;
+10
+11/// An integer less than 2^62
+12///
+13/// Values of this type are suitable for encoding as QUIC variable-length integer.
+14// It would be neat if we could express to Rust that the top two bits are available for use as enum
+15// discriminants
+16#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+17pub struct VarInt(pub(crate) u64);
+18
+19impl VarInt {
+20    /// The largest representable value
+21    pub const MAX: Self = Self((1 << 62) - 1);
+22    /// The largest encoded value length
+23    pub const MAX_SIZE: usize = 8;
+24
+25    /// Construct a `VarInt` infallibly
+26    pub const fn from_u32(x: u32) -> Self {
+27        Self(x as u64)
+28    }
+29
+30    /// Succeeds iff `x` < 2^62
+31    pub fn from_u64(x: u64) -> Result<Self, VarIntBoundsExceeded> {
+32        if x < 2u64.pow(62) {
+33            Ok(Self(x))
+34        } else {
+35            Err(VarIntBoundsExceeded)
+36        }
+37    }
+38
+39    /// Create a VarInt without ensuring it's in range
+40    ///
+41    /// # Safety
+42    ///
+43    /// `x` must be less than 2^62.
+44    pub const unsafe fn from_u64_unchecked(x: u64) -> Self {
+45        Self(x)
+46    }
+47
+48    /// Extract the integer value
+49    pub const fn into_inner(self) -> u64 {
+50        self.0
+51    }
+52
+53    /// Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead
+54    /// of overflowing.
+55    pub fn saturating_add(self, rhs: impl Into<Self>) -> Self {
+56        let rhs = rhs.into();
+57        let inner = self.0.saturating_add(rhs.0).min(Self::MAX.0);
+58        Self(inner)
+59    }
+60
+61    /// Compute the number of bytes needed to encode this value
+62    pub(crate) const fn size(self) -> usize {
+63        let x = self.0;
+64        if x < 2u64.pow(6) {
+65            1
+66        } else if x < 2u64.pow(14) {
+67            2
+68        } else if x < 2u64.pow(30) {
+69            4
+70        } else if x < 2u64.pow(62) {
+71            8
+72        } else {
+73            panic!("malformed VarInt");
+74        }
+75    }
+76}
+77
+78impl From<VarInt> for u64 {
+79    fn from(x: VarInt) -> Self {
+80        x.0
+81    }
+82}
+83
+84impl From<u8> for VarInt {
+85    fn from(x: u8) -> Self {
+86        Self(x.into())
+87    }
+88}
+89
+90impl From<u16> for VarInt {
+91    fn from(x: u16) -> Self {
+92        Self(x.into())
+93    }
+94}
+95
+96impl From<u32> for VarInt {
+97    fn from(x: u32) -> Self {
+98        Self(x.into())
+99    }
+100}
+101
+102impl std::convert::TryFrom<u64> for VarInt {
+103    type Error = VarIntBoundsExceeded;
+104    /// Succeeds iff `x` < 2^62
+105    fn try_from(x: u64) -> Result<Self, VarIntBoundsExceeded> {
+106        Self::from_u64(x)
+107    }
+108}
+109
+110impl std::convert::TryFrom<u128> for VarInt {
+111    type Error = VarIntBoundsExceeded;
+112    /// Succeeds iff `x` < 2^62
+113    fn try_from(x: u128) -> Result<Self, VarIntBoundsExceeded> {
+114        Self::from_u64(x.try_into().map_err(|_| VarIntBoundsExceeded)?)
+115    }
+116}
+117
+118impl std::convert::TryFrom<usize> for VarInt {
+119    type Error = VarIntBoundsExceeded;
+120    /// Succeeds iff `x` < 2^62
+121    fn try_from(x: usize) -> Result<Self, VarIntBoundsExceeded> {
+122        Self::try_from(x as u64)
+123    }
+124}
+125
+126impl fmt::Debug for VarInt {
+127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+128        self.0.fmt(f)
+129    }
+130}
+131
+132impl fmt::Display for VarInt {
+133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+134        self.0.fmt(f)
+135    }
+136}
+137
+138#[cfg(feature = "arbitrary")]
+139impl<'arbitrary> Arbitrary<'arbitrary> for VarInt {
+140    fn arbitrary(u: &mut arbitrary::Unstructured<'arbitrary>) -> arbitrary::Result<Self> {
+141        Ok(Self(u.int_in_range(0..=Self::MAX.0)?))
+142    }
+143}
+144
+145/// Error returned when constructing a `VarInt` from a value >= 2^62
+146#[derive(Debug, Copy, Clone, Eq, PartialEq, Error)]
+147#[error("value too large for varint encoding")]
+148pub struct VarIntBoundsExceeded;
+149
+150impl Decodable for VarInt {
+151    fn decode<B: Buf>(r: &mut B) -> coding::Result<Self> {
+152        if !r.has_remaining() {
+153            return Err(UnexpectedEnd);
+154        }
+155        let mut buf = [0; 8];
+156        buf[0] = r.get_u8();
+157        let tag = buf[0] >> 6;
+158        buf[0] &= 0b0011_1111;
+159        let x = match tag {
+160            0b00 => u64::from(buf[0]),
+161            0b01 => {
+162                if r.remaining() < 1 {
+163                    return Err(UnexpectedEnd);
+164                }
+165                r.copy_to_slice(&mut buf[1..2]);
+166                u64::from(u16::from_be_bytes(buf[..2].try_into().unwrap()))
+167            }
+168            0b10 => {
+169                if r.remaining() < 3 {
+170                    return Err(UnexpectedEnd);
+171                }
+172                r.copy_to_slice(&mut buf[1..4]);
+173                u64::from(u32::from_be_bytes(buf[..4].try_into().unwrap()))
+174            }
+175            0b11 => {
+176                if r.remaining() < 7 {
+177                    return Err(UnexpectedEnd);
+178                }
+179                r.copy_to_slice(&mut buf[1..8]);
+180                u64::from_be_bytes(buf)
+181            }
+182            _ => unreachable!(),
+183        };
+184        Ok(Self(x))
+185    }
+186}
+187impl Encodable for VarInt {
+188    fn encode<B: BufMut>(&self, w: &mut B) {
+189        let x = self.0;
+190        if x < 2u64.pow(6) {
+191            w.put_u8(x as u8);
+192        } else if x < 2u64.pow(14) {
+193            w.put_u16((0b01 << 14) | x as u16);
+194        } else if x < 2u64.pow(30) {
+195            w.put_u32((0b10 << 30) | x as u32);
+196        } else if x < 2u64.pow(62) {
+197            w.put_u64((0b11 << 62) | x);
+198        } else {
+199            unreachable!("malformed VarInt")
+200        }
+201    }
+202}
+203
+204#[cfg(test)]
+205mod tests {
+206    use super::*;
+207
+208    #[test]
+209    fn test_saturating_add() {
+210        // add within range behaves normally
+211        let large: VarInt = u32::MAX.into();
+212        let next = u64::from(u32::MAX) + 1;
+213        assert_eq!(large.saturating_add(1u8), VarInt::from_u64(next).unwrap());
+214
+215        // outside range saturates
+216        assert_eq!(VarInt::MAX.saturating_add(1u8), VarInt::MAX)
+217    }
+218}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_udp/cmsg/mod.rs.html b/pr/299/docs/src/iroh_quinn_udp/cmsg/mod.rs.html new file mode 100644 index 000000000..c9f5e54a5 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_udp/cmsg/unix.rs.html b/pr/299/docs/src/iroh_quinn_udp/cmsg/unix.rs.html new file mode 100644 index 000000000..337ee0895 --- /dev/null +++ b/pr/299/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/299/docs/src/iroh_quinn_udp/lib.rs.html b/pr/299/docs/src/iroh_quinn_udp/lib.rs.html new file mode 100644 index 000000000..d2fe73949 --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_udp/lib.rs.html @@ -0,0 +1,240 @@ +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
+153/// Log at most 1 IO error per minute
+154#[cfg(not(wasm_browser))]
+155const IO_ERROR_LOG_INTERVAL: Duration = std::time::Duration::from_secs(60);
+156
+157/// Logs a warning message when sendmsg fails
+158///
+159/// Logging will only be performed if at least [`IO_ERROR_LOG_INTERVAL`]
+160/// has elapsed since the last error was logged.
+161#[cfg(all(not(wasm_browser), any(feature = "tracing-log", feature = "log")))]
+162fn log_sendmsg_error(
+163    last_send_error: &Mutex<Instant>,
+164    err: impl core::fmt::Debug,
+165    transmit: &Transmit,
+166) {
+167    let now = Instant::now();
+168    let last_send_error = &mut *last_send_error.lock().expect("poisend lock");
+169    if now.saturating_duration_since(*last_send_error) > IO_ERROR_LOG_INTERVAL {
+170        *last_send_error = now;
+171        log::warn!(
+172            "sendmsg error: {:?}, Transmit: {{ destination: {:?}, src_ip: {:?}, ecn: {:?}, len: {:?}, segment_size: {:?} }}",
+173            err,
+174            transmit.destination,
+175            transmit.src_ip,
+176            transmit.ecn,
+177            transmit.contents.len(),
+178            transmit.segment_size
+179        );
+180    }
+181}
+182
+183// No-op
+184#[cfg(not(any(wasm_browser, feature = "tracing-log", feature = "log")))]
+185fn log_sendmsg_error(_: &Mutex<Instant>, _: impl core::fmt::Debug, _: &Transmit) {}
+186
+187/// A borrowed UDP socket
+188///
+189/// On Unix, constructible via `From<T: AsFd>`. On Windows, constructible via `From<T:
+190/// AsSocket>`.
+191// Wrapper around socket2 to avoid making it a public dependency and incurring stability risk
+192#[cfg(not(wasm_browser))]
+193pub struct UdpSockRef<'a>(socket2::SockRef<'a>);
+194
+195#[cfg(unix)]
+196impl<'s, S> From<&'s S> for UdpSockRef<'s>
+197where
+198    S: AsFd,
+199{
+200    fn from(socket: &'s S) -> Self {
+201        Self(socket.into())
+202    }
+203}
+204
+205#[cfg(windows)]
+206impl<'s, S> From<&'s S> for UdpSockRef<'s>
+207where
+208    S: AsSocket,
+209{
+210    fn from(socket: &'s S) -> Self {
+211        Self(socket.into())
+212    }
+213}
+214
+215/// Explicit congestion notification codepoint
+216#[repr(u8)]
+217#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+218pub enum EcnCodepoint {
+219    /// The ECT(0) codepoint, indicating that an endpoint is ECN-capable
+220    Ect0 = 0b10,
+221    /// The ECT(1) codepoint, indicating that an endpoint is ECN-capable
+222    Ect1 = 0b01,
+223    /// The CE codepoint, signalling that congestion was experienced
+224    Ce = 0b11,
+225}
+226
+227impl EcnCodepoint {
+228    /// Create new object from the given bits
+229    pub fn from_bits(x: u8) -> Option<Self> {
+230        use EcnCodepoint::*;
+231        Some(match x & 0b11 {
+232            0b10 => Ect0,
+233            0b01 => Ect1,
+234            0b11 => Ce,
+235            _ => {
+236                return None;
+237            }
+238        })
+239    }
+240}
\ No newline at end of file diff --git a/pr/299/docs/src/iroh_quinn_udp/unix.rs.html b/pr/299/docs/src/iroh_quinn_udp/unix.rs.html new file mode 100644 index 000000000..81b5fcdcf --- /dev/null +++ b/pr/299/docs/src/iroh_quinn_udp/unix.rs.html @@ -0,0 +1,1101 @@ +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// Defined in netinet6/in6.h on OpenBSD, this is not yet exported by the libc crate
+55// directly.  See https://github.com/rust-lang/libc/issues/3704 for when we might be able to
+56// rely on this from the libc crate.
+57#[cfg(any(target_os = "openbsd", target_os = "netbsd"))]
+58const IPV6_DONTFRAG: libc::c_int = 62;
+59#[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))]
+60const IPV6_DONTFRAG: libc::c_int = libc::IPV6_DONTFRAG;
+61
+62#[cfg(target_os = "freebsd")]
+63type IpTosTy = libc::c_uchar;
+64#[cfg(not(any(target_os = "freebsd", target_os = "netbsd")))]
+65type IpTosTy = libc::c_int;
+66
+67/// Tokio-compatible UDP socket with some useful specializations.
+68///
+69/// Unlike a standard tokio UDP socket, this allows ECN bits to be read and written on some
+70/// platforms.
+71#[derive(Debug)]
+72pub struct UdpSocketState {
+73    last_send_error: Mutex<Instant>,
+74    max_gso_segments: AtomicUsize,
+75    gro_segments: NonZeroUsize,
+76    may_fragment: bool,
+77
+78    /// True if we have received EINVAL error from `sendmsg` system call at least once.
+79    ///
+80    /// If enabled, we assume that old kernel is used and switch to fallback mode.
+81    /// In particular, we do not use IP_TOS cmsg_type in this case,
+82    /// which is not supported on Linux <3.13 and results in not sending the UDP packet at all.
+83    sendmsg_einval: AtomicBool,
+84}
+85
+86impl UdpSocketState {
+87    pub fn new(sock: UdpSockRef<'_>) -> io::Result<Self> {
+88        let io = sock.0;
+89        let mut cmsg_platform_space = 0;
+90        if cfg!(target_os = "linux")
+91            || cfg!(bsd)
+92            || cfg!(apple)
+93            || cfg!(target_os = "android")
+94            || cfg!(solarish)
+95        {
+96            cmsg_platform_space +=
+97                unsafe { libc::CMSG_SPACE(mem::size_of::<libc::in6_pktinfo>() as _) as usize };
+98        }
+99
+100        assert!(
+101            CMSG_LEN
+102                >= unsafe { libc::CMSG_SPACE(mem::size_of::<libc::c_int>() as _) as usize }
+103                    + cmsg_platform_space
+104        );
+105        assert!(
+106            mem::align_of::<libc::cmsghdr>() <= mem::align_of::<cmsg::Aligned<[u8; 0]>>(),
+107            "control message buffers will be misaligned"
+108        );
+109
+110        io.set_nonblocking(true)?;
+111
+112        let addr = io.local_addr()?;
+113        let is_ipv4 = addr.family() == libc::AF_INET as libc::sa_family_t;
+114
+115        // mac and ios do not support IP_RECVTOS on dual-stack sockets :(
+116        // older macos versions also don't have the flag and will error out if we don't ignore it
+117        #[cfg(not(any(
+118            target_os = "openbsd",
+119            target_os = "netbsd",
+120            target_os = "dragonfly",
+121            solarish
+122        )))]
+123        if is_ipv4 || !io.only_v6()? {
+124            if let Err(_err) =
+125                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVTOS, OPTION_ON)
+126            {
+127                crate::log::debug!("Ignoring error setting IP_RECVTOS on socket: {_err:?}");
+128            }
+129        }
+130
+131        let mut may_fragment = false;
+132        #[cfg(any(target_os = "linux", target_os = "android"))]
+133        {
+134            // opportunistically try to enable GRO. See gro::gro_segments().
+135            let _ = set_socket_option(&*io, libc::SOL_UDP, gro::UDP_GRO, OPTION_ON);
+136
+137            // Forbid IPv4 fragmentation. Set even for IPv6 to account for IPv6 mapped IPv4 addresses.
+138            // Set `may_fragment` to `true` if this option is not supported on the platform.
+139            may_fragment |= !set_socket_option_supported(
+140                &*io,
+141                libc::IPPROTO_IP,
+142                libc::IP_MTU_DISCOVER,
+143                libc::IP_PMTUDISC_PROBE,
+144            )?;
+145
+146            if is_ipv4 {
+147                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_PKTINFO, OPTION_ON)?;
+148            } else {
+149                // Set `may_fragment` to `true` if this option is not supported on the platform.
+150                may_fragment |= !set_socket_option_supported(
+151                    &*io,
+152                    libc::IPPROTO_IPV6,
+153                    libc::IPV6_MTU_DISCOVER,
+154                    libc::IPV6_PMTUDISC_PROBE,
+155                )?;
+156            }
+157        }
+158        #[cfg(any(target_os = "freebsd", apple))]
+159        {
+160            if is_ipv4 {
+161                // Set `may_fragment` to `true` if this option is not supported on the platform.
+162                may_fragment |= !set_socket_option_supported(
+163                    &*io,
+164                    libc::IPPROTO_IP,
+165                    libc::IP_DONTFRAG,
+166                    OPTION_ON,
+167                )?;
+168            }
+169        }
+170        #[cfg(any(bsd, apple, solarish))]
+171        // IP_RECVDSTADDR == IP_SENDSRCADDR on FreeBSD
+172        // macOS uses only IP_RECVDSTADDR, no IP_SENDSRCADDR on macOS (the same on Solaris)
+173        // macOS also supports IP_PKTINFO
+174        {
+175            if is_ipv4 {
+176                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, OPTION_ON)?;
+177            }
+178        }
+179
+180        // Options standardized in RFC 3542
+181        if !is_ipv4 {
+182            set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, OPTION_ON)?;
+183            set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVTCLASS, OPTION_ON)?;
+184            // Linux's IP_PMTUDISC_PROBE allows us to operate under interface MTU rather than the
+185            // kernel's path MTU guess, but actually disabling fragmentation requires this too. See
+186            // __ip6_append_data in ip6_output.c.
+187            // Set `may_fragment` to `true` if this option is not supported on the platform.
+188            may_fragment |=
+189                !set_socket_option_supported(&*io, libc::IPPROTO_IPV6, IPV6_DONTFRAG, OPTION_ON)?;
+190        }
+191
+192        let now = Instant::now();
+193        Ok(Self {
+194            last_send_error: Mutex::new(now.checked_sub(2 * IO_ERROR_LOG_INTERVAL).unwrap_or(now)),
+195            max_gso_segments: AtomicUsize::new(gso::max_gso_segments()),
+196            gro_segments: gro::gro_segments(),
+197            may_fragment,
+198            sendmsg_einval: AtomicBool::new(false),
+199        })
+200    }
+201
+202    /// Sends a [`Transmit`] on the given socket.
+203    ///
+204    /// This function will only ever return errors of kind [`io::ErrorKind::WouldBlock`].
+205    /// All other errors will be logged and converted to `Ok`.
+206    ///
+207    /// UDP transmission errors are considered non-fatal because higher-level protocols must
+208    /// employ retransmits and timeouts anyway in order to deal with UDP's unreliable nature.
+209    /// Thus, logging is most likely the only thing you can do with these errors.
+210    ///
+211    /// If you would like to handle these errors yourself, use [`UdpSocketState::try_send`]
+212    /// instead.
+213    pub fn send(&self, socket: UdpSockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+214        match send(self, socket.0, transmit) {
+215            Ok(()) => Ok(()),
+216            Err(e) if e.kind() == io::ErrorKind::WouldBlock => Err(e),
+217            // - EMSGSIZE is expected for MTU probes. Future work might be able to avoid
+218            //   these by automatically clamping the MTUD upper bound to the interface MTU.
+219            Err(e) if e.raw_os_error() == Some(libc::EMSGSIZE) => Ok(()),
+220            Err(e) => {
+221                log_sendmsg_error(&self.last_send_error, e, transmit);
+222
+223                Ok(())
+224            }
+225        }
+226    }
+227
+228    /// Sends a [`Transmit`] on the given socket without any additional error handling.
+229    pub fn try_send(&self, socket: UdpSockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+230        send(self, socket.0, transmit)
+231    }
+232
+233    pub fn recv(
+234        &self,
+235        socket: UdpSockRef<'_>,
+236        bufs: &mut [IoSliceMut<'_>],
+237        meta: &mut [RecvMeta],
+238    ) -> io::Result<usize> {
+239        recv(socket.0, bufs, meta)
+240    }
+241
+242    /// The maximum amount of segments which can be transmitted if a platform
+243    /// supports Generic Send Offload (GSO).
+244    ///
+245    /// This is 1 if the platform doesn't support GSO. Subject to change if errors are detected
+246    /// while using GSO.
+247    #[inline]
+248    pub fn max_gso_segments(&self) -> NonZeroUsize {
+249        self.max_gso_segments
+250            .load(Ordering::Relaxed)
+251            .try_into()
+252            .expect("must have non zero GSO segments")
+253    }
+254
+255    /// The number of segments to read when GRO is enabled. Used as a factor to
+256    /// compute the receive buffer size.
+257    ///
+258    /// Returns 1 if the platform doesn't support GRO.
+259    #[inline]
+260    pub fn gro_segments(&self) -> NonZeroUsize {
+261        self.gro_segments
+262    }
+263
+264    /// Resize the send buffer of `socket` to `bytes`
+265    #[inline]
+266    pub fn set_send_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
+267        socket.0.set_send_buffer_size(bytes)
+268    }
+269
+270    /// Resize the receive buffer of `socket` to `bytes`
+271    #[inline]
+272    pub fn set_recv_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
+273        socket.0.set_recv_buffer_size(bytes)
+274    }
+275
+276    /// Get the size of the `socket` send buffer
+277    #[inline]
+278    pub fn send_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
+279        socket.0.send_buffer_size()
+280    }
+281
+282    /// Get the size of the `socket` receive buffer
+283    #[inline]
+284    pub fn recv_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
+285        socket.0.recv_buffer_size()
+286    }
+287
+288    /// Whether transmitted datagrams might get fragmented by the IP layer
+289    ///
+290    /// Returns `false` on targets which employ e.g. the `IPV6_DONTFRAG` socket option.
+291    #[inline]
+292    pub fn may_fragment(&self) -> bool {
+293        self.may_fragment
+294    }
+295
+296    /// Returns true if we previously got an EINVAL error from `sendmsg` syscall.
+297    fn sendmsg_einval(&self) -> bool {
+298        self.sendmsg_einval.load(Ordering::Relaxed)
+299    }
+300
+301    /// Sets the flag indicating we got EINVAL error from `sendmsg` syscall.
+302    #[cfg(not(any(apple, target_os = "openbsd", target_os = "netbsd")))]
+303    fn set_sendmsg_einval(&self) {
+304        self.sendmsg_einval.store(true, Ordering::Relaxed)
+305    }
+306}
+307
+308#[cfg(not(any(apple, target_os = "openbsd", target_os = "netbsd")))]
+309fn send(
+310    #[allow(unused_variables)] // only used on Linux
+311    state: &UdpSocketState,
+312    io: SockRef<'_>,
+313    transmit: &Transmit<'_>,
+314) -> io::Result<()> {
+315    #[allow(unused_mut)] // only mutable on FreeBSD
+316    let mut encode_src_ip = true;
+317    #[cfg(target_os = "freebsd")]
+318    {
+319        let addr = io.local_addr()?;
+320        let is_ipv4 = addr.family() == libc::AF_INET as libc::sa_family_t;
+321        if is_ipv4 {
+322            if let Some(socket) = addr.as_socket_ipv4() {
+323                encode_src_ip = socket.ip() == &Ipv4Addr::UNSPECIFIED;
+324            }
+325        }
+326    }
+327    let mut msg_hdr: libc::msghdr = unsafe { mem::zeroed() };
+328    let mut iovec: libc::iovec = unsafe { mem::zeroed() };
+329    let mut cmsgs = cmsg::Aligned([0u8; CMSG_LEN]);
+330    let dst_addr = socket2::SockAddr::from(transmit.destination);
+331    prepare_msg(
+332        transmit,
+333        &dst_addr,
+334        &mut msg_hdr,
+335        &mut iovec,
+336        &mut cmsgs,
+337        encode_src_ip,
+338        state.sendmsg_einval(),
+339    );
+340
+341    loop {
+342        let n = unsafe { libc::sendmsg(io.as_raw_fd(), &msg_hdr, 0) };
+343
+344        if n >= 0 {
+345            return Ok(());
+346        }
+347
+348        let e = io::Error::last_os_error();
+349        match e.kind() {
+350            // Retry the transmission
+351            io::ErrorKind::Interrupted => continue,
+352            io::ErrorKind::WouldBlock => return Err(e),
+353            _ => {
+354                // Some network adapters and drivers do not support GSO. Unfortunately, Linux
+355                // offers no easy way for us to detect this short of an EIO or sometimes EINVAL
+356                // when we try to actually send datagrams using it.
+357                #[cfg(any(target_os = "linux", target_os = "android"))]
+358                if let Some(libc::EIO) | Some(libc::EINVAL) = e.raw_os_error() {
+359                    // Prevent new transmits from being scheduled using GSO. Existing GSO transmits
+360                    // may already be in the pipeline, so we need to tolerate additional failures.
+361                    if state.max_gso_segments().get() > 1 {
+362                        crate::log::info!(
+363                            "`libc::sendmsg` failed with {e}; halting segmentation offload"
+364                        );
+365                        state
+366                            .max_gso_segments
+367                            .store(1, std::sync::atomic::Ordering::Relaxed);
+368                    }
+369                }
+370
+371                // Some arguments to `sendmsg` are not supported. Switch to
+372                // fallback mode and retry if we haven't already.
+373                if e.raw_os_error() == Some(libc::EINVAL) && !state.sendmsg_einval() {
+374                    state.set_sendmsg_einval();
+375                    prepare_msg(
+376                        transmit,
+377                        &dst_addr,
+378                        &mut msg_hdr,
+379                        &mut iovec,
+380                        &mut cmsgs,
+381                        encode_src_ip,
+382                        state.sendmsg_einval(),
+383                    );
+384                    continue;
+385                }
+386
+387                return Err(e);
+388            }
+389        }
+390    }
+391}
+392
+393#[cfg(apple_fast)]
+394fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+395    let mut hdrs = unsafe { mem::zeroed::<[msghdr_x; BATCH_SIZE]>() };
+396    let mut iovs = unsafe { mem::zeroed::<[libc::iovec; BATCH_SIZE]>() };
+397    let mut ctrls = [cmsg::Aligned([0u8; CMSG_LEN]); BATCH_SIZE];
+398    let addr = socket2::SockAddr::from(transmit.destination);
+399    let segment_size = transmit.segment_size.unwrap_or(transmit.contents.len());
+400    let mut cnt = 0;
+401    debug_assert!(transmit.contents.len().div_ceil(segment_size) <= BATCH_SIZE);
+402    for (i, chunk) in transmit
+403        .contents
+404        .chunks(segment_size)
+405        .enumerate()
+406        .take(BATCH_SIZE)
+407    {
+408        prepare_msg(
+409            &Transmit {
+410                destination: transmit.destination,
+411                ecn: transmit.ecn,
+412                contents: chunk,
+413                segment_size: Some(chunk.len()),
+414                src_ip: transmit.src_ip,
+415            },
+416            &addr,
+417            &mut hdrs[i],
+418            &mut iovs[i],
+419            &mut ctrls[i],
+420            true,
+421            state.sendmsg_einval(),
+422        );
+423        hdrs[i].msg_datalen = chunk.len();
+424        cnt += 1;
+425    }
+426    loop {
+427        let n = unsafe { sendmsg_x(io.as_raw_fd(), hdrs.as_ptr(), cnt as u32, 0) };
+428
+429        if n >= 0 {
+430            return Ok(());
+431        }
+432
+433        let e = io::Error::last_os_error();
+434        match e.kind() {
+435            // Retry the transmission
+436            io::ErrorKind::Interrupted => continue,
+437            _ => return Err(e),
+438        }
+439    }
+440}
+441
+442#[cfg(any(target_os = "openbsd", target_os = "netbsd", apple_slow))]
+443fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+444    let mut hdr: libc::msghdr = unsafe { mem::zeroed() };
+445    let mut iov: libc::iovec = unsafe { mem::zeroed() };
+446    let mut ctrl = cmsg::Aligned([0u8; CMSG_LEN]);
+447    let addr = socket2::SockAddr::from(transmit.destination);
+448    prepare_msg(
+449        transmit,
+450        &addr,
+451        &mut hdr,
+452        &mut iov,
+453        &mut ctrl,
+454        cfg!(apple) || cfg!(target_os = "openbsd") || cfg!(target_os = "netbsd"),
+455        state.sendmsg_einval(),
+456    );
+457    loop {
+458        let n = unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) };
+459
+460        if n >= 0 {
+461            return Ok(());
+462        }
+463
+464        let e = io::Error::last_os_error();
+465        match e.kind() {
+466            // Retry the transmission
+467            io::ErrorKind::Interrupted => continue,
+468            _ => return Err(e),
+469        }
+470    }
+471}
+472
+473#[cfg(not(any(
+474    apple,
+475    target_os = "openbsd",
+476    target_os = "netbsd",
+477    target_os = "dragonfly",
+478    solarish
+479)))]
+480fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize> {
+481    let mut names = [MaybeUninit::<libc::sockaddr_storage>::uninit(); BATCH_SIZE];
+482    let mut ctrls = [cmsg::Aligned(MaybeUninit::<[u8; CMSG_LEN]>::uninit()); BATCH_SIZE];
+483    let mut hdrs = unsafe { mem::zeroed::<[libc::mmsghdr; BATCH_SIZE]>() };
+484    let max_msg_count = bufs.len().min(BATCH_SIZE);
+485    for i in 0..max_msg_count {
+486        prepare_recv(
+487            &mut bufs[i],
+488            &mut names[i],
+489            &mut ctrls[i],
+490            &mut hdrs[i].msg_hdr,
+491        );
+492    }
+493    let msg_count = loop {
+494        let n = unsafe {
+495            libc::recvmmsg(
+496                io.as_raw_fd(),
+497                hdrs.as_mut_ptr(),
+498                bufs.len().min(BATCH_SIZE) as _,
+499                0,
+500                ptr::null_mut::<libc::timespec>(),
+501            )
+502        };
+503
+504        if n >= 0 {
+505            break n;
+506        }
+507
+508        let e = io::Error::last_os_error();
+509        match e.kind() {
+510            // Retry receiving
+511            io::ErrorKind::Interrupted => continue,
+512            _ => return Err(e),
+513        }
+514    };
+515    for i in 0..(msg_count as usize) {
+516        meta[i] = decode_recv(&names[i], &hdrs[i].msg_hdr, hdrs[i].msg_len as usize)?;
+517    }
+518    Ok(msg_count as usize)
+519}
+520
+521#[cfg(apple_fast)]
+522fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize> {
+523    let mut names = [MaybeUninit::<libc::sockaddr_storage>::uninit(); BATCH_SIZE];
+524    // MacOS 10.15 `recvmsg_x` does not override the `msghdr_x`
+525    // `msg_controllen`. Thus, after the call to `recvmsg_x`, one does not know
+526    // which control messages have been written to. To prevent reading
+527    // uninitialized memory, do not use `MaybeUninit` for `ctrls`, instead
+528    // initialize `ctrls` with `0`s. A control message of all `0`s is
+529    // automatically skipped by `libc::CMSG_NXTHDR`.
+530    let mut ctrls = [cmsg::Aligned([0u8; CMSG_LEN]); BATCH_SIZE];
+531    let mut hdrs = unsafe { mem::zeroed::<[msghdr_x; BATCH_SIZE]>() };
+532    let max_msg_count = bufs.len().min(BATCH_SIZE);
+533    for i in 0..max_msg_count {
+534        prepare_recv(&mut bufs[i], &mut names[i], &mut ctrls[i], &mut hdrs[i]);
+535    }
+536    let msg_count = loop {
+537        let n = unsafe { recvmsg_x(io.as_raw_fd(), hdrs.as_mut_ptr(), max_msg_count as _, 0) };
+538
+539        if n >= 0 {
+540            break n;
+541        }
+542
+543        let e = io::Error::last_os_error();
+544        match e.kind() {
+545            // Retry receiving
+546            io::ErrorKind::Interrupted => continue,
+547            _ => return Err(e),
+548        }
+549    };
+550    for i in 0..(msg_count as usize) {
+551        meta[i] = decode_recv(&names[i], &hdrs[i], hdrs[i].msg_datalen as usize)?;
+552    }
+553    Ok(msg_count as usize)
+554}
+555
+556#[cfg(any(
+557    target_os = "openbsd",
+558    target_os = "netbsd",
+559    target_os = "dragonfly",
+560    solarish,
+561    apple_slow
+562))]
+563fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize> {
+564    let mut name = MaybeUninit::<libc::sockaddr_storage>::uninit();
+565    let mut ctrl = cmsg::Aligned(MaybeUninit::<[u8; CMSG_LEN]>::uninit());
+566    let mut hdr = unsafe { mem::zeroed::<libc::msghdr>() };
+567    prepare_recv(&mut bufs[0], &mut name, &mut ctrl, &mut hdr);
+568    let n = loop {
+569        let n = unsafe { libc::recvmsg(io.as_raw_fd(), &mut hdr, 0) };
+570
+571        if hdr.msg_flags & libc::MSG_TRUNC != 0 {
+572            continue;
+573        }
+574
+575        if n >= 0 {
+576            break n;
+577        }
+578
+579        let e = io::Error::last_os_error();
+580        match e.kind() {
+581            // Retry receiving
+582            io::ErrorKind::Interrupted => continue,
+583            _ => return Err(e),
+584        }
+585    };
+586    meta[0] = decode_recv(&name, &hdr, n as usize)?;
+587    Ok(1)
+588}
+589
+590const CMSG_LEN: usize = 88;
+591
+592fn prepare_msg(
+593    transmit: &Transmit<'_>,
+594    dst_addr: &socket2::SockAddr,
+595    #[cfg(not(apple_fast))] hdr: &mut libc::msghdr,
+596    #[cfg(apple_fast)] hdr: &mut msghdr_x,
+597    iov: &mut libc::iovec,
+598    ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
+599    #[allow(unused_variables)] // only used on FreeBSD & macOS
+600    encode_src_ip: bool,
+601    sendmsg_einval: bool,
+602) {
+603    iov.iov_base = transmit.contents.as_ptr() as *const _ as *mut _;
+604    iov.iov_len = transmit.contents.len();
+605
+606    // SAFETY: Casting the pointer to a mutable one is legal,
+607    // as sendmsg is guaranteed to not alter the mutable pointer
+608    // as per the POSIX spec. See the section on the sys/socket.h
+609    // header for details. The type is only mutable in the first
+610    // place because it is reused by recvmsg as well.
+611    let name = dst_addr.as_ptr() as *mut libc::c_void;
+612    let namelen = dst_addr.len();
+613    hdr.msg_name = name as *mut _;
+614    hdr.msg_namelen = namelen;
+615    hdr.msg_iov = iov;
+616    hdr.msg_iovlen = 1;
+617
+618    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+619    hdr.msg_controllen = CMSG_LEN as _;
+620    let mut encoder = unsafe { cmsg::Encoder::new(hdr) };
+621    let ecn = transmit.ecn.map_or(0, |x| x as libc::c_int);
+622    // True for IPv4 or IPv4-Mapped IPv6
+623    let is_ipv4 = transmit.destination.is_ipv4()
+624        || matches!(transmit.destination.ip(), IpAddr::V6(addr) if addr.to_ipv4_mapped().is_some());
+625    if is_ipv4 {
+626        if !sendmsg_einval {
+627            #[cfg(not(target_os = "netbsd"))]
+628            {
+629                encoder.push(libc::IPPROTO_IP, libc::IP_TOS, ecn as IpTosTy);
+630            }
+631        }
+632    } else {
+633        encoder.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
+634    }
+635
+636    // Only set the segment size if it is less than the size of the contents.
+637    // Some network drivers don't like being told to do GSO even if there is effectively only a single segment (i.e. `segment_size == transmit.contents.len()`)
+638    // Additionally, a `segment_size` that is greater than the content also means there is effectively only a single segment.
+639    // This case is actually quite common when splitting up a prepared GSO batch again after GSO has been disabled because the last datagram in a GSO batch is allowed to be smaller than the segment size.
+640    if let Some(segment_size) = transmit
+641        .segment_size
+642        .filter(|segment_size| *segment_size < transmit.contents.len())
+643    {
+644        gso::set_segment_size(&mut encoder, segment_size as u16);
+645    }
+646
+647    if let Some(ip) = &transmit.src_ip {
+648        match ip {
+649            IpAddr::V4(v4) => {
+650                #[cfg(any(target_os = "linux", target_os = "android"))]
+651                {
+652                    let pktinfo = libc::in_pktinfo {
+653                        ipi_ifindex: 0,
+654                        ipi_spec_dst: libc::in_addr {
+655                            s_addr: u32::from_ne_bytes(v4.octets()),
+656                        },
+657                        ipi_addr: libc::in_addr { s_addr: 0 },
+658                    };
+659                    encoder.push(libc::IPPROTO_IP, libc::IP_PKTINFO, pktinfo);
+660                }
+661                #[cfg(any(bsd, apple, solarish))]
+662                {
+663                    if encode_src_ip {
+664                        let addr = libc::in_addr {
+665                            s_addr: u32::from_ne_bytes(v4.octets()),
+666                        };
+667                        encoder.push(libc::IPPROTO_IP, libc::IP_RECVDSTADDR, addr);
+668                    }
+669                }
+670            }
+671            IpAddr::V6(v6) => {
+672                let pktinfo = libc::in6_pktinfo {
+673                    ipi6_ifindex: 0,
+674                    ipi6_addr: libc::in6_addr {
+675                        s6_addr: v6.octets(),
+676                    },
+677                };
+678                encoder.push(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO, pktinfo);
+679            }
+680        }
+681    }
+682
+683    encoder.finish();
+684}
+685
+686#[cfg(not(apple_fast))]
+687fn prepare_recv(
+688    buf: &mut IoSliceMut,
+689    name: &mut MaybeUninit<libc::sockaddr_storage>,
+690    ctrl: &mut cmsg::Aligned<MaybeUninit<[u8; CMSG_LEN]>>,
+691    hdr: &mut libc::msghdr,
+692) {
+693    hdr.msg_name = name.as_mut_ptr() as _;
+694    hdr.msg_namelen = mem::size_of::<libc::sockaddr_storage>() as _;
+695    hdr.msg_iov = buf as *mut IoSliceMut as *mut libc::iovec;
+696    hdr.msg_iovlen = 1;
+697    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+698    hdr.msg_controllen = CMSG_LEN as _;
+699    hdr.msg_flags = 0;
+700}
+701
+702#[cfg(apple_fast)]
+703fn prepare_recv(
+704    buf: &mut IoSliceMut,
+705    name: &mut MaybeUninit<libc::sockaddr_storage>,
+706    ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
+707    hdr: &mut msghdr_x,
+708) {
+709    hdr.msg_name = name.as_mut_ptr() as _;
+710    hdr.msg_namelen = mem::size_of::<libc::sockaddr_storage>() as _;
+711    hdr.msg_iov = buf as *mut IoSliceMut as *mut libc::iovec;
+712    hdr.msg_iovlen = 1;
+713    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+714    hdr.msg_controllen = CMSG_LEN as _;
+715    hdr.msg_flags = 0;
+716    hdr.msg_datalen = buf.len();
+717}
+718
+719fn decode_recv(
+720    name: &MaybeUninit<libc::sockaddr_storage>,
+721    #[cfg(not(apple_fast))] hdr: &libc::msghdr,
+722    #[cfg(apple_fast)] hdr: &msghdr_x,
+723    len: usize,
+724) -> io::Result<RecvMeta> {
+725    let name = unsafe { name.assume_init() };
+726    let mut ecn_bits = 0;
+727    let mut dst_ip = None;
+728    let mut interface_index = None;
+729    #[allow(unused_mut)] // only mutable on Linux
+730    let mut stride = len;
+731
+732    let cmsg_iter = unsafe { cmsg::Iter::new(hdr) };
+733    for cmsg in cmsg_iter {
+734        match (cmsg.cmsg_level, cmsg.cmsg_type) {
+735            (libc::IPPROTO_IP, libc::IP_TOS) => unsafe {
+736                ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+737            },
+738            // FreeBSD uses IP_RECVTOS here, and we can be liberal because cmsgs are opt-in.
+739            #[cfg(not(any(
+740                target_os = "openbsd",
+741                target_os = "netbsd",
+742                target_os = "dragonfly",
+743                solarish
+744            )))]
+745            (libc::IPPROTO_IP, libc::IP_RECVTOS) => unsafe {
+746                ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+747            },
+748            (libc::IPPROTO_IPV6, libc::IPV6_TCLASS) => unsafe {
+749                // Temporary hack around broken macos ABI. Remove once upstream fixes it.
+750                // https://bugreport.apple.com/web/?problemID=48761855
+751                #[allow(clippy::unnecessary_cast)] // cmsg.cmsg_len defined as size_t
+752                if cfg!(apple)
+753                    && cmsg.cmsg_len as usize == libc::CMSG_LEN(mem::size_of::<u8>() as _) as usize
+754                {
+755                    ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+756                } else {
+757                    ecn_bits = cmsg::decode::<libc::c_int, libc::cmsghdr>(cmsg) as u8;
+758                }
+759            },
+760            #[cfg(any(target_os = "linux", target_os = "android"))]
+761            (libc::IPPROTO_IP, libc::IP_PKTINFO) => {
+762                let pktinfo = unsafe { cmsg::decode::<libc::in_pktinfo, libc::cmsghdr>(cmsg) };
+763                dst_ip = Some(IpAddr::V4(Ipv4Addr::from(
+764                    pktinfo.ipi_addr.s_addr.to_ne_bytes(),
+765                )));
+766                interface_index = Some(pktinfo.ipi_ifindex as u32);
+767            }
+768            #[cfg(any(bsd, apple))]
+769            (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
+770                let in_addr = unsafe { cmsg::decode::<libc::in_addr, libc::cmsghdr>(cmsg) };
+771                dst_ip = Some(IpAddr::V4(Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())));
+772            }
+773            (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
+774                let pktinfo = unsafe { cmsg::decode::<libc::in6_pktinfo, libc::cmsghdr>(cmsg) };
+775                dst_ip = Some(IpAddr::V6(Ipv6Addr::from(pktinfo.ipi6_addr.s6_addr)));
+776                interface_index = Some(pktinfo.ipi6_ifindex as u32);
+777            }
+778            #[cfg(any(target_os = "linux", target_os = "android"))]
+779            (libc::SOL_UDP, gro::UDP_GRO) => unsafe {
+780                stride = cmsg::decode::<libc::c_int, libc::cmsghdr>(cmsg) as usize;
+781            },
+782            _ => {}
+783        }
+784    }
+785
+786    let addr = match libc::c_int::from(name.ss_family) {
+787        libc::AF_INET => {
+788            // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
+789            let addr: &libc::sockaddr_in =
+790                unsafe { &*(&name as *const _ as *const libc::sockaddr_in) };
+791            SocketAddr::V4(SocketAddrV4::new(
+792                Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()),
+793                u16::from_be(addr.sin_port),
+794            ))
+795        }
+796        libc::AF_INET6 => {
+797            // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
+798            let addr: &libc::sockaddr_in6 =
+799                unsafe { &*(&name as *const _ as *const libc::sockaddr_in6) };
+800            SocketAddr::V6(SocketAddrV6::new(
+801                Ipv6Addr::from(addr.sin6_addr.s6_addr),
+802                u16::from_be(addr.sin6_port),
+803                addr.sin6_flowinfo,
+804                addr.sin6_scope_id,
+805            ))
+806        }
+807        f => {
+808            return Err(io::Error::other(format!(
+809                "expected AF_INET or AF_INET6, got {f} in decode_recv"
+810            )));
+811        }
+812    };
+813
+814    Ok(RecvMeta {
+815        len,
+816        stride,
+817        addr,
+818        ecn: EcnCodepoint::from_bits(ecn_bits),
+819        dst_ip,
+820        interface_index,
+821    })
+822}
+823
+824#[cfg(not(apple_slow))]
+825// Chosen somewhat arbitrarily; might benefit from additional tuning.
+826pub(crate) const BATCH_SIZE: usize = 32;
+827
+828#[cfg(apple_slow)]
+829pub(crate) const BATCH_SIZE: usize = 1;
+830
+831#[cfg(any(target_os = "linux", target_os = "android"))]
+832mod gso {
+833    use super::*;
+834    use std::{ffi::CStr, mem, str::FromStr, sync::OnceLock};
+835
+836    #[cfg(not(target_os = "android"))]
+837    const UDP_SEGMENT: libc::c_int = libc::UDP_SEGMENT;
+838    #[cfg(target_os = "android")]
+839    // TODO: Add this to libc
+840    const UDP_SEGMENT: libc::c_int = 103;
+841
+842    // Support for UDP GSO has been added to linux kernel in version 4.18
+843    // https://github.com/torvalds/linux/commit/cb586c63e3fc5b227c51fd8c4cb40b34d3750645
+844    const SUPPORTED_SINCE: KernelVersion = KernelVersion {
+845        version: 4,
+846        major_revision: 18,
+847    };
+848
+849    /// Checks whether GSO support is available by checking the kernel version followed by setting
+850    /// the UDP_SEGMENT option on a socket
+851    pub(crate) fn max_gso_segments() -> usize {
+852        const GSO_SIZE: libc::c_int = 1500;
+853
+854        if !SUPPORTED_BY_CURRENT_KERNEL.get_or_init(supported_by_current_kernel) {
+855            return 1;
+856        }
+857
+858        let socket = match std::net::UdpSocket::bind("[::]:0")
+859            .or_else(|_| std::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)))
+860        {
+861            Ok(socket) => socket,
+862            Err(_) => return 1,
+863        };
+864
+865        // As defined in linux/udp.h
+866        // #define UDP_MAX_SEGMENTS        (1 << 6UL)
+867        match set_socket_option(&socket, libc::SOL_UDP, UDP_SEGMENT, GSO_SIZE) {
+868            Ok(()) => 64,
+869            Err(_e) => {
+870                crate::log::debug!(
+871                    "failed to set `UDP_SEGMENT` socket option ({_e}); setting `max_gso_segments = 1`"
+872                );
+873
+874                1
+875            }
+876        }
+877    }
+878
+879    pub(crate) fn set_segment_size(encoder: &mut cmsg::Encoder<libc::msghdr>, segment_size: u16) {
+880        encoder.push(libc::SOL_UDP, UDP_SEGMENT, segment_size);
+881    }
+882
+883    // Avoid calling `supported_by_current_kernel` for each socket by using `OnceLock`.
+884    static SUPPORTED_BY_CURRENT_KERNEL: OnceLock<bool> = OnceLock::new();
+885
+886    fn supported_by_current_kernel() -> bool {
+887        let kernel_version_string = match kernel_version_string() {
+888            Ok(kernel_version_string) => kernel_version_string,
+889            Err(_e) => {
+890                crate::log::warn!("GSO disabled: uname returned {_e}");
+891                return false;
+892            }
+893        };
+894
+895        let Some(kernel_version) = KernelVersion::from_str(&kernel_version_string) else {
+896            crate::log::warn!(
+897                "GSO disabled: failed to parse kernel version ({kernel_version_string})"
+898            );
+899            return false;
+900        };
+901
+902        if kernel_version < SUPPORTED_SINCE {
+903            crate::log::info!("GSO disabled: kernel too old ({kernel_version_string}); need 4.18+",);
+904            return false;
+905        }
+906
+907        true
+908    }
+909
+910    fn kernel_version_string() -> io::Result<String> {
+911        let mut n = unsafe { mem::zeroed() };
+912        let r = unsafe { libc::uname(&mut n) };
+913        if r != 0 {
+914            return Err(io::Error::last_os_error());
+915        }
+916        Ok(unsafe {
+917            CStr::from_ptr(n.release[..].as_ptr())
+918                .to_string_lossy()
+919                .into_owned()
+920        })
+921    }
+922
+923    // https://www.linfo.org/kernel_version_numbering.html
+924    #[derive(Eq, PartialEq, Ord, PartialOrd, Debug)]
+925    struct KernelVersion {
+926        version: u8,
+927        major_revision: u8,
+928    }
+929
+930    impl KernelVersion {
+931        fn from_str(release: &str) -> Option<Self> {
+932            let mut split = release
+933                .split_once('-')
+934                .map(|pair| pair.0)
+935                .unwrap_or(release)
+936                .split('.');
+937
+938            let version = u8::from_str(split.next()?).ok()?;
+939            let major_revision = u8::from_str(split.next()?).ok()?;
+940
+941            Some(Self {
+942                version,
+943                major_revision,
+944            })
+945        }
+946    }
+947
+948    #[cfg(test)]
+949    mod test {
+950        use super::*;
+951
+952        #[test]
+953        fn parse_current_kernel_version_release_string() {
+954            let release = kernel_version_string().unwrap();
+955            KernelVersion::from_str(&release).unwrap();
+956        }
+957
+958        #[test]
+959        fn parse_kernel_version_release_string() {
+960            // These are made up for the test
+961            assert_eq!(
+962                KernelVersion::from_str("4.14"),
+963                Some(KernelVersion {
+964                    version: 4,
+965                    major_revision: 14
+966                })
+967            );
+968            assert_eq!(
+969                KernelVersion::from_str("4.18"),
+970                Some(KernelVersion {
+971                    version: 4,
+972                    major_revision: 18
+973                })
+974            );
+975            // These were seen in the wild
+976            assert_eq!(
+977                KernelVersion::from_str("4.14.186-27095505"),
+978                Some(KernelVersion {
+979                    version: 4,
+980                    major_revision: 14
+981                })
+982            );
+983            assert_eq!(
+984                KernelVersion::from_str("6.8.0-59-generic"),
+985                Some(KernelVersion {
+986                    version: 6,
+987                    major_revision: 8
+988                })
+989            );
+990        }
+991    }
+992}
+993
+994// On Apple platforms using the `sendmsg_x` call, UDP datagram segmentation is not
+995// offloaded to the NIC or even the kernel, but instead done here in user space in
+996// [`send`]) and then passed to the OS as individual `iovec`s (up to `BATCH_SIZE`).
+997#[cfg(not(any(target_os = "linux", target_os = "android")))]
+998mod gso {
+999    use super::*;
+1000
+1001    pub(super) fn max_gso_segments() -> usize {
+1002        #[cfg(apple_fast)]
+1003        {
+1004            BATCH_SIZE
+1005        }
+1006        #[cfg(not(apple_fast))]
+1007        {
+1008            1
+1009        }
+1010    }
+1011
+1012    pub(super) fn set_segment_size(
+1013        #[cfg(not(apple_fast))] _encoder: &mut cmsg::Encoder<libc::msghdr>,
+1014        #[cfg(apple_fast)] _encoder: &mut cmsg::Encoder<msghdr_x>,
+1015        _segment_size: u16,
+1016    ) {
+1017    }
+1018}
+1019
+1020#[cfg(any(target_os = "linux", target_os = "android"))]
+1021mod gro {
+1022    use super::*;
+1023
+1024    #[cfg(not(target_os = "android"))]
+1025    pub(crate) const UDP_GRO: libc::c_int = libc::UDP_GRO;
+1026    #[cfg(target_os = "android")]
+1027    // TODO: Add this to libc
+1028    pub(crate) const UDP_GRO: libc::c_int = 104;
+1029
+1030    pub(crate) fn gro_segments() -> NonZeroUsize {
+1031        let socket = match std::net::UdpSocket::bind("[::]:0")
+1032            .or_else(|_| std::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)))
+1033        {
+1034            Ok(socket) => socket,
+1035            Err(_) => return NonZeroUsize::MIN,
+1036        };
+1037
+1038        // As defined in net/ipv4/udp_offload.c
+1039        // #define UDP_GRO_CNT_MAX 64
+1040        //
+1041        // NOTE: this MUST be set to UDP_GRO_CNT_MAX to ensure that the receive buffer size
+1042        // (get_max_udp_payload_size() * gro_segments()) is large enough to hold the largest GRO
+1043        // list the kernel might potentially produce. See
+1044        // https://github.com/quinn-rs/quinn/pull/1354.
+1045        match set_socket_option(&socket, libc::SOL_UDP, UDP_GRO, OPTION_ON) {
+1046            Ok(()) => NonZeroUsize::new(64).expect("known"),
+1047            Err(_) => NonZeroUsize::MIN,
+1048        }
+1049    }
+1050}
+1051
+1052/// Returns whether the given socket option is supported on the current platform
+1053///
+1054/// Yields `Ok(true)` if the option was set successfully, `Ok(false)` if setting
+1055/// the option raised an `ENOPROTOOPT` or `EOPNOTSUPP` error, and `Err` for any other error.
+1056fn set_socket_option_supported(
+1057    socket: &impl AsRawFd,
+1058    level: libc::c_int,
+1059    name: libc::c_int,
+1060    value: libc::c_int,
+1061) -> io::Result<bool> {
+1062    match set_socket_option(socket, level, name, value) {
+1063        Ok(()) => Ok(true),
+1064        Err(err) if err.raw_os_error() == Some(libc::ENOPROTOOPT) => Ok(false),
+1065        Err(err) if err.raw_os_error() == Some(libc::EOPNOTSUPP) => Ok(false),
+1066        Err(err) => Err(err),
+1067    }
+1068}
+1069
+1070fn set_socket_option(
+1071    socket: &impl AsRawFd,
+1072    level: libc::c_int,
+1073    name: libc::c_int,
+1074    value: libc::c_int,
+1075) -> io::Result<()> {
+1076    let rc = unsafe {
+1077        libc::setsockopt(
+1078            socket.as_raw_fd(),
+1079            level,
+1080            name,
+1081            &value as *const _ as _,
+1082            mem::size_of_val(&value) as _,
+1083        )
+1084    };
+1085
+1086    match rc == 0 {
+1087        true => Ok(()),
+1088        false => Err(io::Error::last_os_error()),
+1089    }
+1090}
+1091
+1092const OPTION_ON: libc::c_int = 1;
+1093
+1094#[cfg(not(any(target_os = "linux", target_os = "android")))]
+1095mod gro {
+1096    use std::num::NonZeroUsize;
+1097
+1098    pub(super) fn gro_segments() -> NonZeroUsize {
+1099        NonZeroUsize::MIN
+1100    }
+1101}
\ No newline at end of file diff --git a/pr/299/docs/src/perf/client.rs.html b/pr/299/docs/src/perf/client.rs.html new file mode 100644 index 000000000..37a94ceca --- /dev/null +++ b/pr/299/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/299/docs/src/perf/lib.rs.html b/pr/299/docs/src/perf/lib.rs.html new file mode 100644 index 000000000..4c2c22ac8 --- /dev/null +++ b/pr/299/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            CongestionAlgorithm::Cubic => Arc::new(congestion::CubicConfig::default()),
+209            CongestionAlgorithm::Bbr => Arc::new(congestion::BbrConfig::default()),
+210            CongestionAlgorithm::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/299/docs/src/perf/noprotection.rs.html b/pr/299/docs/src/perf/noprotection.rs.html new file mode 100644 index 000000000..a3028bc01 --- /dev/null +++ b/pr/299/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/299/docs/src/perf/server.rs.html b/pr/299/docs/src/perf/server.rs.html new file mode 100644 index 000000000..e1b75cd59 --- /dev/null +++ b/pr/299/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/299/docs/src/perf/stats.rs.html b/pr/299/docs/src/perf/stats.rs.html new file mode 100644 index 000000000..8d8d86e96 --- /dev/null +++ b/pr/299/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/299/docs/src/quinn_perf/perf.rs.html b/pr/299/docs/src/quinn_perf/perf.rs.html new file mode 100644 index 000000000..34363d0d1 --- /dev/null +++ b/pr/299/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/299/docs/src/set_up_connection/set-up-connection.rs.html b/pr/299/docs/src/set_up_connection/set-up-connection.rs.html new file mode 100644 index 000000000..a601750b4 --- /dev/null +++ b/pr/299/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/299/docs/static.files/COPYRIGHT-7fb11f4e.txt b/pr/299/docs/static.files/COPYRIGHT-7fb11f4e.txt new file mode 100644 index 000000000..752dab0a3 --- /dev/null +++ b/pr/299/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/299/docs/static.files/FiraMono-Medium-86f75c8c.woff2 b/pr/299/docs/static.files/FiraMono-Medium-86f75c8c.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..610e9b2071ec1d6c47a7815010acb20f0abbdd98 GIT binary patch literal 64572 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 N?7!cn*`i$bGXP%$q+|d9 literal 0 HcmV?d00001 diff --git a/pr/299/docs/static.files/FiraMono-Regular-87c26294.woff2 b/pr/299/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/299/docs/static.files/FiraSans-Italic-81dc35de.woff2 b/pr/299/docs/static.files/FiraSans-Italic-81dc35de.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..3f63664fee6ddf221f71a57461a7f4d1de281177 GIT binary patch literal 136300 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 _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/299/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt b/pr/299/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt new file mode 100644 index 000000000..d7e9c149b --- /dev/null +++ b/pr/299/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/299/docs/static.files/FiraSans-Medium-e1aa3f0a.woff2 b/pr/299/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/299/docs/static.files/FiraSans-MediumItalic-ccf7e434.woff2 b/pr/299/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/299/docs/static.files/FiraSans-Regular-0fe48ade.woff2 b/pr/299/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/299/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt b/pr/299/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt new file mode 100644 index 000000000..4b3edc29e --- /dev/null +++ b/pr/299/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/299/docs/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 b/pr/299/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/299/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt b/pr/299/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt new file mode 100644 index 000000000..0d2941e14 --- /dev/null +++ b/pr/299/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/299/docs/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 b/pr/299/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/299/docs/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 b/pr/299/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/299/docs/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 b/pr/299/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/299/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 b/pr/299/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/299/docs/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 b/pr/299/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/299/docs/static.files/favicon-044be391.svg b/pr/299/docs/static.files/favicon-044be391.svg new file mode 100644 index 000000000..8b34b5119 --- /dev/null +++ b/pr/299/docs/static.files/favicon-044be391.svg @@ -0,0 +1,24 @@ + + + + + diff --git a/pr/299/docs/static.files/favicon-32x32-eab170b8.png b/pr/299/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/299/docs/static.files/normalize-9960930a.css b/pr/299/docs/static.files/normalize-9960930a.css new file mode 100644 index 000000000..469959f13 --- /dev/null +++ b/pr/299/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/299/docs/static.files/noscript-263c88ec.css b/pr/299/docs/static.files/noscript-263c88ec.css new file mode 100644 index 000000000..e41db77dc --- /dev/null +++ b/pr/299/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/299/docs/static.files/rust-logo-9a9549ea.svg b/pr/299/docs/static.files/rust-logo-9a9549ea.svg new file mode 100644 index 000000000..62424d8ff --- /dev/null +++ b/pr/299/docs/static.files/rust-logo-9a9549ea.svg @@ -0,0 +1,61 @@ + + + diff --git a/pr/299/docs/static.files/rustdoc-ca0dd0c4.css b/pr/299/docs/static.files/rustdoc-ca0dd0c4.css new file mode 100644 index 000000000..7e6df69c2 --- /dev/null +++ b/pr/299/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/299/docs/static.files/scrape-examples-2bbcccac.js b/pr/299/docs/static.files/scrape-examples-2bbcccac.js new file mode 100644 index 000000000..c90f297f9 --- /dev/null +++ b/pr/299/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/299/docs/static.files/search-8d3311b9.js b/pr/299/docs/static.files/search-8d3311b9.js new file mode 100644 index 000000000..5da8debd8 --- /dev/null +++ b/pr/299/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/299/docs/static.files/settings-c38705f0.js b/pr/299/docs/static.files/settings-c38705f0.js new file mode 100644 index 000000000..7e4939e5c --- /dev/null +++ b/pr/299/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/299/docs/static.files/src-script-813739b1.js b/pr/299/docs/static.files/src-script-813739b1.js new file mode 100644 index 000000000..bf546257c --- /dev/null +++ b/pr/299/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/299/docs/static.files/storage-e2aeef58.js b/pr/299/docs/static.files/storage-e2aeef58.js new file mode 100644 index 000000000..fb76ad5e9 --- /dev/null +++ b/pr/299/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/299/docs/static.files/stringdex-828709d0.js b/pr/299/docs/static.files/stringdex-828709d0.js new file mode 100644 index 000000000..b8268d530 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/clap_builder/derive/trait.Args.js b/pr/299/docs/trait.impl/clap_builder/derive/trait.Args.js new file mode 100644 index 000000000..0ebbfe755 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js b/pr/299/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js new file mode 100644 index 000000000..6b11f051f --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js b/pr/299/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js new file mode 100644 index 000000000..48a955b6f --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/clap_builder/derive/trait.Parser.js b/pr/299/docs/trait.impl/clap_builder/derive/trait.Parser.js new file mode 100644 index 000000000..823b963bf --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/clap_builder/derive/trait.Subcommand.js b/pr/299/docs/trait.impl/clap_builder/derive/trait.Subcommand.js new file mode 100644 index 000000000..18f0f2f47 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js b/pr/299/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js new file mode 100644 index 000000000..c39cd5dd7 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/clone/trait.Clone.js b/pr/299/docs/trait.impl/core/clone/trait.Clone.js new file mode 100644 index 000000000..f78999e6a --- /dev/null +++ b/pr/299/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 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 EndpointConfig"],["impl Clone for FourTuple"],["impl Clone for FrameStats"],["impl Clone for IdleTimeout"],["impl Clone for InvalidCid"],["impl Clone for MtuDiscoveryConfig"],["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 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,18061,851,568]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/cmp/trait.Eq.js b/pr/299/docs/trait.impl/core/cmp/trait.Eq.js new file mode 100644 index 000000000..48b25c8fb --- /dev/null +++ b/pr/299/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 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 FourTuple"],["impl Eq for IdleTimeout"],["impl Eq for PathId"],["impl Eq for PathStats"],["impl Eq for ShouldTransmit"],["impl Eq for StreamId"],["impl Eq for Error"],["impl Eq for Code"],["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,11336,282]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/cmp/trait.Ord.js b/pr/299/docs/trait.impl/core/cmp/trait.Ord.js new file mode 100644 index 000000000..8af068c55 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/cmp/trait.PartialEq.js b/pr/299/docs/trait.impl/core/cmp/trait.PartialEq.js new file mode 100644 index 000000000..324658711 --- /dev/null +++ b/pr/299/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 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 FourTuple"],["impl PartialEq for IdleTimeout"],["impl PartialEq for PathId"],["impl PartialEq for PathStats"],["impl PartialEq for ShouldTransmit"],["impl PartialEq for StreamId"],["impl PartialEq for Error"],["impl PartialEq for Code"],["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,12218,303]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/cmp/trait.PartialOrd.js b/pr/299/docs/trait.impl/core/cmp/trait.PartialOrd.js new file mode 100644 index 000000000..fa81432c6 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/convert/trait.From.js b/pr/299/docs/trait.impl/core/convert/trait.From.js new file mode 100644 index 000000000..7873e1228 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/convert/trait.TryFrom.js b/pr/299/docs/trait.impl/core/convert/trait.TryFrom.js new file mode 100644 index 000000000..010a7eb0d --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/default/trait.Default.js b/pr/299/docs/trait.impl/core/default/trait.Default.js new file mode 100644 index 000000000..51d382b93 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/error/trait.Error.js b/pr/299/docs/trait.impl/core/error/trait.Error.js new file mode 100644 index 000000000..212b4f6df --- /dev/null +++ b/pr/299/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 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,6038]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/fmt/trait.Debug.js b/pr/299/docs/trait.impl/core/fmt/trait.Debug.js new file mode 100644 index 000000000..51de3dba4 --- /dev/null +++ b/pr/299/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 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 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 MtuDiscoveryConfig"],["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 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,23184,1121]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/fmt/trait.Display.js b/pr/299/docs/trait.impl/core/fmt/trait.Display.js new file mode 100644 index 000000000..2d939c9aa --- /dev/null +++ b/pr/299/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 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 FourTuple"],["impl Display for PathId"],["impl Display for RetryError"],["impl Display for StreamId"],["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,8860]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/future/future/trait.Future.js b/pr/299/docs/trait.impl/core/future/future/trait.Future.js new file mode 100644 index 000000000..3c2fb2ac9 --- /dev/null +++ b/pr/299/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 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":[3503]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/future/into_future/trait.IntoFuture.js b/pr/299/docs/trait.impl/core/future/into_future/trait.IntoFuture.js new file mode 100644 index 000000000..b07d4bb68 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/hash/trait.Hash.js b/pr/299/docs/trait.impl/core/hash/trait.Hash.js new file mode 100644 index 000000000..0018a136e --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/marker/trait.Copy.js b/pr/299/docs/trait.impl/core/marker/trait.Copy.js new file mode 100644 index 000000000..15b8d4fe0 --- /dev/null +++ b/pr/299/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 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 FourTuple"],["impl Copy for FrameStats"],["impl Copy for IdleTimeout"],["impl Copy for InvalidCid"],["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 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,7616,561,285]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/marker/trait.Freeze.js b/pr/299/docs/trait.impl/core/marker/trait.Freeze.js new file mode 100644 index 000000000..a17d6cc70 --- /dev/null +++ b/pr/299/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 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 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 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 MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["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 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,9899,35374,1629,2824,575]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/marker/trait.Send.js b/pr/299/docs/trait.impl/core/marker/trait.Send.js new file mode 100644 index 000000000..6ed39071c --- /dev/null +++ b/pr/299/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 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 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 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 MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["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 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,9697,34743,1598,2769,563]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/marker/trait.StructuralPartialEq.js b/pr/299/docs/trait.impl/core/marker/trait.StructuralPartialEq.js new file mode 100644 index 000000000..b17420186 --- /dev/null +++ b/pr/299/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 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 FourTuple"],["impl StructuralPartialEq for IdleTimeout"],["impl StructuralPartialEq for PathId"],["impl StructuralPartialEq for PathStats"],["impl StructuralPartialEq for ShouldTransmit"],["impl StructuralPartialEq for StreamId"],["impl StructuralPartialEq for Code"],["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,13405,339]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/marker/trait.Sync.js b/pr/299/docs/trait.impl/core/marker/trait.Sync.js new file mode 100644 index 000000000..d56b83334 --- /dev/null +++ b/pr/299/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 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 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 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 MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["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 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,9697,34743,1598,2769,563]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/marker/trait.Unpin.js b/pr/299/docs/trait.impl/core/marker/trait.Unpin.js new file mode 100644 index 000000000..0a4f95744 --- /dev/null +++ b/pr/299/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 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 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 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 MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["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 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,11146,35052,1613,2796,569]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/marker/trait.UnsafeUnpin.js b/pr/299/docs/trait.impl/core/marker/trait.UnsafeUnpin.js new file mode 100644 index 000000000..d0e3af770 --- /dev/null +++ b/pr/299/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 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 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 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 MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["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 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,5922,22177,988,1671,605]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/ops/bit/trait.Not.js b/pr/299/docs/trait.impl/core/ops/bit/trait.Not.js new file mode 100644 index 000000000..5b2b391db --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/ops/deref/trait.Deref.js b/pr/299/docs/trait.impl/core/ops/deref/trait.Deref.js new file mode 100644 index 000000000..7b1e3385e --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/ops/deref/trait.DerefMut.js b/pr/299/docs/trait.impl/core/ops/deref/trait.DerefMut.js new file mode 100644 index 000000000..0fbf8f6b5 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/ops/drop/trait.Drop.js b/pr/299/docs/trait.impl/core/ops/drop/trait.Drop.js new file mode 100644 index 000000000..4f190db7b --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/ops/index/trait.Index.js b/pr/299/docs/trait.impl/core/ops/index/trait.Index.js new file mode 100644 index 000000000..dabc7ba10 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/ops/index/trait.IndexMut.js b/pr/299/docs/trait.impl/core/ops/index/trait.IndexMut.js new file mode 100644 index 000000000..ebfe83045 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js b/pr/299/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js new file mode 100644 index 000000000..4c88463df --- /dev/null +++ b/pr/299/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 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 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 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 MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["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 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,11269,39915,1848,3221,663]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js b/pr/299/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js new file mode 100644 index 000000000..19c211d09 --- /dev/null +++ b/pr/299/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 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 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 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 MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["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 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,10990,38990,1803,3140,645]} \ No newline at end of file diff --git a/pr/299/docs/trait.impl/core/str/traits/trait.FromStr.js b/pr/299/docs/trait.impl/core/str/traits/trait.FromStr.js new file mode 100644 index 000000000..8d57e8916 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js b/pr/299/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js new file mode 100644 index 000000000..f5d531735 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js b/pr/299/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js new file mode 100644 index 000000000..53ae8c728 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/identity_hash/trait.IdentityHashable.js b/pr/299/docs/trait.impl/identity_hash/trait.IdentityHashable.js new file mode 100644 index 000000000..dd10c652c --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn/runtime/trait.AsyncTimer.js b/pr/299/docs/trait.impl/iroh_quinn/runtime/trait.AsyncTimer.js new file mode 100644 index 000000000..4cabdc2d0 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn/runtime/trait.Runtime.js b/pr/299/docs/trait.impl/iroh_quinn/runtime/trait.Runtime.js new file mode 100644 index 000000000..4cabdc2d0 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.js b/pr/299/docs/trait.impl/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/coding/trait.Decodable.js b/pr/299/docs/trait.impl/iroh_quinn_proto/coding/trait.Decodable.js new file mode 100644 index 000000000..ed3b1991d --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/coding/trait.Encodable.js b/pr/299/docs/trait.impl/iroh_quinn_proto/coding/trait.Encodable.js new file mode 100644 index 000000000..ed3b1991d --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/config/qlog/trait.QlogFactory.js b/pr/299/docs/trait.impl/iroh_quinn_proto/config/qlog/trait.QlogFactory.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/config/trait.TimeSource.js b/pr/299/docs/trait.impl/iroh_quinn_proto/config/trait.TimeSource.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/congestion/trait.Controller.js b/pr/299/docs/trait.impl/iroh_quinn_proto/congestion/trait.Controller.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/congestion/trait.ControllerFactory.js b/pr/299/docs/trait.impl/iroh_quinn_proto/congestion/trait.ControllerFactory.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.AeadKey.js b/pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.AeadKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.ClientConfig.js b/pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.ClientConfig.js new file mode 100644 index 000000000..692621ae4 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.js b/pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.HeaderKey.js b/pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.HeaderKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.HmacKey.js b/pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.HmacKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.PacketKey.js b/pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.PacketKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.ServerConfig.js b/pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.ServerConfig.js new file mode 100644 index 000000000..45d00a6be --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.Session.js b/pr/299/docs/trait.impl/iroh_quinn_proto/crypto/trait.Session.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/packet/trait.ConnectionIdParser.js b/pr/299/docs/trait.impl/iroh_quinn_proto/packet/trait.ConnectionIdParser.js new file mode 100644 index 000000000..ed3b1991d --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/token/trait.TokenLog.js b/pr/299/docs/trait.impl/iroh_quinn_proto/token/trait.TokenLog.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/iroh_quinn_proto/token/trait.TokenStore.js b/pr/299/docs/trait.impl/iroh_quinn_proto/token/trait.TokenStore.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js b/pr/299/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js new file mode 100644 index 000000000..45f6b7887 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js b/pr/299/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js new file mode 100644 index 000000000..f5d531735 --- /dev/null +++ b/pr/299/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/299/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js b/pr/299/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js new file mode 100644 index 000000000..53ae8c728 --- /dev/null +++ b/pr/299/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/299/docs/type.impl/core/result/enum.Result.js b/pr/299/docs/type.impl/core/result/enum.Result.js new file mode 100644 index 000000000..ad2c8c137 --- /dev/null +++ b/pr/299/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